+ val f = d/b.result; b.clear();
+ try { f.mkfile_!(); exit(f); }
+ catch { case SystemError(EEXIST, _) => ok; }
+ }
+}
+
+/*----- Running a command -------------------------------------------------*/
+
+private val devnull = new File("/dev/null");
+
+private def captureStream(in: InputStream, out: StringBuilder) {
+ /* Capture the INSTREAM's contents in a string. */
+
+ for ((buf, n) <- blocks(new InputStreamReader(in)))
+ out.appendAll(buf, 0, n);
+}
+
+class SubprocessFailed(val cmd: Seq[String], rc: Int, stderr: String)
+ extends Exception {
+ override def getMessage(): String =
+ s"process (${quoteTokens(cmd)}) failed (rc = $rc):\n" + stderr
+}
+
+def runCommand(cmd: String*): (String, String) = {
+ /* Run a command, returning its stdout and stderr. */
+
+ withCleaner { clean =>
+
+ /* Create the child process and pick up the ends of its streams. */
+ val pb = new ProcessBuilder(cmd.asJava).redirectInput(devnull);
+ val kid = pb.start(); clean { kid.destroy(); }
+ val out = kid.getInputStream(); clean { out.close(); }
+ val err = kid.getErrorStream(); clean { err.close(); }
+
+ /* Capture the output in threads, so we don't block. Also, wait for the
+ * child to complete. Amazingly, messing with threads here isn't too
+ * much of a disaster.
+ */
+ val bout, berr = new StringBuilder;
+ val rdout = thread("capture process stdout", daemon = false) {
+ captureStream(out, bout);
+ }
+ val rderr = thread("capture process stderr", daemon = false) {
+ captureStream(err, berr);
+ }
+ val wait = thread("await process exit", daemon = false) {
+ kid.waitFor();
+ }
+ rdout.join(); rderr.join(); wait.join();
+
+ /* Check the exit status. */
+ val rc = kid.exitValue;
+ if (rc != 0) throw new SubprocessFailed(cmd, rc, berr.result);
+
+ /* We're all done. */
+ return (bout.result, berr.result);
+ }
+}
+
+/*----- Interrupt triggers ------------------------------------------------*/
+
+private val triggerLock = new Object;
+private final val maxTriggers = 2;
+private var nTriggers = 0;
+private var triggers: List[Wrapper] = Nil;
+
+@native protected def makeTrigger(): Wrapper;
+@native protected def destroyTrigger(trig: Wrapper);
+@native protected def resetTrigger(trig: Wrapper);
+@native protected def trigger(trig: Wrapper);
+
+private def getTrigger(): Wrapper = {
+ triggerLock synchronized {
+ if (nTriggers == 0)
+ makeTrigger()
+ else {
+ val trig = triggers.head;
+ triggers = triggers.tail;
+ nTriggers -= 1;
+ trig
+ }