InterruptibleProcess.scala 2.15 KB
Newer Older
1
package de.tu_darmstadt.cs.esa.tapasco.util
2
3
4
import  scala.sys.process._
import  scala.collection.mutable.ArrayBuffer

5
6
private[tapasco] final case class InterruptibleProcess(p: ProcessBuilder, waitMillis: Option[Int] = None) {
  private final val logger = de.tu_darmstadt.cs.esa.tapasco.Logging.logger(getClass)
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  private var result: Option[Int]         = None
  private val output: ArrayBuffer[String] = ArrayBuffer()
  private val errors: ArrayBuffer[String] = ArrayBuffer()
  private val plog: ProcessLogger         = ProcessLogger(output += _, errors += _)

  private def mkThread(plogger: ProcessLogger) = new Thread(new Runnable {
    private var proc: Option[Process] = None

    def run() {
      try {
        proc = Some(p.run(plogger))
        result = proc map (_.exitValue())
      } catch { case e: InterruptedException =>
        logger.warn("thread interrupted, destroying external process")
        proc foreach { _.destroy() }
      }
    }
  })

  private def mkThread(pio: ProcessIO) = new Thread(new Runnable {
    private var proc: Option[Process] = None

    def run() {
      try {
        proc = Some(p.run(pio))
        result = proc map (_.exitValue())
      } catch { case e: InterruptedException =>
        logger.warn("thread interrupted, destroying external process")
        proc foreach { _.destroy() }
      }
    }
  })

  def !(plogger: ProcessLogger = plog): Int = {
    val t = mkThread(plog)
    t.start()
    if (waitMillis.isEmpty) t.join() else t.join(waitMillis.get)
    if (t.isAlive()) t.interrupt()
    result getOrElse InterruptibleProcess.TIMEOUT_RETCODE
  }

  def !(pio: ProcessIO): Int = {
    val t = mkThread(pio)
    t.start()
    if (waitMillis.isEmpty) t.join() else t.join(waitMillis.get)
    if (t.isAlive()) t.interrupt()
    result getOrElse InterruptibleProcess.TIMEOUT_RETCODE
  }

  def !!(): String = {
    this.!()
    output mkString scala.util.Properties.lineSeparator
  }
}

object InterruptibleProcess {
  final val TIMEOUT_RETCODE = 124 // matches 'timeout' command
64
65
66
67
68
69
  // custom ProcessIO: ignore everything
  val io = new ProcessIO(
    stdin => {stdin.close()},
    stdout => {stdout.close()},
    stderr => {stderr.close()}
  )
70
}