src/Pure/Tools/isabelle_process.scala
changeset 29572 e3a99d957392
parent 29522 793766d4c1b5
child 29573 bb0f395db245
equal deleted inserted replaced
29571:ba0cf984e593 29572:e3a99d957392
     5 Isabelle process management -- always reactive due to multi-threaded I/O.
     5 Isabelle process management -- always reactive due to multi-threaded I/O.
     6 */
     6 */
     7 
     7 
     8 package isabelle
     8 package isabelle
     9 
     9 
    10 import java.util.Properties
       
    11 import java.util.concurrent.LinkedBlockingQueue
    10 import java.util.concurrent.LinkedBlockingQueue
    12 import java.io.{BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter,
    11 import java.io.{BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter,
    13   InputStream, OutputStream, IOException}
    12   InputStream, OutputStream, IOException}
    14 
    13 
    15 
    14 
    78       kind == SIGNAL ||
    77       kind == SIGNAL ||
    79       kind == EXIT ||
    78       kind == EXIT ||
    80       kind == STATUS
    79       kind == STATUS
    81   }
    80   }
    82 
    81 
    83   class Result(val kind: Kind.Value, val props: Properties, val result: String) {
    82   class Result(val kind: Kind.Value, val props: List[(String, String)], val result: String) {
    84     override def toString = {
    83     override def toString = {
    85       val trees = YXML.parse_body_failsafe(result)
    84       val trees = YXML.parse_body_failsafe(result)
    86       val res =
    85       val res =
    87         if (kind == Kind.STATUS) trees.map(_.toString).mkString
    86         if (kind == Kind.STATUS) trees.map(_.toString).mkString
    88         else trees.flatMap(XML.content(_).mkString).mkString
    87         else trees.flatMap(XML.content(_).mkString).mkString
    89       if (props == null) kind.toString + " [[" + res + "]]"
    88       if (props.isEmpty)
    90       else kind.toString + " " + props.toString + " [[" + res + "]]"
    89         kind.toString + " [[" + res + "]]"
       
    90       else
       
    91         kind.toString + " " +
       
    92           (for ((x, y) <- props) yield x + "=" + y).mkString("{", ",", "}") + " [[" + res + "]]"
    91     }
    93     }
    92     def is_raw = Kind.is_raw(kind)
    94     def is_raw = Kind.is_raw(kind)
    93     def is_control = Kind.is_control(kind)
    95     def is_control = Kind.is_control(kind)
    94     def is_system = Kind.is_system(kind)
    96     def is_system = Kind.is_system(kind)
    95   }
    97   }
    96 
    98 
    97   def parse_message(kind: Kind.Value, result: String) = {
    99   def parse_message(isabelle_system: IsabelleSystem, result: Result) =
    98     XML.Elem(Markup.MESSAGE, List((Markup.CLASS, Kind.markup(kind))),
   100   {
    99       YXML.parse_body_failsafe(result))
   101     XML.Elem(Markup.MESSAGE, (Markup.CLASS, Kind.markup(result.kind)) :: result.props,
       
   102       YXML.parse_body_failsafe(isabelle_system.symbols.decode(result.result)))
   100   }
   103   }
   101 }
   104 }
   102 
   105 
   103 
   106 
   104 class IsabelleProcess(isabelle_system: IsabelleSystem,
   107 class IsabelleProcess(isabelle_system: IsabelleSystem,
   117 
   120 
   118   private var proc: Process = null
   121   private var proc: Process = null
   119   private var closing = false
   122   private var closing = false
   120   private var pid: String = null
   123   private var pid: String = null
   121   private var the_session: String = null
   124   private var the_session: String = null
   122   def session() = the_session
   125   def session = the_session
   123 
   126 
   124 
   127 
   125   /* results */
   128   /* results */
   126 
   129 
       
   130   def parse_message(result: Result): XML.Tree =
       
   131     IsabelleProcess.parse_message(isabelle_system, result)
       
   132 
   127   private val result_queue = new LinkedBlockingQueue[Result]
   133   private val result_queue = new LinkedBlockingQueue[Result]
   128 
   134 
   129   private def put_result(kind: Kind.Value, props: Properties, result: String) {
   135   private def put_result(kind: Kind.Value, props: List[(String, String)], result: String)
   130     if (kind == Kind.INIT && props != null) {
   136   {
   131       pid = props.getProperty(Markup.PID)
   137     if (kind == Kind.INIT) {
   132       the_session = props.getProperty(Markup.SESSION)
   138       val map = Map(props: _*)
       
   139       if (map.isDefinedAt(Markup.PID)) pid = map(Markup.PID)
       
   140       if (map.isDefinedAt(Markup.SESSION)) the_session = map(Markup.SESSION)
   133     }
   141     }
   134     result_queue.put(new Result(kind, props, result))
   142     result_queue.put(new Result(kind, props, result))
   135   }
   143   }
   136 
   144 
   137   private class ResultThread extends Thread("isabelle: results") {
   145   private class ResultThread extends Thread("isabelle: results") {
   141         val result =
   149         val result =
   142           try { result_queue.take }
   150           try { result_queue.take }
   143           catch { case _: NullPointerException => null }
   151           catch { case _: NullPointerException => null }
   144 
   152 
   145         if (result != null) {
   153         if (result != null) {
   146           results.event(result)  // FIXME try/catch (!??)
   154           results.event(result)
   147           if (result.kind == Kind.EXIT) finished = true
   155           if (result.kind == Kind.EXIT) finished = true
   148         }
   156         }
   149         else finished = true
   157         else finished = true
   150       }
   158       }
   151     }
   159     }
   154 
   162 
   155   /* signals */
   163   /* signals */
   156 
   164 
   157   def interrupt() = synchronized {
   165   def interrupt() = synchronized {
   158     if (proc == null) error("Cannot interrupt Isabelle: no process")
   166     if (proc == null) error("Cannot interrupt Isabelle: no process")
   159     if (pid == null) put_result(Kind.SYSTEM, null, "Cannot interrupt: unknown pid")
   167     if (pid == null) put_result(Kind.SYSTEM, Nil, "Cannot interrupt: unknown pid")
   160     else {
   168     else {
   161       try {
   169       try {
   162         if (isabelle_system.execute(true, "kill", "-INT", pid).waitFor == 0)
   170         if (isabelle_system.execute(true, "kill", "-INT", pid).waitFor == 0)
   163           put_result(Kind.SIGNAL, null, "INT")
   171           put_result(Kind.SIGNAL, Nil, "INT")
   164         else
   172         else
   165           put_result(Kind.SYSTEM, null, "Cannot interrupt: kill command failed")
   173           put_result(Kind.SYSTEM, Nil, "Cannot interrupt: kill command failed")
   166       }
   174       }
   167       catch { case e: IOException => error("Cannot interrupt Isabelle: " + e.getMessage) }
   175       catch { case e: IOException => error("Cannot interrupt Isabelle: " + e.getMessage) }
   168     }
   176     }
   169   }
   177   }
   170 
   178 
   171   def kill() = synchronized {
   179   def kill() = synchronized {
   172     if (proc == 0) error("Cannot kill Isabelle: no process")
   180     if (proc == 0) error("Cannot kill Isabelle: no process")
   173     else {
   181     else {
   174       try_close()
   182       try_close()
   175       Thread.sleep(500)
   183       Thread.sleep(500)
   176       put_result(Kind.SIGNAL, null, "KILL")
   184       put_result(Kind.SIGNAL, Nil, "KILL")
   177       proc.destroy
   185       proc.destroy
   178       proc = null
   186       proc = null
   179       pid = null
   187       pid = null
   180     }
   188     }
   181   }
   189   }
   196 
   204 
   197 
   205 
   198   def command(text: String) =
   206   def command(text: String) =
   199     output_sync("Isabelle.command " + IsabelleSyntax.encode_string(text))
   207     output_sync("Isabelle.command " + IsabelleSyntax.encode_string(text))
   200 
   208 
   201   def command(props: Properties, text: String) =
   209   def command(props: List[(String, String)], text: String) =
   202     output_sync("Isabelle.command " + IsabelleSyntax.encode_properties(props) + " " +
   210     output_sync("Isabelle.command " + IsabelleSyntax.encode_properties(props) + " " +
   203       IsabelleSyntax.encode_string(text))
   211       IsabelleSyntax.encode_string(text))
   204 
   212 
   205   def ML(text: String) =
   213   def ML(text: String) =
   206     output_sync("ML_val " + IsabelleSyntax.encode_string(text))
   214     output_sync("ML_val " + IsabelleSyntax.encode_string(text))
   231           if (s == "\u0000") {
   239           if (s == "\u0000") {
   232             writer.close
   240             writer.close
   233             finished = true
   241             finished = true
   234           }
   242           }
   235           else {
   243           else {
   236             put_result(Kind.STDIN, null, s)
   244             put_result(Kind.STDIN, Nil, s)
   237             writer.write(s)
   245             writer.write(s)
   238             writer.flush
   246             writer.flush
   239           }
   247           }
   240           //}}}
   248           //}}}
   241         }
   249         }
   242         catch {
   250         catch {
   243           case e: IOException => put_result(Kind.SYSTEM, null, "Stdin thread: " + e.getMessage)
   251           case e: IOException => put_result(Kind.SYSTEM, Nil, "Stdin thread: " + e.getMessage)
   244         }
   252         }
   245       }
   253       }
   246       put_result(Kind.SYSTEM, null, "Stdin thread terminated")
   254       put_result(Kind.SYSTEM, Nil, "Stdin thread terminated")
   247     }
   255     }
   248   }
   256   }
   249 
   257 
   250 
   258 
   251   /* stdout */
   259   /* stdout */
   265             c = reader.read
   273             c = reader.read
   266             if (c >= 0) result.append(c.asInstanceOf[Char])
   274             if (c >= 0) result.append(c.asInstanceOf[Char])
   267             else done = true
   275             else done = true
   268           }
   276           }
   269           if (result.length > 0) {
   277           if (result.length > 0) {
   270             put_result(Kind.STDOUT, null, result.toString)
   278             put_result(Kind.STDOUT, Nil, result.toString)
   271             result.length = 0
   279             result.length = 0
   272           }
   280           }
   273           else {
   281           else {
   274             reader.close
   282             reader.close
   275             finished = true
   283             finished = true
   276             try_close()
   284             try_close()
   277           }
   285           }
   278           //}}}
   286           //}}}
   279         }
   287         }
   280         catch {
   288         catch {
   281           case e: IOException => put_result(Kind.SYSTEM, null, "Stdout thread: " + e.getMessage)
   289           case e: IOException => put_result(Kind.SYSTEM, Nil, "Stdout thread: " + e.getMessage)
   282         }
   290         }
   283       }
   291       }
   284       put_result(Kind.SYSTEM, null, "Stdout thread terminated")
   292       put_result(Kind.SYSTEM, Nil, "Stdout thread terminated")
   285     }
   293     }
   286   }
   294   }
   287 
   295 
   288 
   296 
   289   /* messages */
   297   /* messages */
   290 
   298 
   291   private class MessageThread(fifo: String) extends Thread("isabelle: messages") {
   299   private class MessageThread(fifo: String) extends Thread("isabelle: messages") {
   292     override def run() = {
   300     override def run() = {
   293       val reader = isabelle_system.fifo_reader(fifo)
   301       val reader = isabelle_system.fifo_reader(fifo)
   294       var kind: Kind.Value = null
   302       var kind: Kind.Value = null
   295       var props: Properties = null
   303       var props: List[(String, String)] = Nil
   296       var result = new StringBuilder
   304       var result = new StringBuilder
   297 
   305 
   298       var finished = false
   306       var finished = false
   299       while (!finished) {
   307       while (!finished) {
   300         try {
   308         try {
   305               c = reader.read
   313               c = reader.read
   306               if (c >= 0 && c != 2) result.append(c.asInstanceOf[Char])
   314               if (c >= 0 && c != 2) result.append(c.asInstanceOf[Char])
   307             } while (c >= 0 && c != 2)
   315             } while (c >= 0 && c != 2)
   308 
   316 
   309             if (result.length > 0) {
   317             if (result.length > 0) {
   310               put_result(Kind.SYSTEM, null, "Malformed message:\n" + result.toString)
   318               put_result(Kind.SYSTEM, Nil, "Malformed message:\n" + result.toString)
   311               result.length = 0
   319               result.length = 0
   312             }
   320             }
   313             if (c < 0) {
   321             if (c < 0) {
   314               reader.close
   322               reader.close
   315               finished = true
   323               finished = true
   317             }
   325             }
   318             else {
   326             else {
   319               c = reader.read
   327               c = reader.read
   320               if (Kind.code.isDefinedAt(c)) kind = Kind.code(c)
   328               if (Kind.code.isDefinedAt(c)) kind = Kind.code(c)
   321               else kind = null
   329               else kind = null
   322               props = null
   330               props = Nil
   323             }
   331             }
   324             //}}}
   332             //}}}
   325           }
   333           }
   326           else {
   334           else {
   327             //{{{ Line mode
   335             //{{{ Line mode
   337               if (line.endsWith("\u0002,")) {
   345               if (line.endsWith("\u0002,")) {
   338                 val i = line.indexOf('=')
   346                 val i = line.indexOf('=')
   339                 if (i > 0) {
   347                 if (i > 0) {
   340                   val name = line.substring(0, i)
   348                   val name = line.substring(0, i)
   341                   val value = line.substring(i + 1, len - 2)
   349                   val value = line.substring(i + 1, len - 2)
   342                   if (props == null) props = new Properties
   350                   props = (name, value) :: props
   343                   if (!props.containsKey(name)) props.setProperty(name, value)
       
   344                 }
   351                 }
   345               }
   352               }
   346               // last text line
   353               // last text line
   347               else if (line.endsWith("\u0002.")) {
   354               else if (line.endsWith("\u0002.")) {
   348                 result.append(line.substring(0, len - 2))
   355                 result.append(line.substring(0, len - 2))
   349                 put_result(kind, props, result.toString)
   356                 put_result(kind, props.reverse, result.toString)
   350                 result.length = 0
   357                 result.length = 0
   351                 kind = null
   358                 kind = null
   352               }
   359               }
   353               // text line
   360               // text line
   354               else {
   361               else {
   358             }
   365             }
   359             //}}}
   366             //}}}
   360           }
   367           }
   361         }
   368         }
   362         catch {
   369         catch {
   363           case e: IOException => put_result(Kind.SYSTEM, null, "Message thread: " + e.getMessage)
   370           case e: IOException => put_result(Kind.SYSTEM, Nil, "Message thread: " + e.getMessage)
   364         }
   371         }
   365       }
   372       }
   366       put_result(Kind.SYSTEM, null, "Message thread terminated")
   373       put_result(Kind.SYSTEM, Nil, "Message thread terminated")
   367     }
   374     }
   368   }
   375   }
   369 
   376 
   370 
   377 
   371 
   378 
   375     /* isabelle version */
   382     /* isabelle version */
   376 
   383 
   377     {
   384     {
   378       val (msg, rc) = isabelle_system.isabelle_tool("version")
   385       val (msg, rc) = isabelle_system.isabelle_tool("version")
   379       if (rc != 0) error("Version check failed -- bad Isabelle installation:\n" + msg)
   386       if (rc != 0) error("Version check failed -- bad Isabelle installation:\n" + msg)
   380       put_result(Kind.SYSTEM, null, msg)
   387       put_result(Kind.SYSTEM, Nil, msg)
   381     }
   388     }
   382 
   389 
   383 
   390 
   384     /* messages */
   391     /* messages */
   385 
   392 
   416 
   423 
   417     new Thread("isabelle: exit") {
   424     new Thread("isabelle: exit") {
   418       override def run() = {
   425       override def run() = {
   419         val rc = proc.waitFor()
   426         val rc = proc.waitFor()
   420         Thread.sleep(300)
   427         Thread.sleep(300)
   421         put_result(Kind.SYSTEM, null, "Exit thread terminated")
   428         put_result(Kind.SYSTEM, Nil, "Exit thread terminated")
   422         put_result(Kind.EXIT, null, Integer.toString(rc))
   429         put_result(Kind.EXIT, Nil, Integer.toString(rc))
   423         rm_fifo()
   430         rm_fifo()
   424       }
   431       }
   425     }.start
   432     }.start
   426 
   433 
   427   }
   434   }