Scala - получение обратного вызова при выходе внешнего процесса

Я хочу заменить много своего perl scala. Одна из вещей, которые я обычно делаю, - это двоичные файлы вызовов (обычно скомпилированные С++, но могут быть java, другие скрипты perl, q скрипты и т.д.), Предоставленные мне другими группами в моей компании.

Например, чтобы выполнить некоторую сложную математику, я бы начал использовать один из чужих двоичных файлов, а затем подключил к нему свои входы. Затем я слушал поток stdout для результатов и поток stderr для диагностических сообщений. В perl я сделал бы это с помощью виджета POE::Wheel::Run. Я придумал что-то аналогичное (и намного приятнее) в scala, но я хотел бы сделать его более надежным. Это небольшая оболочка вокруг объекта ProcessIO. Это выглядит так:

class Exe(command: String, out: String => Unit, err: String => Unit) {

    import scala.sys.process._
    import scala.io._
    import java.io._
    import scala.concurrent._

    val inputStream = new SyncVar[OutputStream];

    val process = Process(command).run(
        new ProcessIO(
            stdin => inputStream.put(stdin),
            stdout => Source.fromInputStream(stdout).getLines.foreach(out),
            stderr => Source.fromInputStream(stderr).getLines.foreach(err)));

    def write(s: String): Unit = synchronized {
        inputStream.get.write((s + "\n").getBytes)
    }

    def close(): Unit = {
        inputStream.get.close
    }
}

Я бы использовал его следующим образом:

val exe = new Exe("tr [a-z] [A-Z]", 
                  out => println("o: " + out), 
                  err => println("e: " + err))
exe.write("lower")
exe.close()

Что печатает:

o: LOWER

Это дает мне 90%, но было бы неплохо получить обратный вызов, когда процесс завершится. Он может выйти из-за того, что я закрыл входной поток и его внутренний цикл остановился, он может выйти сам по себе, или он может выйти, потому что я его убил. В обратном вызове было бы полезно узнать, почему он остановился, и код выхода.

Я немного похудел на то, как это сделать, любая помощь будет оценена (и любые изменения в этом коде, конечно, приветствуются - я немного нуб).

Я использую 2.9.0.1

Ответ 1

Вы можете дождаться окончания процесса, вызывающего exitValue. Вы можете сделать это в отдельном потоке, в котором будет выполняться обратный вызов. Возможно, класс Process может быть сжат таким образом:

import scala.concurrent.ops.spawn
implicit def ProcessWithCallback(p: Process) {
  def whenTerminatedDo(callback: Int => Unit) = spawn{
    val exitValue = p.exitValue; callback(p)
  }
}

Затем вы можете использовать это в Exe по своему усмотрению.

Класс Process, заданный JVM и обернутый scala.sys.Process, действительно весьма эффективен, будет трудно не блокировать поток

Ответ 2

с помощью spawn, чтобы создать новый поток, который блокирует и ждет код выхода

class Exe(command:String, out:String=>Unit, err:String=>Unit, onExit:Int=>Unit) {

    import scala.sys.process._
    import scala.io._
    import java.io._
    import scala.concurrent._
    import scala.concurrent.ops.spawn

    val inputStream = new SyncVar[OutputStream];

    val process = Process(command).run(
        new ProcessIO(
            stdin => inputStream.put(stdin),
            stdout => Source.fromInputStream(stdout).getLines.foreach(out),
            stderr => Source.fromInputStream(stderr).getLines.foreach(err)));

    spawn { onExit(process.exitValue()) }

    def write(s:String):Unit = synchronized {
        inputStream.get.write((s + "\n").getBytes)
    }

    def close():Unit = {
        inputStream.get.close
    }
}

может использовать это как

import java.util.concurrent.CountDownLatch

val latch = new CountDownLatch(1)

val exe = new Exe("tr [a-z] [A-Z]",
        out => println("o: " + out), 
        err => println("e: " + err), 
        code=> {println(code) ; latch.countDown() })
exe.write("lower") 
exe.close()

latch.await

печатает

o: LOWER
0

Спасибо всем!

Ответ 3

Рассматривали ли вы нересту новый поток, который затем вызывает метод блокировки process.exitValue()? Затем вы можете вызвать свой обратный вызов.