summaryrefslogtreecommitdiff
path: root/src/library/scala/sys
diff options
context:
space:
mode:
authorSom Snytt <som.snytt@gmail.com>2016-10-26 11:45:55 -0700
committerSom Snytt <som.snytt@gmail.com>2016-11-17 07:49:03 -0800
commitfbcfba212fff76272c509c6781ea2a2897d84bff (patch)
tree2ff682f1233400ad9626294c1f6ca6e0d17666fb /src/library/scala/sys
parent51e77408160fcef8b8c0669e1c62ec9a36f3b606 (diff)
downloadscala-fbcfba212fff76272c509c6781ea2a2897d84bff.tar.gz
scala-fbcfba212fff76272c509c6781ea2a2897d84bff.tar.bz2
scala-fbcfba212fff76272c509c6781ea2a2897d84bff.zip
SI-10007 sys.process thread sync
A previous change to replace `SyncVar.set` with `SyncVar.put` breaks things. This commit tweaks the thread synchronizing in `sys.process` to actually use `SyncVar` to sync and pass a var. Joining the thread about to exit is superfluous. A result is put exactly once, and consumers use non-destructive `get`. Note that as usual, avoid kicking off threads in a static context, since class loading cycles are somewhat dicier with 2.12 lambdas. In particular, REPL is a static context by default. SI-10007 Clarify deprecation message The message on `set` was self-fulfilling, as it didn't hint that `put` has different semantics. So explain why `put` helps avoid errors instead of creating them. SI-10007 Always set exit value Always put a value to exit code, defaulting to None. Also clean up around tuple change to unfortunately named Future.apply. Very hard to follow those types. Date command pollutes output, so tweak test.
Diffstat (limited to 'src/library/scala/sys')
-rw-r--r--src/library/scala/sys/process/ProcessBuilderImpl.scala14
-rw-r--r--src/library/scala/sys/process/ProcessImpl.scala34
2 files changed, 26 insertions, 22 deletions
diff --git a/src/library/scala/sys/process/ProcessBuilderImpl.scala b/src/library/scala/sys/process/ProcessBuilderImpl.scala
index eef140c16a..0df2e648e0 100644
--- a/src/library/scala/sys/process/ProcessBuilderImpl.scala
+++ b/src/library/scala/sys/process/ProcessBuilderImpl.scala
@@ -53,12 +53,14 @@ private[process] trait ProcessBuilderImpl {
override def run(io: ProcessIO): Process = {
val success = new SyncVar[Boolean]
- success put false
- val t = Spawn({
- runImpl(io)
- success.put(true)
- }, io.daemonizeThreads)
-
+ def go(): Unit = {
+ var ok = false
+ try {
+ runImpl(io)
+ ok = true
+ } finally success.put(ok)
+ }
+ val t = Spawn(go(), io.daemonizeThreads)
new ThreadProcess(t, success)
}
}
diff --git a/src/library/scala/sys/process/ProcessImpl.scala b/src/library/scala/sys/process/ProcessImpl.scala
index 6da0dee056..8a0002b316 100644
--- a/src/library/scala/sys/process/ProcessImpl.scala
+++ b/src/library/scala/sys/process/ProcessImpl.scala
@@ -86,17 +86,20 @@ private[process] trait ProcessImpl {
private[process] abstract class CompoundProcess extends BasicProcess {
def isAlive() = processThread.isAlive()
def destroy() = destroyer()
- def exitValue() = getExitValue._2() getOrElse scala.sys.error("No exit code: process destroyed.")
- def start() = getExitValue
+ def exitValue() = futureValue() getOrElse scala.sys.error("No exit code: process destroyed.")
+ def start() = { futureThread ;() }
- protected lazy val (processThread, getExitValue, destroyer) = {
+ protected lazy val (processThread, (futureThread, futureValue), destroyer) = {
val code = new SyncVar[Option[Int]]()
- code.put(None)
- val thread = Spawn(code.put(runAndExitValue()))
+ val thread = Spawn {
+ var value: Option[Int] = None
+ try value = runAndExitValue()
+ finally code.put(value)
+ }
(
thread,
- Future { thread.join(); code.get },
+ Future(code.get), // thread.join()
() => thread.interrupt()
)
}
@@ -215,13 +218,15 @@ private[process] trait ProcessImpl {
}
/** A thin wrapper around a java.lang.Process. `ioThreads` are the Threads created to do I/O.
- * The implementation of `exitValue` waits until these threads die before returning. */
+ * The implementation of `exitValue` waits until these threads die before returning.
+ */
private[process] class DummyProcess(action: => Int) extends Process {
- private[this] val exitCode = Future(action)
- override def isAlive() = exitCode._1.isAlive()
- override def exitValue() = exitCode._2()
+ private[this] val (thread, value) = Future(action)
+ override def isAlive() = thread.isAlive()
+ override def exitValue() = value()
override def destroy() { }
}
+
/** A thin wrapper around a java.lang.Process. `outputThreads` are the Threads created to read from the
* output and error streams of the process. `inputThread` is the Thread created to write to the input stream of
* the process.
@@ -245,11 +250,8 @@ private[process] trait ProcessImpl {
}
}
private[process] final class ThreadProcess(thread: Thread, success: SyncVar[Boolean]) extends Process {
- override def isAlive() = thread.isAlive()
- override def exitValue() = {
- thread.join()
- if (success.get) 0 else 1
- }
- override def destroy() { thread.interrupt() }
+ override def isAlive() = thread.isAlive()
+ override def exitValue() = if (success.get) 0 else 1 // thread.join()
+ override def destroy() = thread.interrupt()
}
}