| author | blanchet | 
| Thu, 22 May 2014 13:07:52 +0200 | |
| changeset 57059 | fcd25f2e3da6 | 
| parent 56831 | e3ccf0809d51 | 
| child 57915 | 448325de6e4f | 
| 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 | |
| 55618 | 10 | |
| 56703 | 11 | import java.io.{InputStream, OutputStream, BufferedOutputStream, IOException}
 | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 12 | |
| 27973 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 13 | |
| 45055 
55274f7e306b
explicit option for socket vs. fifo communication;
 wenzelm parents: 
45027diff
changeset | 14 | class Isabelle_Process( | 
| 56831 
e3ccf0809d51
prefer scala.Console with its support for thread-local redirection;
 wenzelm parents: 
56794diff
changeset | 15 | receiver: Prover.Message => Unit = Console.println(_), | 
| 56387 | 16 | prover_args: List[String] = Nil) | 
| 29192 | 17 | {
 | 
| 56387 | 18 | /* text and tree data */ | 
| 48705 
dd32321d6eef
tuned signature -- slightly more abstract text representation of prover process;
 wenzelm parents: 
48355diff
changeset | 19 | |
| 
dd32321d6eef
tuned signature -- slightly more abstract text representation of prover process;
 wenzelm parents: 
48355diff
changeset | 20 | def encode(s: String): String = Symbol.encode(s) | 
| 
dd32321d6eef
tuned signature -- slightly more abstract text representation of prover process;
 wenzelm parents: 
48355diff
changeset | 21 | def decode(s: String): String = Symbol.decode(s) | 
| 
dd32321d6eef
tuned signature -- slightly more abstract text representation of prover process;
 wenzelm parents: 
48355diff
changeset | 22 | |
| 56387 | 23 | val xml_cache = new XML.Cache() | 
| 48705 
dd32321d6eef
tuned signature -- slightly more abstract text representation of prover process;
 wenzelm parents: 
48355diff
changeset | 24 | |
| 
dd32321d6eef
tuned signature -- slightly more abstract text representation of prover process;
 wenzelm parents: 
48355diff
changeset | 25 | |
| 46772 
be21f050eda4
tuned signature -- emphasize Isabelle_Process Input vs. Output;
 wenzelm parents: 
46769diff
changeset | 26 | /* output */ | 
| 39573 | 27 | |
| 46772 
be21f050eda4
tuned signature -- emphasize Isabelle_Process Input vs. Output;
 wenzelm parents: 
46769diff
changeset | 28 | private def system_output(text: String) | 
| 39573 | 29 |   {
 | 
| 56385 | 30 | receiver(new Prover.Output(XML.Elem(Markup(Markup.SYSTEM, Nil), List(XML.Text(text))))) | 
| 39573 | 31 | } | 
| 32 | ||
| 54442 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 33 | private def protocol_output(props: Properties.T, bytes: Bytes) | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 34 |   {
 | 
| 56385 | 35 | receiver(new Prover.Protocol_Output(props, bytes)) | 
| 54442 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 36 | } | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 37 | |
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 38 | private def output(kind: String, props: Properties.T, body: XML.Body) | 
| 39573 | 39 |   {
 | 
| 50201 
c26369c9eda6
Isabelle-specific implementation of quasi-abstract markup elements -- back to module arrangement before d83797ef0d2d;
 wenzelm parents: 
50117diff
changeset | 40 | if (kind == Markup.INIT) system_channel.accepted() | 
| 54442 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 41 | |
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 42 | val main = XML.Elem(Markup(kind, props), Protocol.clean_message(body)) | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 43 | val reports = Protocol.message_reports(props, body) | 
| 56385 | 44 | for (msg <- main :: reports) receiver(new Prover.Output(xml_cache.elem(msg))) | 
| 39573 | 45 | } | 
| 46 | ||
| 48016 
edbc8e8accd9
more explicit treatment of return code vs. session phase;
 wenzelm parents: 
46774diff
changeset | 47 | private def exit_message(rc: Int) | 
| 39573 | 48 |   {
 | 
| 54442 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 49 |     output(Markup.EXIT, Markup.Return_Code(rc), List(XML.Text("Return code: " + rc.toString)))
 | 
| 39573 | 50 | } | 
| 51 | ||
| 52 | ||
| 56387 | 53 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 54 | /** 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 | 55 | |
| 56390 
8185044353fd
recovered public command_line from d92eb5c3960d, which is important for alternative prover processes;
 wenzelm parents: 
56387diff
changeset | 56 | def command_line(channel: System_Channel, args: List[String]): List[String] = | 
| 
8185044353fd
recovered public command_line from d92eb5c3960d, which is important for alternative prover processes;
 wenzelm parents: 
56387diff
changeset | 57 |     Isabelle_System.getenv_strict("ISABELLE_PROCESS") :: (channel.isabelle_args ::: args)
 | 
| 
8185044353fd
recovered public command_line from d92eb5c3960d, which is important for alternative prover processes;
 wenzelm parents: 
56387diff
changeset | 58 | |
| 45158 | 59 | 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 | 60 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 61 | 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 | 62 |     try {
 | 
| 56390 
8185044353fd
recovered public command_line from d92eb5c3960d, which is important for alternative prover processes;
 wenzelm parents: 
56387diff
changeset | 63 | val cmdline = command_line(system_channel, prover_args) | 
| 48353 
bcce872202b3
support external processes with explicit environment;
 wenzelm parents: 
48020diff
changeset | 64 | new Isabelle_System.Managed_Process(null, null, 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 | 65 | } | 
| 45027 
f459e93a038e
more abstract wrapping of fifos as System_Channel;
 wenzelm parents: 
44775diff
changeset | 66 |     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 | 67 | |
| 56387 | 68 | private val (_, process_result) = | 
| 39587 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 69 |     Simple_Thread.future("process_result") { process.join }
 | 
| 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 70 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 71 | private def terminate_process() | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 72 |   {
 | 
| 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 73 |     try { process.terminate }
 | 
| 46772 
be21f050eda4
tuned signature -- emphasize Isabelle_Process Input vs. Output;
 wenzelm parents: 
46769diff
changeset | 74 |     catch { case e: IOException => system_output("Failed to terminate Isabelle: " + e.getMessage) }
 | 
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 75 | } | 
| 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 | 76 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 77 |   private val process_manager = Simple_Thread.fork("process_manager")
 | 
| 39572 | 78 |   {
 | 
| 46548 
c54a4a22501c
clarified initial process startup errors: recover image load failure message (cf. 2cb7e34f6096) and suppress accidental output from raw ML toplevel;
 wenzelm parents: 
46121diff
changeset | 79 | val (startup_failed, startup_errors) = | 
| 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 | 80 |     {
 | 
| 48020 
a4f9957878ab
clarified prover startup: no timeout, read stderr more carefully;
 wenzelm parents: 
48019diff
changeset | 81 | var finished: Option[Boolean] = None | 
| 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 | 82 | val result = new StringBuilder(100) | 
| 48020 
a4f9957878ab
clarified prover startup: no timeout, read stderr more carefully;
 wenzelm parents: 
48019diff
changeset | 83 |       while (finished.isEmpty && (process.stderr.ready || !process_result.is_finished)) {
 | 
| 46548 
c54a4a22501c
clarified initial process startup errors: recover image load failure message (cf. 2cb7e34f6096) and suppress accidental output from raw ML toplevel;
 wenzelm parents: 
46121diff
changeset | 84 |         while (finished.isEmpty && process.stderr.ready) {
 | 
| 48020 
a4f9957878ab
clarified prover startup: no timeout, read stderr more carefully;
 wenzelm parents: 
48019diff
changeset | 85 |           try {
 | 
| 
a4f9957878ab
clarified prover startup: no timeout, read stderr more carefully;
 wenzelm parents: 
48019diff
changeset | 86 | val c = process.stderr.read | 
| 
a4f9957878ab
clarified prover startup: no timeout, read stderr more carefully;
 wenzelm parents: 
48019diff
changeset | 87 | if (c == 2) finished = Some(true) | 
| 
a4f9957878ab
clarified prover startup: no timeout, read stderr more carefully;
 wenzelm parents: 
48019diff
changeset | 88 | else result += c.toChar | 
| 
a4f9957878ab
clarified prover startup: no timeout, read stderr more carefully;
 wenzelm parents: 
48019diff
changeset | 89 | } | 
| 
a4f9957878ab
clarified prover startup: no timeout, read stderr more carefully;
 wenzelm parents: 
48019diff
changeset | 90 |           catch { case _: IOException => finished = Some(false) }
 | 
| 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 | 91 | } | 
| 48020 
a4f9957878ab
clarified prover startup: no timeout, read stderr more carefully;
 wenzelm parents: 
48019diff
changeset | 92 | 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 | 93 | } | 
| 39623 | 94 | (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 | 95 | } | 
| 46772 
be21f050eda4
tuned signature -- emphasize Isabelle_Process Input vs. Output;
 wenzelm parents: 
46769diff
changeset | 96 | if (startup_errors != "") system_output(startup_errors) | 
| 39572 | 97 | |
| 52581 | 98 | process.stdin.close | 
| 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 | 99 |     if (startup_failed) {
 | 
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 100 | terminate_process() | 
| 39587 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 101 | process_result.join | 
| 56794 
a7c5c35b7125
clarified exit sequence: prover is reset afterwards, no more output messages;
 wenzelm parents: 
56784diff
changeset | 102 | exit_message(127) | 
| 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 | 103 | } | 
| 
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 | 104 |     else {
 | 
| 45027 
f459e93a038e
more abstract wrapping of fifos as System_Channel;
 wenzelm parents: 
44775diff
changeset | 105 | 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 | 106 | |
| 56700 | 107 | command_input_init(command_stream) | 
| 56697 | 108 | val stdout = physical_output(false) | 
| 109 | val stderr = physical_output(true) | |
| 110 | val message = message_output(message_stream) | |
| 111 | ||
| 39587 
f84b70e3bb9c
more reactive handling of Isabelle_Process startup errors;
 wenzelm parents: 
39585diff
changeset | 112 | val rc = process_result.join | 
| 46772 
be21f050eda4
tuned signature -- emphasize Isabelle_Process Input vs. Output;
 wenzelm parents: 
46769diff
changeset | 113 |       system_output("process terminated")
 | 
| 56700 | 114 | command_input_close() | 
| 56697 | 115 | for (thread <- List(stdout, stderr, message)) thread.join | 
| 46772 
be21f050eda4
tuned signature -- emphasize Isabelle_Process Input vs. Output;
 wenzelm parents: 
46769diff
changeset | 116 |       system_output("process_manager terminated")
 | 
| 48016 
edbc8e8accd9
more explicit treatment of return code vs. session phase;
 wenzelm parents: 
46774diff
changeset | 117 | exit_message(rc) | 
| 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 | 118 | } | 
| 45027 
f459e93a038e
more abstract wrapping of fifos as System_Channel;
 wenzelm parents: 
44775diff
changeset | 119 | 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 | 120 | } | 
| 27973 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 121 | |
| 39572 | 122 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 123 | /* management methods */ | 
| 27973 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 124 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 125 |   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 | 126 | |
| 56617 
c00646996701
reintroduced process interrupt for the sake of synchronous protocol commands like "use_theories" (see also 27930cf6f0f7);
 wenzelm parents: 
56390diff
changeset | 127 | def interrupt() | 
| 
c00646996701
reintroduced process interrupt for the sake of synchronous protocol commands like "use_theories" (see also 27930cf6f0f7);
 wenzelm parents: 
56390diff
changeset | 128 |   {
 | 
| 
c00646996701
reintroduced process interrupt for the sake of synchronous protocol commands like "use_theories" (see also 27930cf6f0f7);
 wenzelm parents: 
56390diff
changeset | 129 |     try { process.interrupt }
 | 
| 
c00646996701
reintroduced process interrupt for the sake of synchronous protocol commands like "use_theories" (see also 27930cf6f0f7);
 wenzelm parents: 
56390diff
changeset | 130 |     catch { case e: IOException => system_output("Failed to interrupt Isabelle: " + e.getMessage) }
 | 
| 
c00646996701
reintroduced process interrupt for the sake of synchronous protocol commands like "use_theories" (see also 27930cf6f0f7);
 wenzelm parents: 
56390diff
changeset | 131 | } | 
| 
c00646996701
reintroduced process interrupt for the sake of synchronous protocol commands like "use_theories" (see also 27930cf6f0f7);
 wenzelm parents: 
56390diff
changeset | 132 | |
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 133 | def terminate() | 
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 134 |   {
 | 
| 56700 | 135 | command_input_close() | 
| 46772 
be21f050eda4
tuned signature -- emphasize Isabelle_Process Input vs. Output;
 wenzelm parents: 
46769diff
changeset | 136 |     system_output("Terminating Isabelle process")
 | 
| 39585 
00be8711082f
main Isabelle_Process via Isabelle_System.Managed_Process;
 wenzelm parents: 
39575diff
changeset | 137 | terminate_process() | 
| 27973 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 138 | } | 
| 
18d02c0b90b6
moved class Result into static object, removed dynamic tree method;
 wenzelm parents: 
27963diff
changeset | 139 | |
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 140 | |
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 141 | |
| 56714 | 142 | /** process streams **/ | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 143 | |
| 56700 | 144 | /* command input */ | 
| 145 | ||
| 146 | private var command_input: Option[Consumer_Thread[List[Bytes]]] = None | |
| 147 | ||
| 148 | private def command_input_close(): Unit = command_input.foreach(_.shutdown) | |
| 149 | ||
| 150 | private def command_input_init(raw_stream: OutputStream) | |
| 151 |   {
 | |
| 152 | val name = "command_input" | |
| 153 | val stream = new BufferedOutputStream(raw_stream) | |
| 154 | command_input = | |
| 155 | Some( | |
| 156 | Consumer_Thread.fork(name)( | |
| 157 | consume = | |
| 158 |             {
 | |
| 159 | case chunks => | |
| 160 |                 try {
 | |
| 161 |                   Bytes(chunks.map(_.length).mkString("", ",", "\n")).write(stream)
 | |
| 162 | chunks.foreach(_.write(stream)) | |
| 163 | stream.flush | |
| 164 | true | |
| 165 | } | |
| 166 |                 catch { case e: IOException => system_output(name + ": " + e.getMessage); false }
 | |
| 167 | }, | |
| 168 |           finish = { case () => stream.close; system_output(name + " terminated") }
 | |
| 169 | ) | |
| 170 | ) | |
| 171 | } | |
| 172 | ||
| 173 | ||
| 46773 | 174 | /* physical output */ | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 175 | |
| 56697 | 176 | private def physical_output(err: Boolean): Thread = | 
| 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 | 177 |   {
 | 
| 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 | 178 | val (name, reader, markup) = | 
| 50201 
c26369c9eda6
Isabelle-specific implementation of quasi-abstract markup elements -- back to module arrangement before d83797ef0d2d;
 wenzelm parents: 
50117diff
changeset | 179 |       if (err) ("standard_error", process.stderr, Markup.STDERR)
 | 
| 
c26369c9eda6
Isabelle-specific implementation of quasi-abstract markup elements -- back to module arrangement before d83797ef0d2d;
 wenzelm parents: 
50117diff
changeset | 180 |       else ("standard_output", process.stdout, 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 | 181 | |
| 56697 | 182 |     Simple_Thread.fork(name) {
 | 
| 46769 | 183 |       try {
 | 
| 184 | var result = new StringBuilder(100) | |
| 185 | var finished = false | |
| 186 |         while (!finished) {
 | |
| 28045 | 187 |           //{{{
 | 
| 188 | var c = -1 | |
| 189 | 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 | 190 |           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 | 191 | c = reader.read | 
| 28045 | 192 | if (c >= 0) result.append(c.asInstanceOf[Char]) | 
| 193 | else done = true | |
| 194 | } | |
| 195 |           if (result.length > 0) {
 | |
| 54442 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 196 | output(markup, Nil, List(XML.Text(decode(result.toString)))) | 
| 28045 | 197 | result.length = 0 | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 198 | } | 
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 199 |           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 | 200 | reader.close | 
| 28045 | 201 | finished = true | 
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 202 | } | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 203 | //}}} | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 204 | } | 
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 205 | } | 
| 46772 
be21f050eda4
tuned signature -- emphasize Isabelle_Process Input vs. Output;
 wenzelm parents: 
46769diff
changeset | 206 |       catch { case e: IOException => system_output(name + ": " + e.getMessage) }
 | 
| 
be21f050eda4
tuned signature -- emphasize Isabelle_Process Input vs. Output;
 wenzelm parents: 
46769diff
changeset | 207 | system_output(name + " terminated") | 
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 208 | } | 
| 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 | 209 | } | 
| 38270 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 210 | |
| 
71bb3c273dd1
native Isabelle_Process commands, based on efficient byte channel protocol for string lists;
 wenzelm parents: 
38262diff
changeset | 211 | |
| 38259 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 212 | /* message output */ | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 213 | |
| 56697 | 214 | private def message_output(stream: InputStream): Thread = | 
| 39526 | 215 |   {
 | 
| 216 | class EOF extends Exception | |
| 217 | 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 | 218 | |
| 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 | 219 | val name = "message_output" | 
| 56697 | 220 |     Simple_Thread.fork(name) {
 | 
| 34100 | 221 | val default_buffer = new Array[Byte](65536) | 
| 222 | var c = -1 | |
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 223 | |
| 52799 | 224 | def read_int(): Int = | 
| 225 |       //{{{
 | |
| 34100 | 226 |       {
 | 
| 227 | var n = 0 | |
| 228 | c = stream.read | |
| 39526 | 229 | if (c == -1) throw new EOF | 
| 34100 | 230 |         while (48 <= c && c <= 57) {
 | 
| 231 | n = 10 * n + (c - 48) | |
| 232 | c = stream.read | |
| 233 | } | |
| 52799 | 234 | if (c != 10) | 
| 235 |           throw new Protocol_Error("malformed header: expected integer followed by newline")
 | |
| 236 | else n | |
| 237 | } | |
| 238 | //}}} | |
| 34100 | 239 | |
| 54442 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 240 | def read_chunk_bytes(): (Array[Byte], Int) = | 
| 52799 | 241 |       //{{{
 | 
| 242 |       {
 | |
| 243 | val n = read_int() | |
| 34100 | 244 | val buf = | 
| 245 | if (n <= default_buffer.size) default_buffer | |
| 246 | else new Array[Byte](n) | |
| 247 | ||
| 248 | var i = 0 | |
| 249 | var m = 0 | |
| 250 |         do {
 | |
| 251 | 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 | 252 | if (m != -1) i += m | 
| 52799 | 253 | } | 
| 254 | while (m != -1 && n > i) | |
| 34100 | 255 | |
| 52799 | 256 | if (i != n) | 
| 257 |           throw new Protocol_Error("bad chunk (unexpected EOF after " + i + " of " + n + " bytes)")
 | |
| 34100 | 258 | |
| 54442 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 259 | (buf, n) | 
| 34100 | 260 | } | 
| 52799 | 261 | //}}} | 
| 34100 | 262 | |
| 54442 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 263 | def read_chunk(): XML.Body = | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 264 |       {
 | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 265 | val (buf, n) = read_chunk_bytes() | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 266 | YXML.parse_body_failsafe(UTF8.decode_chars(decode, buf, 0, n)) | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 267 | } | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 268 | |
| 46769 | 269 |       try {
 | 
| 270 |         do {
 | |
| 271 |           try {
 | |
| 54442 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 272 | val header = read_chunk() | 
| 46769 | 273 |             header match {
 | 
| 49677 
c4e2762a265c
more direct message header: eliminated historic encoding via single letter;
 wenzelm parents: 
49445diff
changeset | 274 | case List(XML.Elem(Markup(name, props), Nil)) => | 
| 
c4e2762a265c
more direct message header: eliminated historic encoding via single letter;
 wenzelm parents: 
49445diff
changeset | 275 | val kind = name.intern | 
| 54442 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 276 |                 if (kind == Markup.PROTOCOL) {
 | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 277 | val (buf, n) = read_chunk_bytes() | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 278 | protocol_output(props, Bytes(buf, 0, n)) | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 279 | } | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 280 |                 else {
 | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 281 | val body = read_chunk() | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 282 | output(kind, props, body) | 
| 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 283 | } | 
| 46769 | 284 | case _ => | 
| 54442 
c39972ddd672
more specific Protocol_Output: empty message.body, main content via bytes/text;
 wenzelm parents: 
54440diff
changeset | 285 | read_chunk() | 
| 46769 | 286 |                 throw new Protocol_Error("bad header: " + header.toString)
 | 
| 287 | } | |
| 28063 | 288 | } | 
| 46769 | 289 |           catch { case _: EOF => }
 | 
| 52799 | 290 | } | 
| 291 | while (c != -1) | |
| 46769 | 292 | } | 
| 293 |       catch {
 | |
| 46772 
be21f050eda4
tuned signature -- emphasize Isabelle_Process Input vs. Output;
 wenzelm parents: 
46769diff
changeset | 294 |         case e: IOException => system_output("Cannot read message:\n" + e.getMessage)
 | 
| 
be21f050eda4
tuned signature -- emphasize Isabelle_Process Input vs. Output;
 wenzelm parents: 
46769diff
changeset | 295 |         case e: Protocol_Error => system_output("Malformed message:\n" + e.getMessage)
 | 
| 46769 | 296 | } | 
| 34100 | 297 | stream.close | 
| 298 | ||
| 46772 
be21f050eda4
tuned signature -- emphasize Isabelle_Process Input vs. Output;
 wenzelm parents: 
46769diff
changeset | 299 | system_output(name + " terminated") | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 300 | } | 
| 39526 | 301 | } | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 302 | |
| 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 303 | |
| 38259 
2b61c5e27399
distinguish proper Isabelle_Process INPUT vs. raw STDIN, tuned corresponding method names;
 wenzelm parents: 
38253diff
changeset | 304 | |
| 56387 | 305 | /** protocol commands **/ | 
| 306 | ||
| 307 | def protocol_command_bytes(name: String, args: Bytes*): Unit = | |
| 56700 | 308 |     command_input match {
 | 
| 309 | case Some(thread) => thread.send(Bytes(name) :: args.toList) | |
| 310 |       case None => error("Uninitialized command input thread")
 | |
| 311 | } | |
| 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 | 312 | |
| 52582 | 313 | def protocol_command(name: String, args: String*) | 
| 43721 
fad8634cee62
echo prover input via raw_messages, for improved protocol tracing;
 wenzelm parents: 
43695diff
changeset | 314 |   {
 | 
| 56385 | 315 | receiver(new Prover.Input(name, args.toList)) | 
| 56387 | 316 | protocol_command_bytes(name, args.map(Bytes(_)): _*) | 
| 43721 
fad8634cee62
echo prover input via raw_messages, for improved protocol tracing;
 wenzelm parents: 
43695diff
changeset | 317 | } | 
| 27949 
6eb0327c0b9b
Isabelle process management -- always reactive due to multi-threaded I/O.
 wenzelm parents: diff
changeset | 318 | } |