59 redirect: Boolean = false, |
57 redirect: Boolean = false, |
60 cleanup: () => Unit = () => ()): Process = |
58 cleanup: () => Unit = () => ()): Process = |
61 new Process(script, description, cwd, env, redirect, cleanup) |
59 new Process(script, description, cwd, env, redirect, cleanup) |
62 |
60 |
63 class Process private[Bash]( |
61 class Process private[Bash]( |
64 script: String, |
62 script: String, |
65 description: String, |
63 description: String, |
66 cwd: JFile, |
64 cwd: JFile, |
67 env: JMap[String, String], |
65 env: JMap[String, String], |
68 redirect: Boolean, |
66 redirect: Boolean, |
69 cleanup: () => Unit) |
67 cleanup: () => Unit |
70 { |
68 ) { |
71 override def toString: String = make_description(description) |
69 override def toString: String = make_description(description) |
72 |
70 |
73 private val timing_file = Isabelle_System.tmp_file("bash_timing") |
71 private val timing_file = Isabelle_System.tmp_file("bash_timing") |
74 private val timing = Synchronized[Option[Timing]](None) |
72 private val timing = Synchronized[Option[Timing]](None) |
75 def get_timing: Timing = timing.value getOrElse Timing.zero |
73 def get_timing: Timing = timing.value getOrElse Timing.zero |
121 case None => process_alive(group_pid) |
119 case None => process_alive(group_pid) |
122 case Some(file) => |
120 case Some(file) => |
123 file.exists() && process_alive(Library.trim_line(File.read(file))) |
121 file.exists() && process_alive(Library.trim_line(File.read(file))) |
124 } |
122 } |
125 |
123 |
126 @tailrec private def signal(s: String, count: Int = 1): Boolean = |
124 @tailrec private def signal(s: String, count: Int = 1): Boolean = { |
127 { |
125 count <= 0 || { |
128 count <= 0 || |
|
129 { |
|
130 isabelle.setup.Environment.kill_process(group_pid, s) |
126 isabelle.setup.Environment.kill_process(group_pid, s) |
131 val running = |
127 val running = |
132 root_process_alive() || |
128 root_process_alive() || |
133 isabelle.setup.Environment.kill_process(group_pid, "0") |
129 isabelle.setup.Environment.kill_process(group_pid, "0") |
134 if (running) { |
130 if (running) { |
137 } |
133 } |
138 else false |
134 else false |
139 } |
135 } |
140 } |
136 } |
141 |
137 |
142 def terminate(): Unit = Isabelle_Thread.try_uninterruptible |
138 def terminate(): Unit = Isabelle_Thread.try_uninterruptible { |
143 { |
|
144 signal("INT", count = 7) && signal("TERM", count = 3) && signal("KILL") |
139 signal("INT", count = 7) && signal("TERM", count = 3) && signal("KILL") |
145 proc.destroy() |
140 proc.destroy() |
146 do_cleanup() |
141 do_cleanup() |
147 } |
142 } |
148 |
143 |
149 def interrupt(): Unit = Isabelle_Thread.try_uninterruptible |
144 def interrupt(): Unit = Isabelle_Thread.try_uninterruptible { |
150 { |
|
151 isabelle.setup.Environment.kill_process(group_pid, "INT") |
145 isabelle.setup.Environment.kill_process(group_pid, "INT") |
152 } |
146 } |
153 |
147 |
154 |
148 |
155 // JVM shutdown hook |
149 // JVM shutdown hook |
160 catch { case _: IllegalStateException => } |
154 catch { case _: IllegalStateException => } |
161 |
155 |
162 |
156 |
163 // cleanup |
157 // cleanup |
164 |
158 |
165 private def do_cleanup(): Unit = |
159 private def do_cleanup(): Unit = { |
166 { |
|
167 try { Runtime.getRuntime.removeShutdownHook(shutdown_hook) } |
160 try { Runtime.getRuntime.removeShutdownHook(shutdown_hook) } |
168 catch { case _: IllegalStateException => } |
161 catch { case _: IllegalStateException => } |
169 |
162 |
170 script_file.delete() |
163 script_file.delete() |
171 winpid_file.foreach(_.delete()) |
164 winpid_file.foreach(_.delete()) |
205 def result( |
197 def result( |
206 input: String = "", |
198 input: String = "", |
207 progress_stdout: String => Unit = (_: String) => (), |
199 progress_stdout: String => Unit = (_: String) => (), |
208 progress_stderr: String => Unit = (_: String) => (), |
200 progress_stderr: String => Unit = (_: String) => (), |
209 watchdog: Option[Watchdog] = None, |
201 watchdog: Option[Watchdog] = None, |
210 strict: Boolean = true): Process_Result = |
202 strict: Boolean = true |
211 { |
203 ): Process_Result = { |
212 val in = |
204 val in = |
213 if (input.isEmpty) Future.value(stdin.close()) |
205 if (input.isEmpty) Future.value(stdin.close()) |
214 else Future.thread("bash_stdin") { stdin.write(input); stdin.flush(); stdin.close(); } |
206 else Future.thread("bash_stdin") { stdin.write(input); stdin.flush(); stdin.close(); } |
215 val out_lines = |
207 val out_lines = |
216 Future.thread("bash_stdout") { File.read_lines(stdout, progress_stdout) } |
208 Future.thread("bash_stdout") { File.read_lines(stdout, progress_stdout) } |
245 } |
237 } |
246 |
238 |
247 |
239 |
248 /* server */ |
240 /* server */ |
249 |
241 |
250 object Server |
242 object Server { |
251 { |
|
252 // input messages |
243 // input messages |
253 private val RUN = "run" |
244 private val RUN = "run" |
254 private val KILL = "kill" |
245 private val KILL = "kill" |
255 |
246 |
256 // output messages |
247 // output messages |
257 private val UUID = "uuid" |
248 private val UUID = "uuid" |
258 private val INTERRUPT = "interrupt" |
249 private val INTERRUPT = "interrupt" |
259 private val FAILURE = "failure" |
250 private val FAILURE = "failure" |
260 private val RESULT = "result" |
251 private val RESULT = "result" |
261 |
252 |
262 def start(port: Int = 0, debugging: => Boolean = false): Server = |
253 def start(port: Int = 0, debugging: => Boolean = false): Server = { |
263 { |
|
264 val server = new Server(port, debugging) |
254 val server = new Server(port, debugging) |
265 server.start() |
255 server.start() |
266 server |
256 server |
267 } |
257 } |
268 } |
258 } |
269 |
259 |
270 class Server private(port: Int, debugging: => Boolean) |
260 class Server private(port: Int, debugging: => Boolean) |
271 extends isabelle.Server.Handler(port) |
261 extends isabelle.Server.Handler(port) { |
272 { |
|
273 server => |
262 server => |
274 |
263 |
275 private val _processes = Synchronized(Map.empty[UUID.T, Bash.Process]) |
264 private val _processes = Synchronized(Map.empty[UUID.T, Bash.Process]) |
276 |
265 |
277 override def stop(): Unit = |
266 override def stop(): Unit = { |
278 { |
|
279 for ((_, process) <- _processes.value) process.terminate() |
267 for ((_, process) <- _processes.value) process.terminate() |
280 super.stop() |
268 super.stop() |
281 } |
269 } |
282 |
270 |
283 override def handle(connection: isabelle.Server.Connection): Unit = |
271 override def handle(connection: isabelle.Server.Connection): Unit = { |
284 { |
|
285 def reply(chunks: List[String]): Unit = |
272 def reply(chunks: List[String]): Unit = |
286 try { connection.write_byte_message(chunks.map(Bytes.apply)) } |
273 try { connection.write_byte_message(chunks.map(Bytes.apply)) } |
287 catch { case _: IOException => } |
274 catch { case _: IOException => } |
288 |
275 |
289 def reply_failure(exn: Throwable): Unit = |
276 def reply_failure(exn: Throwable): Unit = |
364 case Some(_) => reply_failure(ERROR("Bad protocol message")) |
351 case Some(_) => reply_failure(ERROR("Bad protocol message")) |
365 } |
352 } |
366 } |
353 } |
367 } |
354 } |
368 |
355 |
369 class Handler extends Session.Protocol_Handler |
356 class Handler extends Session.Protocol_Handler { |
370 { |
|
371 private var server: Server = null |
357 private var server: Server = null |
372 |
358 |
373 override def init(session: Session): Unit = |
359 override def init(session: Session): Unit = { |
374 { |
|
375 exit() |
360 exit() |
376 server = Server.start(debugging = session.session_options.bool("bash_process_debugging")) |
361 server = Server.start(debugging = session.session_options.bool("bash_process_debugging")) |
377 } |
362 } |
378 |
363 |
379 override def exit(): Unit = |
364 override def exit(): Unit = { |
380 { |
|
381 if (server != null) { |
365 if (server != null) { |
382 server.stop() |
366 server.stop() |
383 server = null |
367 server = null |
384 } |
368 } |
385 } |
369 } |
386 |
370 |
387 override def prover_options(options: Options): Options = |
371 override def prover_options(options: Options): Options = { |
388 { |
|
389 val address = if (server == null) "" else server.address |
372 val address = if (server == null) "" else server.address |
390 val password = if (server == null) "" else server.password |
373 val password = if (server == null) "" else server.password |
391 options + |
374 options + |
392 ("bash_process_address=" + address) + |
375 ("bash_process_address=" + address) + |
393 ("bash_process_password=" + password) |
376 ("bash_process_password=" + password) |