src/Pure/System/isabelle_process.scala
changeset 39528 c01d89d18ff0
parent 39526 f1296795a8dc
child 39530 16adc476348f
equal deleted inserted replaced
39527:f03a9c57760a 39528:c01d89d18ff0
    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)
   357     command_input ! Input_Chunks(Standard_System.string_bytes(name) :: args.toList)
   382     command_input ! Input_Chunks(Standard_System.string_bytes(name) :: args.toList)
   358 
   383 
   359   def input(name: String, args: String*): Unit =
   384   def input(name: String, args: String*): Unit =
   360     input_bytes(name, args.map(Standard_System.string_bytes): _*)
   385     input_bytes(name, args.map(Standard_System.string_bytes): _*)
   361 
   386 
   362   def close(): Unit = { standard_input ! Close; command_input ! Close }
   387   def close(): Unit = { close(command_input); close(standard_input) }
   363 }
   388 }