author | wenzelm |
Sun, 14 Feb 2016 12:40:51 +0100 | |
changeset 62304 | e7a52a838a23 |
parent 62303 | f868f12f9419 |
child 62399 | 36e885190439 |
permissions | -rw-r--r-- |
60991 | 1 |
/* Title: Pure/Concurrent/bash.scala |
2 |
Author: Makarius |
|
3 |
||
4 |
GNU bash processes, with propagation of interrupts. |
|
5 |
*/ |
|
6 |
||
7 |
package isabelle |
|
8 |
||
9 |
||
10 |
import java.io.{File => JFile, BufferedReader, InputStreamReader, |
|
11 |
BufferedWriter, OutputStreamWriter} |
|
12 |
||
13 |
||
14 |
object Bash |
|
15 |
{ |
|
16 |
/** result **/ |
|
17 |
||
18 |
final case class Result(out_lines: List[String], err_lines: List[String], rc: Int) |
|
19 |
{ |
|
20 |
def out: String = cat_lines(out_lines) |
|
21 |
def err: String = cat_lines(err_lines) |
|
62303 | 22 |
|
23 |
def error(s: String, err_rc: Int = 0): Result = |
|
24 |
copy(err_lines = err_lines ::: List(s), rc = rc max err_rc) |
|
60991 | 25 |
|
62303 | 26 |
def check: Result = |
60991 | 27 |
if (rc == Exn.Interrupt.return_code) throw Exn.Interrupt() |
62303 | 28 |
else if (rc != 0) Library.error(err) |
60991 | 29 |
else this |
62302 | 30 |
|
31 |
def print: Result = |
|
32 |
{ |
|
33 |
Output.warning(Library.trim_line(err)) |
|
34 |
Output.writeln(Library.trim_line(out)) |
|
35 |
Result(Nil, Nil, rc) |
|
36 |
} |
|
60991 | 37 |
} |
38 |
||
39 |
||
40 |
||
41 |
/** process **/ |
|
42 |
||
43 |
def process(cwd: JFile, env: Map[String, String], redirect: Boolean, args: String*): Process = |
|
44 |
new Process(cwd, env, redirect, args:_*) |
|
45 |
||
46 |
class Process private [Bash]( |
|
47 |
cwd: JFile, env: Map[String, String], redirect: Boolean, args: String*) |
|
48 |
extends Prover.System_Process |
|
49 |
{ |
|
62296 | 50 |
private val proc = |
62304
e7a52a838a23
more direct invocation of ISABELLE_BASH_PROCESS on Windows;
wenzelm
parents:
62303
diff
changeset
|
51 |
{ |
e7a52a838a23
more direct invocation of ISABELLE_BASH_PROCESS on Windows;
wenzelm
parents:
62303
diff
changeset
|
52 |
val params = |
e7a52a838a23
more direct invocation of ISABELLE_BASH_PROCESS on Windows;
wenzelm
parents:
62303
diff
changeset
|
53 |
List(File.platform_path(Path.variable("ISABELLE_BASH_PROCESS")), "-", "bash") |
e7a52a838a23
more direct invocation of ISABELLE_BASH_PROCESS on Windows;
wenzelm
parents:
62303
diff
changeset
|
54 |
Isabelle_System.process( |
e7a52a838a23
more direct invocation of ISABELLE_BASH_PROCESS on Windows;
wenzelm
parents:
62303
diff
changeset
|
55 |
cwd, Isabelle_System.settings(env), redirect, (params ::: args.toList):_*) |
e7a52a838a23
more direct invocation of ISABELLE_BASH_PROCESS on Windows;
wenzelm
parents:
62303
diff
changeset
|
56 |
} |
60991 | 57 |
|
58 |
||
59 |
// channels |
|
60 |
||
61 |
val stdin: BufferedWriter = |
|
62 |
new BufferedWriter(new OutputStreamWriter(proc.getOutputStream, UTF8.charset)) |
|
63 |
||
64 |
val stdout: BufferedReader = |
|
65 |
new BufferedReader(new InputStreamReader(proc.getInputStream, UTF8.charset)) |
|
66 |
||
67 |
val stderr: BufferedReader = |
|
68 |
new BufferedReader(new InputStreamReader(proc.getErrorStream, UTF8.charset)) |
|
69 |
||
70 |
||
71 |
// signals |
|
72 |
||
73 |
private val pid = stdout.readLine |
|
74 |
||
75 |
private def kill(signal: String): Boolean = |
|
61025 | 76 |
Exn.Interrupt.postpone { |
77 |
Isabelle_System.kill(signal, pid) |
|
78 |
Isabelle_System.kill("0", pid)._2 == 0 } getOrElse true |
|
60991 | 79 |
|
80 |
private def multi_kill(signal: String): Boolean = |
|
81 |
{ |
|
82 |
var running = true |
|
83 |
var count = 10 |
|
84 |
while (running && count > 0) { |
|
85 |
if (kill(signal)) { |
|
86 |
Exn.Interrupt.postpone { |
|
87 |
Thread.sleep(100) |
|
88 |
count -= 1 |
|
89 |
} |
|
90 |
} |
|
91 |
else running = false |
|
92 |
} |
|
93 |
running |
|
94 |
} |
|
95 |
||
96 |
def interrupt() { multi_kill("INT") } |
|
97 |
def terminate() { multi_kill("INT") && multi_kill("TERM") && kill("KILL"); proc.destroy } |
|
98 |
||
99 |
||
100 |
// JVM shutdown hook |
|
101 |
||
102 |
private val shutdown_hook = new Thread { override def run = terminate() } |
|
103 |
||
104 |
try { Runtime.getRuntime.addShutdownHook(shutdown_hook) } |
|
105 |
catch { case _: IllegalStateException => } |
|
106 |
||
107 |
private def cleanup() = |
|
108 |
try { Runtime.getRuntime.removeShutdownHook(shutdown_hook) } |
|
109 |
catch { case _: IllegalStateException => } |
|
110 |
||
111 |
||
112 |
/* result */ |
|
113 |
||
114 |
def join: Int = { val rc = proc.waitFor; cleanup(); rc } |
|
115 |
} |
|
116 |
} |