| author | wenzelm | 
| Sat, 17 Dec 2011 12:10:37 +0100 | |
| changeset 45906 | 0aaeb5520f2f | 
| parent 45709 | 87017fcbad83 | 
| child 46121 | 30a69cd8a9a0 | 
| permissions | -rw-r--r-- | 
| 43283 | 1 | /* Title: Pure/System/isabelle_process.scala | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 2 | Author: Makarius | 
| 27963 | 3 | Options: :folding=explicit:collapseFolds=1: | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 4 | |
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 5 | Isabelle process management -- always reactive due to multi-threaded I/O. | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 6 | */ | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 7 | |
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 8 | package isabelle | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 9 | |
| 43520 
cec9b95fa35d
explicit import java.lang.System to prevent odd scope problems;
 wenzelm parents: 
43514diff
changeset | 10 | import java.lang.System | 
| 27955 
4c32c5e75eca
use java.util.concurrent.LinkedBlockingQueue, which blocks as required;
 wenzelm parents: 
27949diff
changeset | 11 | import java.util.concurrent.LinkedBlockingQueue | 
| 28045 | 12 | import java.io.{BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter,
 | 
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 13 | InputStream, OutputStream, BufferedOutputStream, IOException} | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 14 | |
| 32474 
0818e6b1c8a6
Isabelle_Process: receiver as Actor, not EventBus;
 wenzelm parents: 
32448diff
changeset | 15 | import scala.actors.Actor | 
| 
0818e6b1c8a6
Isabelle_Process: receiver as Actor, not EventBus;
 wenzelm parents: 
32448diff
changeset | 16 | import Actor._ | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 17 | |
| 27973 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 18 | |
| 32474 
0818e6b1c8a6
Isabelle_Process: receiver as Actor, not EventBus;
 wenzelm parents: 
32448diff
changeset | 19 | object Isabelle_Process | 
| 
0818e6b1c8a6
Isabelle_Process: receiver as Actor, not EventBus;
 wenzelm parents: 
32448diff
changeset | 20 | {
 | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 21 | /* results */ | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 22 | |
| 39525 | 23 | object Kind | 
| 24 |   {
 | |
| 25 | val message_markup = Map( | |
| 45666 | 26 |       ('A' : Int) -> Isabelle_Markup.INIT,
 | 
| 27 |       ('B' : Int) -> Isabelle_Markup.STATUS,
 | |
| 28 |       ('C' : Int) -> Isabelle_Markup.REPORT,
 | |
| 29 |       ('D' : Int) -> Isabelle_Markup.WRITELN,
 | |
| 30 |       ('E' : Int) -> Isabelle_Markup.TRACING,
 | |
| 31 |       ('F' : Int) -> Isabelle_Markup.WARNING,
 | |
| 32 |       ('G' : Int) -> Isabelle_Markup.ERROR,
 | |
| 33 |       ('H' : Int) -> Isabelle_Markup.RAW)
 | |
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 34 | } | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 35 | |
| 44733 
329320fc88df
buffer prover messages to prevent overloading of session_actor input channel -- which is critical due to synchronous messages wrt. GUI thread;
 wenzelm parents: 
44732diff
changeset | 36 | sealed abstract class Message | 
| 43721 
fad8634cee62
echo prover input via raw_messages, for improved protocol tracing;
 wenzelm parents: 
43695diff
changeset | 37 | |
| 
fad8634cee62
echo prover input via raw_messages, for improved protocol tracing;
 wenzelm parents: 
43695diff
changeset | 38 | class Input(name: String, args: List[String]) extends Message | 
| 
fad8634cee62
echo prover input via raw_messages, for improved protocol tracing;
 wenzelm parents: 
43695diff
changeset | 39 |   {
 | 
| 
fad8634cee62
echo prover input via raw_messages, for improved protocol tracing;
 wenzelm parents: 
43695diff
changeset | 40 | override def toString: String = | 
| 45666 | 41 | XML.Elem(Markup(Isabelle_Markup.PROVER_COMMAND, List((Markup.NAME, name))), | 
| 43721 
fad8634cee62
echo prover input via raw_messages, for improved protocol tracing;
 wenzelm parents: 
43695diff
changeset | 42 | args.map(s => | 
| 45666 | 43 |           List(XML.Text("\n"),
 | 
| 44 | XML.elem(Isabelle_Markup.PROVER_ARG, YXML.parse_body(s)))).flatten).toString | |
| 43721 
fad8634cee62
echo prover input via raw_messages, for improved protocol tracing;
 wenzelm parents: 
43695diff
changeset | 45 | } | 
| 
fad8634cee62
echo prover input via raw_messages, for improved protocol tracing;
 wenzelm parents: 
43695diff
changeset | 46 | |
| 
fad8634cee62
echo prover input via raw_messages, for improved protocol tracing;
 wenzelm parents: 
43695diff
changeset | 47 | class Result(val message: XML.Elem) extends Message | 
| 34100 | 48 |   {
 | 
| 43747 | 49 | def kind: String = message.markup.name | 
| 43780 | 50 | def properties: Properties.T = message.markup.properties | 
| 43747 | 51 | def body: XML.Body = message.body | 
| 37689 
628eabe2213a
simplified Isabelle_Process.Result: use markup directly;
 wenzelm parents: 
37132diff
changeset | 52 | |
| 45666 | 53 | def is_init = kind == Isabelle_Markup.INIT | 
| 54 | def is_exit = kind == Isabelle_Markup.EXIT | |
| 55 | def is_stdout = kind == Isabelle_Markup.STDOUT | |
| 56 | def is_stderr = kind == Isabelle_Markup.STDERR | |
| 57 | def is_system = kind == Isabelle_Markup.SYSTEM | |
| 58 | def is_status = kind == Isabelle_Markup.STATUS | |
| 59 | def is_report = kind == Isabelle_Markup.REPORT | |
| 60 | def is_raw = kind == Isabelle_Markup.RAW | |
| 45709 
87017fcbad83
clarified modules (again) -- NB: both Document and Protocol are specific to this particular prover;
 wenzelm parents: 
45672diff
changeset | 61 | def is_ready = Protocol.is_ready(message) | 
| 45633 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 62 | def is_syslog = is_init || is_exit || is_system || is_ready || is_stderr | 
| 34100 | 63 | |
| 64 | override def toString: String = | |
| 65 |     {
 | |
| 29522 
793766d4c1b5
moved message markup into Scala layer -- reduced redundancy;
 wenzelm parents: 
29506diff
changeset | 66 | val res = | 
| 38236 
d8c7be27e01d
explicitly distinguish Output.status (essential feedback) vs. Output.report (useful markup);
 wenzelm parents: 
38231diff
changeset | 67 | if (is_status || is_report) message.body.map(_.toString).mkString | 
| 43748 | 68 | else if (is_raw) "..." | 
| 37689 
628eabe2213a
simplified Isabelle_Process.Result: use markup directly;
 wenzelm parents: 
37132diff
changeset | 69 | else Pretty.string_of(message.body) | 
| 38230 
ed147003de4b
simplified type XML.Tree: embed Markup directly, avoid slightly odd triple;
 wenzelm parents: 
37712diff
changeset | 70 | if (properties.isEmpty) | 
| 29572 
e3a99d957392
replaced java.util.Properties by plain association list;
 wenzelm parents: 
29522diff
changeset | 71 | kind.toString + " [[" + res + "]]" | 
| 
e3a99d957392
replaced java.util.Properties by plain association list;
 wenzelm parents: 
29522diff
changeset | 72 | else | 
| 
e3a99d957392
replaced java.util.Properties by plain association list;
 wenzelm parents: 
29522diff
changeset | 73 | kind.toString + " " + | 
| 38230 
ed147003de4b
simplified type XML.Tree: embed Markup directly, avoid slightly odd triple;
 wenzelm parents: 
37712diff
changeset | 74 |           (for ((x, y) <- properties) yield x + "=" + y).mkString("{", ",", "}") + " [[" + res + "]]"
 | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 75 | } | 
| 27973 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 76 | } | 
| 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 77 | } | 
| 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 78 | |
| 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 79 | |
| 45055 
55274f7e306b
explicit option for socket vs. fifo communication;
 wenzelm parents: 
45027diff
changeset | 80 | class Isabelle_Process( | 
| 45075 | 81 | timeout: Time = Time.seconds(25), | 
| 45055 
55274f7e306b
explicit option for socket vs. fifo communication;
 wenzelm parents: 
45027diff
changeset | 82 | receiver: Isabelle_Process.Message => Unit = Console.println(_), | 
| 
55274f7e306b
explicit option for socket vs. fifo communication;
 wenzelm parents: 
45027diff
changeset | 83 | args: List[String] = Nil) | 
| 29192 | 84 | {
 | 
| 31797 | 85 | import Isabelle_Process._ | 
| 29194 | 86 | |
| 27973 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 87 | |
| 39626 
a5d0bcfb95a3
manage persistent syslog via Session, not Isabelle_Process;
 wenzelm parents: 
39625diff
changeset | 88 | /* results */ | 
| 39573 | 89 | |
| 90 | private def system_result(text: String) | |
| 91 |   {
 | |
| 45666 | 92 | receiver(new Result(XML.Elem(Markup(Isabelle_Markup.SYSTEM, Nil), List(XML.Text(text))))) | 
| 39573 | 93 | } | 
| 94 | ||
| 43745 | 95 | private val xml_cache = new XML.Cache() | 
| 39573 | 96 | |
| 43780 | 97 | private def put_result(kind: String, props: Properties.T, body: XML.Body) | 
| 39573 | 98 |   {
 | 
| 45666 | 99 | if (kind == Isabelle_Markup.INIT) system_channel.accepted() | 
| 100 | if (kind == Isabelle_Markup.RAW) | |
| 44732 | 101 | receiver(new Result(XML.Elem(Markup(kind, props), body))) | 
| 43746 
a41f618c641d
some support for raw messages, which bypass standard Symbol/YXML decoding;
 wenzelm parents: 
43745diff
changeset | 102 |     else {
 | 
| 45709 
87017fcbad83
clarified modules (again) -- NB: both Document and Protocol are specific to this particular prover;
 wenzelm parents: 
45672diff
changeset | 103 | val msg = XML.Elem(Markup(kind, props), Protocol.clean_message(body)) | 
| 44732 | 104 | receiver(new Result(xml_cache.cache_tree(msg).asInstanceOf[XML.Elem])) | 
| 43746 
a41f618c641d
some support for raw messages, which bypass standard Symbol/YXML decoding;
 wenzelm parents: 
43745diff
changeset | 105 | } | 
| 39573 | 106 | } | 
| 107 | ||
| 108 | private def put_result(kind: String, text: String) | |
| 109 |   {
 | |
| 43695 
5130dfe1b7be
simplified Symbol based on lazy Symbol.Interpretation -- reduced odd "functorial style";
 wenzelm parents: 
43661diff
changeset | 110 | put_result(kind, Nil, List(XML.Text(Symbol.decode(text)))) | 
| 39573 | 111 | } | 
| 112 | ||
| 113 | ||
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 114 | /* input actors */ | 
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 115 | |
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 116 | private case class Input_Text(text: String) | 
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 117 | private case class Input_Chunks(chunks: List[Array[Byte]]) | 
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 118 | |
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 119 | private case object Close | 
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 120 | private def close(p: (Thread, Actor)) | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 121 |   {
 | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 122 |     if (p != null && p._1.isAlive) {
 | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 123 | p._2 ! Close | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 124 | p._1.join | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 125 | } | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 126 | } | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 127 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 128 | @volatile private var standard_input: (Thread, Actor) = null | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 129 | @volatile private var command_input: (Thread, Actor) = null | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 130 | |
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 131 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 132 | /** process manager **/ | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 133 | |
| 45158 | 134 | private val system_channel = System_Channel() | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 135 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 136 | private val process = | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 137 |     try {
 | 
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 138 | val cmdline = | 
| 45027 
f459e93a038e
more abstract wrapping of fifos as System_Channel;
 wenzelm parents: 
44775diff
changeset | 139 |         Isabelle_System.getenv_strict("ISABELLE_PROCESS") ::
 | 
| 45055 
55274f7e306b
explicit option for socket vs. fifo communication;
 wenzelm parents: 
45027diff
changeset | 140 | (system_channel.isabelle_args ::: args) | 
| 45633 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 141 | new Isabelle_System.Managed_Process(false, cmdline: _*) | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 142 | } | 
| 45027 
f459e93a038e
more abstract wrapping of fifos as System_Channel;
 wenzelm parents: 
44775diff
changeset | 143 |     catch { case e: IOException => system_channel.accepted(); throw(e) }
 | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 144 | |
| 39587 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 145 | val process_result = | 
| 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 146 |     Simple_Thread.future("process_result") { process.join }
 | 
| 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 147 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 148 | private def terminate_process() | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 149 |   {
 | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 150 |     try { process.terminate }
 | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 151 |     catch { case e: IOException => system_result("Failed to terminate Isabelle: " + e.getMessage) }
 | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 152 | } | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 153 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 154 |   private val process_manager = Simple_Thread.fork("process_manager")
 | 
| 39572 | 155 |   {
 | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 156 | val (startup_failed, startup_output) = | 
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 157 |     {
 | 
| 40848 
8662b9b1f123
more abstract/uniform handling of time, preferring seconds as Double;
 wenzelm parents: 
39731diff
changeset | 158 | val expired = System.currentTimeMillis() + timeout.ms | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 159 | val result = new StringBuilder(100) | 
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 160 | |
| 39587 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 161 | var finished: Option[Boolean] = None | 
| 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 162 |       while (finished.isEmpty && System.currentTimeMillis() <= expired) {
 | 
| 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 163 |         while (finished.isEmpty && process.stdout.ready) {
 | 
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 164 | val c = process.stdout.read | 
| 39587 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 165 | if (c == 2) finished = Some(true) | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 166 | else result += c.toChar | 
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 167 | } | 
| 39587 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 168 | if (process_result.is_finished) finished = Some(false) | 
| 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 169 | else Thread.sleep(10) | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 170 | } | 
| 39623 | 171 | (finished.isEmpty || !finished.get, result.toString.trim) | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 172 | } | 
| 39572 | 173 | system_result(startup_output) | 
| 174 | ||
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 175 |     if (startup_failed) {
 | 
| 45666 | 176 | put_result(Isabelle_Markup.EXIT, "Return code: 127") | 
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 177 | process.stdin.close | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 178 | Thread.sleep(300) | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 179 | terminate_process() | 
| 39587 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 180 | process_result.join | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 181 | } | 
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 182 |     else {
 | 
| 45027 
f459e93a038e
more abstract wrapping of fifos as System_Channel;
 wenzelm parents: 
44775diff
changeset | 183 | val (command_stream, message_stream) = system_channel.rendezvous() | 
| 39530 
16adc476348f
Isabelle_Process: more robust rendezvous, even without proper blocking on open (Cygwin);
 wenzelm parents: 
39528diff
changeset | 184 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 185 | standard_input = stdin_actor() | 
| 45633 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 186 | val stdout = raw_output_actor(false) | 
| 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 187 | val stderr = raw_output_actor(true) | 
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 188 | command_input = input_actor(command_stream) | 
| 39572 | 189 | val message = message_actor(message_stream) | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 190 | |
| 39587 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 191 | val rc = process_result.join | 
| 39618 | 192 |       system_result("process terminated")
 | 
| 45633 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 193 | for ((thread, _) <- List(standard_input, stdout, stderr, command_input, message)) | 
| 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 194 | thread.join | 
| 39572 | 195 |       system_result("process_manager terminated")
 | 
| 45666 | 196 | put_result(Isabelle_Markup.EXIT, "Return code: " + rc.toString) | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 197 | } | 
| 45027 
f459e93a038e
more abstract wrapping of fifos as System_Channel;
 wenzelm parents: 
44775diff
changeset | 198 | system_channel.accepted() | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 199 | } | 
| 27973 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 200 | |
| 39572 | 201 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 202 | /* management methods */ | 
| 27973 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 203 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 204 |   def join() { process_manager.join() }
 | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 205 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 206 | def terminate() | 
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 207 |   {
 | 
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 208 | close() | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 209 |     system_result("Terminating Isabelle process")
 | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 210 | terminate_process() | 
| 27973 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 211 | } | 
| 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 212 | |
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 213 | |
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 214 | |
| 38259 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 215 | /** stream actors **/ | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 216 | |
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 217 | /* raw stdin */ | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 218 | |
| 39572 | 219 | private def stdin_actor(): (Thread, Actor) = | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 220 |   {
 | 
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 221 | val name = "standard_input" | 
| 38636 
b7647ca7de5a
module for simplified thread operations (Scala version);
 wenzelm parents: 
38573diff
changeset | 222 |     Simple_Thread.actor(name) {
 | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 223 | var finished = false | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 224 |       while (!finished) {
 | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 225 |         try {
 | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 226 |           //{{{
 | 
| 38259 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 227 |           receive {
 | 
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 228 | case Input_Text(text) => | 
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 229 | process.stdin.write(text) | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 230 | process.stdin.flush | 
| 38259 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 231 | case Close => | 
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 232 | process.stdin.close | 
| 38259 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 233 | finished = true | 
| 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 234 | case bad => System.err.println(name + ": ignoring bad message " + bad) | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 235 | } | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 236 | //}}} | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 237 | } | 
| 39525 | 238 |         catch { case e: IOException => system_result(name + ": " + e.getMessage) }
 | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 239 | } | 
| 39525 | 240 | system_result(name + " terminated") | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 241 | } | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 242 | } | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 243 | |
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 244 | |
| 45633 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 245 | /* raw output */ | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 246 | |
| 45633 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 247 | private def raw_output_actor(err: Boolean): (Thread, Actor) = | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 248 |   {
 | 
| 45633 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 249 | val (name, reader, markup) = | 
| 45666 | 250 |       if (err) ("standard_error", process.stderr, Isabelle_Markup.STDERR)
 | 
| 251 |       else ("standard_output", process.stdout, Isabelle_Markup.STDOUT)
 | |
| 45633 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 252 | |
| 38636 
b7647ca7de5a
module for simplified thread operations (Scala version);
 wenzelm parents: 
38573diff
changeset | 253 |     Simple_Thread.actor(name) {
 | 
| 28045 | 254 | var result = new StringBuilder(100) | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 255 | |
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 256 | var finished = false | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 257 |       while (!finished) {
 | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 258 |         try {
 | 
| 28045 | 259 |           //{{{
 | 
| 260 | var c = -1 | |
| 261 | var done = false | |
| 45633 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 262 |           while (!done && (result.length == 0 || reader.ready)) {
 | 
| 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 263 | c = reader.read | 
| 28045 | 264 | if (c >= 0) result.append(c.asInstanceOf[Char]) | 
| 265 | else done = true | |
| 266 | } | |
| 267 |           if (result.length > 0) {
 | |
| 45633 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 268 | put_result(markup, result.toString) | 
| 28045 | 269 | result.length = 0 | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 270 | } | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 271 |           else {
 | 
| 45633 
2cb7e34f6096
retain stderr and include it in syslog, which is buffered and thus increases the chance that users see remains from crashes etc.;
 wenzelm parents: 
45158diff
changeset | 272 | reader.close | 
| 28045 | 273 | finished = true | 
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 274 | } | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 275 | //}}} | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 276 | } | 
| 39525 | 277 |         catch { case e: IOException => system_result(name + ": " + e.getMessage) }
 | 
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 278 | } | 
| 39525 | 279 | system_result(name + " terminated") | 
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 280 | } | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 281 | } | 
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 282 | |
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 283 | |
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 284 | /* command input */ | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 285 | |
| 39572 | 286 | private def input_actor(raw_stream: OutputStream): (Thread, Actor) = | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 287 |   {
 | 
| 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 288 | val name = "command_input" | 
| 38636 
b7647ca7de5a
module for simplified thread operations (Scala version);
 wenzelm parents: 
38573diff
changeset | 289 |     Simple_Thread.actor(name) {
 | 
| 39530 
16adc476348f
Isabelle_Process: more robust rendezvous, even without proper blocking on open (Cygwin);
 wenzelm parents: 
39528diff
changeset | 290 | val stream = new BufferedOutputStream(raw_stream) | 
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 291 | var finished = false | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 292 |       while (!finished) {
 | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 293 |         try {
 | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 294 |           //{{{
 | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 295 |           receive {
 | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 296 | case Input_Chunks(chunks) => | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 297 | stream.write(Standard_System.string_bytes( | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 298 |                   chunks.map(_.length).mkString("", ",", "\n")));
 | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 299 | chunks.foreach(stream.write(_)); | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 300 | stream.flush | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 301 | case Close => | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 302 | stream.close | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 303 | finished = true | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 304 | case bad => System.err.println(name + ": ignoring bad message " + bad) | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 305 | } | 
| 28045 | 306 | //}}} | 
| 27963 | 307 | } | 
| 39525 | 308 |         catch { case e: IOException => system_result(name + ": " + e.getMessage) }
 | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 309 | } | 
| 39525 | 310 | system_result(name + " terminated") | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 311 | } | 
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 312 | } | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 313 | |
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 314 | |
| 38259 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 315 | /* message output */ | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 316 | |
| 39572 | 317 | private def message_actor(stream: InputStream): (Thread, Actor) = | 
| 39526 | 318 |   {
 | 
| 319 | class EOF extends Exception | |
| 320 | class Protocol_Error(msg: String) extends Exception(msg) | |
| 38259 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 321 | |
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 322 | val name = "message_output" | 
| 38636 
b7647ca7de5a
module for simplified thread operations (Scala version);
 wenzelm parents: 
38573diff
changeset | 323 |     Simple_Thread.actor(name) {
 | 
| 34100 | 324 | val default_buffer = new Array[Byte](65536) | 
| 325 | var c = -1 | |
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 326 | |
| 43746 
a41f618c641d
some support for raw messages, which bypass standard Symbol/YXML decoding;
 wenzelm parents: 
43745diff
changeset | 327 | def read_chunk(decode: Boolean): XML.Body = | 
| 34100 | 328 |       {
 | 
| 329 |         //{{{
 | |
| 330 | // chunk size | |
| 331 | var n = 0 | |
| 332 | c = stream.read | |
| 39526 | 333 | if (c == -1) throw new EOF | 
| 34100 | 334 |         while (48 <= c && c <= 57) {
 | 
| 335 | n = 10 * n + (c - 48) | |
| 336 | c = stream.read | |
| 337 | } | |
| 338 |         if (c != 10) throw new Protocol_Error("bad message chunk header")
 | |
| 339 | ||
| 340 | // chunk content | |
| 341 | val buf = | |
| 342 | if (n <= default_buffer.size) default_buffer | |
| 343 | else new Array[Byte](n) | |
| 344 | ||
| 345 | var i = 0 | |
| 346 | var m = 0 | |
| 347 |         do {
 | |
| 348 | m = stream.read(buf, i, n - i) | |
| 39731 
5cb0d7b0d601
bulk read: observe EOF protocol more carefully -- 0 counts as successful read;
 wenzelm parents: 
39632diff
changeset | 349 | if (m != -1) i += m | 
| 
5cb0d7b0d601
bulk read: observe EOF protocol more carefully -- 0 counts as successful read;
 wenzelm parents: 
39632diff
changeset | 350 | } while (m != -1 && n > i) | 
| 34100 | 351 | |
| 352 |         if (i != n) throw new Protocol_Error("bad message chunk content")
 | |
| 353 | ||
| 43746 
a41f618c641d
some support for raw messages, which bypass standard Symbol/YXML decoding;
 wenzelm parents: 
43745diff
changeset | 354 | if (decode) | 
| 
a41f618c641d
some support for raw messages, which bypass standard Symbol/YXML decoding;
 wenzelm parents: 
43745diff
changeset | 355 | YXML.parse_body_failsafe(Standard_System.decode_chars(Symbol.decode, buf, 0, n)) | 
| 
a41f618c641d
some support for raw messages, which bypass standard Symbol/YXML decoding;
 wenzelm parents: 
43745diff
changeset | 356 | else List(XML.Text(Standard_System.decode_chars(s => s, buf, 0, n).toString)) | 
| 34100 | 357 | //}}} | 
| 358 | } | |
| 359 | ||
| 360 |       do {
 | |
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 361 |         try {
 | 
| 43746 
a41f618c641d
some support for raw messages, which bypass standard Symbol/YXML decoding;
 wenzelm parents: 
43745diff
changeset | 362 | val header = read_chunk(true) | 
| 38445 
ba9ea6b9b75c
simplified internal message format: dropped special Symbol.STX header;
 wenzelm parents: 
38372diff
changeset | 363 |           header match {
 | 
| 
ba9ea6b9b75c
simplified internal message format: dropped special Symbol.STX header;
 wenzelm parents: 
38372diff
changeset | 364 | case List(XML.Elem(Markup(name, props), Nil)) | 
| 39525 | 365 | if name.size == 1 && Kind.message_markup.isDefinedAt(name(0)) => | 
| 43746 
a41f618c641d
some support for raw messages, which bypass standard Symbol/YXML decoding;
 wenzelm parents: 
43745diff
changeset | 366 | val kind = Kind.message_markup(name(0)) | 
| 45666 | 367 | val body = read_chunk(kind != Isabelle_Markup.RAW) | 
| 43746 
a41f618c641d
some support for raw messages, which bypass standard Symbol/YXML decoding;
 wenzelm parents: 
43745diff
changeset | 368 | put_result(kind, props, body) | 
| 
a41f618c641d
some support for raw messages, which bypass standard Symbol/YXML decoding;
 wenzelm parents: 
43745diff
changeset | 369 | case _ => | 
| 
a41f618c641d
some support for raw messages, which bypass standard Symbol/YXML decoding;
 wenzelm parents: 
43745diff
changeset | 370 | read_chunk(false) | 
| 
a41f618c641d
some support for raw messages, which bypass standard Symbol/YXML decoding;
 wenzelm parents: 
43745diff
changeset | 371 |               throw new Protocol_Error("bad header: " + header.toString)
 | 
| 28063 | 372 | } | 
| 27963 | 373 | } | 
| 28063 | 374 |         catch {
 | 
| 39525 | 375 |           case e: IOException => system_result("Cannot read message:\n" + e.getMessage)
 | 
| 376 |           case e: Protocol_Error => system_result("Malformed message:\n" + e.getMessage)
 | |
| 39526 | 377 | case _: EOF => | 
| 28063 | 378 | } | 
| 34100 | 379 | } while (c != -1) | 
| 380 | stream.close | |
| 381 | ||
| 39525 | 382 | system_result(name + " terminated") | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 383 | } | 
| 39526 | 384 | } | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 385 | |
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 386 | |
| 38259 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 387 | /** main methods **/ | 
| 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 388 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 389 | def input_raw(text: String): Unit = standard_input._2 ! Input_Text(text) | 
| 38259 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 390 | |
| 38372 
e753f71b6b34
added Isabelle_Process.input_bytes, which avoids the somewhat slow Standard_System.string_bytes (just in case someone wants to stream raw data at 250MB/s);
 wenzelm parents: 
38270diff
changeset | 391 | def input_bytes(name: String, args: Array[Byte]*): Unit = | 
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 392 | command_input._2 ! Input_Chunks(Standard_System.string_bytes(name) :: args.toList) | 
| 38372 
e753f71b6b34
added Isabelle_Process.input_bytes, which avoids the somewhat slow Standard_System.string_bytes (just in case someone wants to stream raw data at 250MB/s);
 wenzelm parents: 
38270diff
changeset | 393 | |
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 394 | def input(name: String, args: String*): Unit = | 
| 43721 
fad8634cee62
echo prover input via raw_messages, for improved protocol tracing;
 wenzelm parents: 
43695diff
changeset | 395 |   {
 | 
| 44732 | 396 | receiver(new Input(name, args.toList)) | 
| 38372 
e753f71b6b34
added Isabelle_Process.input_bytes, which avoids the somewhat slow Standard_System.string_bytes (just in case someone wants to stream raw data at 250MB/s);
 wenzelm parents: 
38270diff
changeset | 397 | input_bytes(name, args.map(Standard_System.string_bytes): _*) | 
| 43721 
fad8634cee62
echo prover input via raw_messages, for improved protocol tracing;
 wenzelm parents: 
43695diff
changeset | 398 | } | 
| 38259 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 399 | |
| 39528 
c01d89d18ff0
refined Isabelle_Process startup: emit \002 before rendezvous on fifos, more robust treatment of startup failure with timeout, do not quit() after main loop;
 wenzelm parents: 
39526diff
changeset | 400 |   def close(): Unit = { close(command_input); close(standard_input) }
 | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 401 | } |