72 val component = Components.provide(Component_Bash_Process.home, ssh = ssh) |
72 val component = Components.provide(Component_Bash_Process.home, ssh = ssh) |
73 val exe = Component_Bash_Process.remote_program(component) |
73 val exe = Component_Bash_Process.remote_program(component) |
74 ssh.make_command(args_host = true, args = ssh.bash_path(exe)) |
74 ssh.make_command(args_host = true, args = ssh.bash_path(exe)) |
75 } |
75 } |
76 |
76 |
77 type Watchdog = (Time, Process => Boolean) |
77 object Watchdog { |
|
78 val none: Watchdog = new Watchdog(Time.zero, _ => false) { |
|
79 override def toString: String = "Bash.Watchdog.none" |
|
80 } |
|
81 def apply(time: Time, check: Process => Boolean = _ => true): Watchdog = |
|
82 if (time.is_zero) none else new Watchdog(time, check) |
|
83 } |
|
84 class Watchdog private(val time: Time, val check: Process => Boolean) { |
|
85 override def toString: String = "Bash.Watchdog(" + time + ")" |
|
86 def defined: Boolean = !time.is_zero |
|
87 } |
78 |
88 |
79 def process(script: String, |
89 def process(script: String, |
80 description: String = "", |
90 description: String = "", |
81 ssh: SSH.System = SSH.Local, |
91 ssh: SSH.System = SSH.Local, |
82 cwd: Path = Path.current, |
92 cwd: Path = Path.current, |
246 |
256 |
247 def result( |
257 def result( |
248 input: String = "", |
258 input: String = "", |
249 progress_stdout: String => Unit = (_: String) => (), |
259 progress_stdout: String => Unit = (_: String) => (), |
250 progress_stderr: String => Unit = (_: String) => (), |
260 progress_stderr: String => Unit = (_: String) => (), |
251 watchdog: Option[Watchdog] = None, |
261 watchdog: Watchdog = Watchdog.none, |
252 strict: Boolean = true |
262 strict: Boolean = true |
253 ): Process_Result = { |
263 ): Process_Result = { |
254 val in = |
264 val in = |
255 if (input.isEmpty) Future.value(stdin.close()) |
265 if (input.isEmpty) Future.value(stdin.close()) |
256 else Future.thread("bash_stdin") { stdin.write(input); stdin.flush(); stdin.close(); } |
266 else Future.thread("bash_stdin") { stdin.write(input); stdin.flush(); stdin.close(); } |
258 Future.thread("bash_stdout") { File.read_lines(stdout, progress_stdout) } |
268 Future.thread("bash_stdout") { File.read_lines(stdout, progress_stdout) } |
259 val err_lines = |
269 val err_lines = |
260 Future.thread("bash_stderr") { File.read_lines(stderr, progress_stderr) } |
270 Future.thread("bash_stderr") { File.read_lines(stderr, progress_stderr) } |
261 |
271 |
262 val watchdog_thread = |
272 val watchdog_thread = |
263 for ((time, check) <- watchdog) |
273 if (watchdog.defined) { |
264 yield { |
274 Some( |
265 Future.thread("bash_watchdog") { |
275 Future.thread("bash_watchdog") { |
266 while (proc.isAlive) { |
276 while (proc.isAlive) { |
267 time.sleep() |
277 watchdog.time.sleep() |
268 if (check(this)) interrupt() |
278 if (watchdog.check(this)) interrupt() |
269 } |
279 } |
270 } |
280 }) |
271 } |
281 } |
|
282 else None |
272 |
283 |
273 val rc = |
284 val rc = |
274 try { join() } |
285 try { join() } |
275 catch { case Exn.Interrupt() => terminate(); Process_Result.RC.interrupt } |
286 catch { case Exn.Interrupt() => terminate(); Process_Result.RC.interrupt } |
276 |
287 |
375 _processes.change(processes => processes + (uuid -> process)) |
386 _processes.change(processes => processes + (uuid -> process)) |
376 reply(List(Server.UUID, uuid.toString)) |
387 reply(List(Server.UUID, uuid.toString)) |
377 |
388 |
378 Isabelle_Thread.fork(name = "bash_process") { |
389 Isabelle_Thread.fork(name = "bash_process") { |
379 @volatile var is_timeout = false |
390 @volatile var is_timeout = false |
380 val watchdog: Option[Watchdog] = |
391 val watchdog = |
381 if (timeout.is_zero) None else Some((timeout, _ => { is_timeout = true; true })) |
392 if (timeout.is_zero) Watchdog.none |
|
393 else Watchdog(timeout, _ => { is_timeout = true; true }) |
382 |
394 |
383 Exn.capture { process.result(input = input, watchdog = watchdog, strict = false) } |
395 Exn.capture { process.result(input = input, watchdog = watchdog, strict = false) } |
384 match { |
396 match { |
385 case Exn.Exn(exn) => reply_failure(exn) |
397 case Exn.Exn(exn) => reply_failure(exn) |
386 case Exn.Res(res0) => |
398 case Exn.Res(res0) => |