58 } |
58 } |
59 } |
59 } |
60 } |
60 } |
61 |
61 |
62 |
62 |
63 class Isabelle_Process(system: Isabelle_System, receiver: Actor, args: String*) |
63 class Isabelle_Process(system: Isabelle_System, timeout: Int, receiver: Actor, args: String*) |
64 { |
64 { |
65 import Isabelle_Process._ |
65 import Isabelle_Process._ |
66 |
66 |
67 |
67 |
68 /* demo constructor */ |
68 /* demo constructor */ |
69 |
69 |
70 def this(args: String*) = |
70 def this(args: String*) = |
71 this(new Isabelle_System, |
71 this(new Isabelle_System, 10000, |
72 actor { loop { react { case res => Console.println(res) } } }, args: _*) |
72 actor { loop { react { case res => Console.println(res) } } }, args: _*) |
73 |
73 |
74 |
74 |
75 /* process information */ |
75 /* input actors */ |
76 |
76 |
77 @volatile private var proc: Option[Process] = None |
77 private case class Input_Text(text: String) |
78 @volatile private var pid: Option[String] = None |
78 private case class Input_Chunks(chunks: List[Array[Byte]]) |
|
79 |
|
80 private case object Close |
|
81 private def close(a: Actor) { if (a != null) a ! Close } |
|
82 |
|
83 @volatile private var standard_input: Actor = null |
|
84 @volatile private var command_input: Actor = null |
|
85 |
|
86 |
|
87 /* process manager */ |
|
88 |
|
89 private val in_fifo = system.mk_fifo() |
|
90 private val out_fifo = system.mk_fifo() |
|
91 private def rm_fifos() { system.rm_fifo(in_fifo); system.rm_fifo(out_fifo) } |
|
92 |
|
93 private val proc = |
|
94 try { |
|
95 val cmdline = |
|
96 List(system.getenv_strict("ISABELLE_PROCESS"), "-W", in_fifo + ":" + out_fifo) ++ args |
|
97 system.execute(true, cmdline: _*) |
|
98 } |
|
99 catch { case e: IOException => rm_fifos(); throw(e) } |
|
100 |
|
101 private val stdout_reader = |
|
102 new BufferedReader(new InputStreamReader(proc.getInputStream, Standard_System.charset)) |
|
103 |
|
104 private val stdin_writer = |
|
105 new BufferedWriter(new OutputStreamWriter(proc.getOutputStream, Standard_System.charset)) |
|
106 |
|
107 Simple_Thread.actor("process_manager") { |
|
108 val (startup_failed, startup_output) = |
|
109 { |
|
110 val expired = System.currentTimeMillis() + timeout |
|
111 val result = new StringBuilder(100) |
|
112 |
|
113 var finished = false |
|
114 while (!finished && System.currentTimeMillis() <= expired) { |
|
115 while (!finished && stdout_reader.ready) { |
|
116 val c = stdout_reader.read |
|
117 if (c == 2) finished = true |
|
118 else result += c.toChar |
|
119 } |
|
120 Thread.sleep(10) |
|
121 } |
|
122 (!finished, result.toString) |
|
123 } |
|
124 if (startup_failed) { |
|
125 put_result(Markup.STDOUT, startup_output) |
|
126 put_result(Markup.EXIT, "127") |
|
127 stdin_writer.close |
|
128 Thread.sleep(300) // FIXME !? |
|
129 proc.destroy // FIXME reliable!? |
|
130 } |
|
131 else { |
|
132 put_result(Markup.SYSTEM, startup_output) |
|
133 |
|
134 standard_input = stdin_actor() |
|
135 stdout_actor() |
|
136 command_input = input_actor() |
|
137 message_actor() |
|
138 |
|
139 val rc = proc.waitFor() |
|
140 Thread.sleep(300) // FIXME !? |
|
141 system_result("Isabelle process terminated") |
|
142 put_result(Markup.EXIT, rc.toString) |
|
143 } |
|
144 rm_fifos() |
|
145 } |
79 |
146 |
80 |
147 |
81 /* results */ |
148 /* results */ |
82 |
149 |
83 private def system_result(text: String) |
150 private def system_result(text: String) |
108 } |
175 } |
109 |
176 |
110 |
177 |
111 /* signals */ |
178 /* signals */ |
112 |
179 |
|
180 @volatile private var pid: Option[String] = None |
|
181 |
113 def interrupt() |
182 def interrupt() |
114 { |
183 { |
115 if (proc.isEmpty) system_result("Cannot interrupt Isabelle: no process") |
184 pid match { |
116 else |
185 case None => system_result("Cannot interrupt Isabelle: unknowd pid") |
117 pid match { |
186 case Some(i) => |
118 case None => system_result("Cannot interrupt Isabelle: unknowd pid") |
187 try { |
119 case Some(i) => |
188 if (system.execute(true, "kill", "-INT", i).waitFor == 0) |
120 try { |
189 system_result("Interrupt Isabelle") |
121 if (system.execute(true, "kill", "-INT", i).waitFor == 0) |
190 else |
122 system_result("Interrupt Isabelle") |
191 system_result("Cannot interrupt Isabelle: kill command failed") |
123 else |
192 } |
124 system_result("Cannot interrupt Isabelle: kill command failed") |
193 catch { case e: IOException => error("Cannot interrupt Isabelle: " + e.getMessage) } |
125 } |
194 } |
126 catch { case e: IOException => error("Cannot interrupt Isabelle: " + e.getMessage) } |
|
127 } |
|
128 } |
195 } |
129 |
196 |
130 def kill() |
197 def kill() |
131 { |
198 { |
132 proc match { |
199 val running = |
133 case None => system_result("Cannot kill Isabelle: no process") |
200 try { proc.exitValue; false } |
134 case Some(p) => |
201 catch { case e: java.lang.IllegalThreadStateException => true } |
135 close() |
202 if (running) { |
136 Thread.sleep(500) // FIXME !? |
203 close() |
137 system_result("Kill Isabelle") |
204 Thread.sleep(500) // FIXME !? |
138 p.destroy |
205 system_result("Kill Isabelle") |
139 proc = None |
206 proc.destroy |
140 pid = None |
|
141 } |
207 } |
142 } |
208 } |
143 |
209 |
144 |
210 |
145 |
211 |
146 /** stream actors **/ |
212 /** stream actors **/ |
147 |
213 |
148 private case class Input_Text(text: String) |
|
149 private case class Input_Chunks(chunks: List[Array[Byte]]) |
|
150 private case object Close |
|
151 |
|
152 |
|
153 /* raw stdin */ |
214 /* raw stdin */ |
154 |
215 |
155 private def stdin_actor(name: String, stream: OutputStream): Actor = |
216 private def stdin_actor(): Actor = |
|
217 { |
|
218 val name = "standard_input" |
156 Simple_Thread.actor(name) { |
219 Simple_Thread.actor(name) { |
157 val writer = new BufferedWriter(new OutputStreamWriter(stream, Standard_System.charset)) |
|
158 var finished = false |
220 var finished = false |
159 while (!finished) { |
221 while (!finished) { |
160 try { |
222 try { |
161 //{{{ |
223 //{{{ |
162 receive { |
224 receive { |
163 case Input_Text(text) => |
225 case Input_Text(text) => |
164 writer.write(text) |
226 stdin_writer.write(text) |
165 writer.flush |
227 stdin_writer.flush |
166 case Close => |
228 case Close => |
167 writer.close |
229 stdin_writer.close |
168 finished = true |
230 finished = true |
169 case bad => System.err.println(name + ": ignoring bad message " + bad) |
231 case bad => System.err.println(name + ": ignoring bad message " + bad) |
170 } |
232 } |
171 //}}} |
233 //}}} |
172 } |
234 } |
173 catch { case e: IOException => system_result(name + ": " + e.getMessage) } |
235 catch { case e: IOException => system_result(name + ": " + e.getMessage) } |
174 } |
236 } |
175 system_result(name + " terminated") |
237 system_result(name + " terminated") |
176 } |
238 } |
|
239 } |
177 |
240 |
178 |
241 |
179 /* raw stdout */ |
242 /* raw stdout */ |
180 |
243 |
181 private def stdout_actor(name: String, stream: InputStream): Actor = |
244 private def stdout_actor(): Actor = |
|
245 { |
|
246 val name = "standard_output" |
182 Simple_Thread.actor(name) { |
247 Simple_Thread.actor(name) { |
183 val reader = new BufferedReader(new InputStreamReader(stream, Standard_System.charset)) |
|
184 var result = new StringBuilder(100) |
248 var result = new StringBuilder(100) |
185 |
249 |
186 var finished = false |
250 var finished = false |
187 while (!finished) { |
251 while (!finished) { |
188 try { |
252 try { |
189 //{{{ |
253 //{{{ |
190 var c = -1 |
254 var c = -1 |
191 var done = false |
255 var done = false |
192 while (!done && (result.length == 0 || reader.ready)) { |
256 while (!done && (result.length == 0 || stdout_reader.ready)) { |
193 c = reader.read |
257 c = stdout_reader.read |
194 if (c >= 0) result.append(c.asInstanceOf[Char]) |
258 if (c >= 0) result.append(c.asInstanceOf[Char]) |
195 else done = true |
259 else done = true |
196 } |
260 } |
197 if (result.length > 0) { |
261 if (result.length > 0) { |
198 put_result(Markup.STDOUT, result.toString) |
262 put_result(Markup.STDOUT, result.toString) |
199 result.length = 0 |
263 result.length = 0 |
200 } |
264 } |
201 else { |
265 else { |
202 reader.close |
266 stdout_reader.close |
203 finished = true |
267 finished = true |
204 close() |
|
205 } |
268 } |
206 //}}} |
269 //}}} |
207 } |
270 } |
208 catch { case e: IOException => system_result(name + ": " + e.getMessage) } |
271 catch { case e: IOException => system_result(name + ": " + e.getMessage) } |
209 } |
272 } |
210 system_result(name + " terminated") |
273 system_result(name + " terminated") |
211 } |
274 } |
|
275 } |
212 |
276 |
213 |
277 |
214 /* command input */ |
278 /* command input */ |
215 |
279 |
216 private def input_actor(name: String, fifo: String): Actor = |
280 private def input_actor(): Actor = |
|
281 { |
|
282 val name = "command_input" |
217 Simple_Thread.actor(name) { |
283 Simple_Thread.actor(name) { |
218 val stream = new BufferedOutputStream(system.fifo_output_stream(fifo)) // FIXME potentially blocking forever |
284 val stream = new BufferedOutputStream(system.fifo_output_stream(in_fifo)) |
219 var finished = false |
285 var finished = false |
220 while (!finished) { |
286 while (!finished) { |
221 try { |
287 try { |
222 //{{{ |
288 //{{{ |
223 receive { |
289 receive { |
235 } |
301 } |
236 catch { case e: IOException => system_result(name + ": " + e.getMessage) } |
302 catch { case e: IOException => system_result(name + ": " + e.getMessage) } |
237 } |
303 } |
238 system_result(name + " terminated") |
304 system_result(name + " terminated") |
239 } |
305 } |
|
306 } |
240 |
307 |
241 |
308 |
242 /* message output */ |
309 /* message output */ |
243 |
310 |
244 private def message_actor(name: String, fifo: String): Actor = |
311 private def message_actor(): Actor = |
245 { |
312 { |
246 class EOF extends Exception |
313 class EOF extends Exception |
247 class Protocol_Error(msg: String) extends Exception(msg) |
314 class Protocol_Error(msg: String) extends Exception(msg) |
248 |
315 |
|
316 val name = "message_output" |
249 Simple_Thread.actor(name) { |
317 Simple_Thread.actor(name) { |
250 val stream = system.fifo_input_stream(fifo) // FIXME potentially blocking forever |
318 val stream = system.fifo_input_stream(out_fifo) |
251 val default_buffer = new Array[Byte](65536) |
319 val default_buffer = new Array[Byte](65536) |
252 var c = -1 |
320 var c = -1 |
253 |
321 |
254 def read_chunk(): XML.Body = |
322 def read_chunk(): XML.Body = |
255 { |
323 { |
298 case e: Protocol_Error => system_result("Malformed message:\n" + e.getMessage) |
366 case e: Protocol_Error => system_result("Malformed message:\n" + e.getMessage) |
299 case _: EOF => |
367 case _: EOF => |
300 } |
368 } |
301 } while (c != -1) |
369 } while (c != -1) |
302 stream.close |
370 stream.close |
303 close() |
|
304 |
371 |
305 system_result(name + " terminated") |
372 system_result(name + " terminated") |
306 } |
373 } |
307 } |
374 } |
308 |
|
309 |
|
310 |
|
311 /** init **/ |
|
312 |
|
313 /* exec process */ |
|
314 |
|
315 private val in_fifo = system.mk_fifo() |
|
316 private val out_fifo = system.mk_fifo() |
|
317 private def rm_fifos() { system.rm_fifo(in_fifo); system.rm_fifo(out_fifo) } |
|
318 |
|
319 try { |
|
320 val cmdline = |
|
321 List(system.getenv_strict("ISABELLE_PROCESS"), "-W", in_fifo + ":" + out_fifo) ++ args |
|
322 proc = Some(system.execute(true, cmdline: _*)) |
|
323 } |
|
324 catch { case e: IOException => rm_fifos(); throw(e) } |
|
325 |
|
326 |
|
327 /* I/O actors */ |
|
328 |
|
329 private val command_input = input_actor("command_input", in_fifo) |
|
330 message_actor("message_output", out_fifo) |
|
331 |
|
332 private val standard_input = stdin_actor("standard_input", proc.get.getOutputStream) |
|
333 stdout_actor("standard_output", proc.get.getInputStream) |
|
334 |
|
335 |
|
336 /* exit process */ |
|
337 |
|
338 Simple_Thread.actor("process_exit") { |
|
339 proc match { |
|
340 case None => |
|
341 case Some(p) => |
|
342 val rc = p.waitFor() |
|
343 Thread.sleep(300) // FIXME property!? |
|
344 system_result("Isabelle process terminated") |
|
345 put_result(Markup.EXIT, rc.toString) |
|
346 } |
|
347 rm_fifos() |
|
348 } |
|
349 |
|
350 |
375 |
351 |
376 |
352 /** main methods **/ |
377 /** main methods **/ |
353 |
378 |
354 def input_raw(text: String): Unit = standard_input ! Input_Text(text) |
379 def input_raw(text: String): Unit = standard_input ! Input_Text(text) |