@@ -13,6 +13,8 @@ import scala.concurrent.{ Await, Future }
13
13
import scala .concurrent .ExecutionContext .Implicits .global
14
14
import scala .collection .mutable
15
15
16
+ import vulpix .Statuses ._
17
+
16
18
trait RunnerOrchestration {
17
19
18
20
/** The maximum amount of active runners, which contain a child JVM */
@@ -36,14 +38,30 @@ trait RunnerOrchestration {
36
38
def runMain (dir : JFile ): Status = withRunner(_.runMain(dir))
37
39
38
40
private class Runner (private var process : Process ) {
39
- private [this ] val ois = new ObjectInputStream (process.getInputStream)
40
- private [this ] val oos = new ObjectOutputStream (process.getOutputStream)
41
-
41
+ private [this ] var ois : ObjectInputStream = _
42
+ private [this ] var oos : ObjectOutputStream = _
43
+
44
+ /** Checks if `process` is still alive
45
+ *
46
+ * When `process.exitValue()` is called on an active process the caught
47
+ * exception is thrown. As such we can know if the subprocess exited or
48
+ * not.
49
+ *
50
+ * @note used for debug
51
+ */
52
+ def isAlive : Boolean =
53
+ try { process.exitValue(); false }
54
+ catch { case _ : IllegalThreadStateException => true }
55
+
56
+ /** Destroys the underlying process and kills IO streams */
42
57
def kill (): Unit = {
43
58
if (process ne null ) process.destroy()
44
59
process = null
60
+ ois = null
61
+ oos = null
45
62
}
46
63
64
+ /** Blocks less than `maxDuration` while running `Test.main` from `dir` */
47
65
def runMain (dir : JFile ): Status = {
48
66
assert(process ne null ,
49
67
" Runner was killed and then reused without setting a new process" )
@@ -52,37 +70,51 @@ trait RunnerOrchestration {
52
70
def respawn (): Unit = {
53
71
process.destroy()
54
72
process = createProcess
73
+ ois = null
74
+ oos = null
55
75
}
56
76
77
+ if (oos eq null ) oos = new ObjectOutputStream (process.getOutputStream)
78
+
57
79
// pass file to running process
58
80
oos.writeObject(dir)
81
+ oos.flush()
59
82
60
83
// Create a future reading the object:
61
- val readObject = Future (ois.readObject().asInstanceOf [Status ])
84
+ val readObject = Future {
85
+ if (ois eq null ) ois = new ObjectInputStream (process.getInputStream)
86
+ ois.readObject().asInstanceOf [Status ]
87
+ }
62
88
63
89
// Await result for `maxDuration` and then timout and destroy the
64
90
// process:
65
91
val status =
66
92
try Await .result(readObject, maxDuration)
67
- catch { case _ : TimeoutException => { Timeout } }
93
+ catch { case _ : TimeoutException => new Timeout () }
68
94
69
95
// Handle failure of the VM:
70
96
status match {
71
97
case _ if safeMode => respawn()
72
- case status : Failure => respawn()
73
- case Timeout => respawn()
98
+ case _ : Failure => respawn()
99
+ case _ : Timeout => respawn()
74
100
case _ => ()
75
101
}
76
-
77
- // return run status:
78
102
status
79
103
}
80
104
}
81
105
82
- private def createProcess : Process = ???
106
+ private def createProcess : Process = {
107
+ val sep = sys.props(" file.separator" )
108
+ val cp = sys.props(" java.class.path" )
109
+ val java = sys.props(" java.home" ) + sep + " bin" + sep + " java"
110
+ new ProcessBuilder (java, " -cp" , cp, " dotty.tools.dotc.vulpix.ChildMain" )// classOf[ChildMain].getName)
111
+ .redirectErrorStream(true )
112
+ .redirectInput(ProcessBuilder .Redirect .PIPE )
113
+ .redirectOutput(ProcessBuilder .Redirect .PIPE )
114
+ .start()
115
+ }
83
116
84
- private [this ] val allRunners =
85
- List .fill(numberOfSlaves)(new Runner (createProcess))
117
+ private [this ] val allRunners = List .fill(numberOfSlaves)(new Runner (createProcess))
86
118
87
119
private [this ] val freeRunners = mutable.Queue (allRunners : _* )
88
120
private [this ] val busyRunners = mutable.Set .empty[Runner ]
0 commit comments