task execution interruptible using ctrl+c. fixes #228,#229

- interrupts task execution only
- no further tasks scheduled
- existing tasks interrupted
- a task must terminate any other started threads when interrupted
- set cancelable to true to enable
- currently, 'run' properly terminates if the application properly
   terminates when interrupted
- 'console' does not, 'test' depends on the test framework

- also bundled: set connectInput to true to connect standard input to forked run
This commit is contained in:
Mark Harrah 2011-10-18 22:43:25 -04:00
parent 5898cba4a8
commit 64bf50cd08
3 changed files with 49 additions and 4 deletions

View File

@ -0,0 +1,43 @@
package sbt
object Signals
{
def withHandler[T](handler: () => Unit)(action: () => T): T =
{
val result =
try
{
val signals = new Signals0
signals.withHandler(handler)(action)
}
catch { case e: LinkageError => Right(action()) }
result match {
case Left(e) => throw e
case Right(v) => v
}
}
}
// Must only be referenced using a
// try { } catch { case e: LinkageError => ... }
// block to
private final class Signals0
{
// returns a LinkageError in `action` as Left(t) in order to avoid it being
// incorrectly swallowed as missing Signal/SignalHandler
def withHandler[T](handler: () => Unit)(action: () => T): Either[Throwable, T] =
{
import sun.misc.{Signal,SignalHandler}
val intSignal = new Signal("INT")
val newHandler = new SignalHandler {
def handle(sig: Signal) { handler() }
}
val oldHandler = Signal.handle(intSignal, newHandler)
try Right(action())
catch { case e: LinkageError => Left(e) }
finally Signal.handle(intSignal, oldHandler)
}
}

View File

@ -166,6 +166,8 @@ trait ProcessBuilder extends SourcePartialBuilder with SinkPartialBuilder
* The newly started process reads from standard input of the current process if `connectInput` is true.*/
def run(log: ProcessLogger, connectInput: Boolean): Process
def runBuffered(log: ProcessLogger, connectInput: Boolean): Process
/** Constructs a command that runs this command first and then `other` if this command succeeds.*/
def #&& (other: ProcessBuilder): ProcessBuilder
/** Constructs a command that runs this command first and then `other` if this command does not succeed.*/

View File

@ -159,10 +159,10 @@ private abstract class AbstractProcessBuilder extends ProcessBuilder with SinkPa
def ! = run(false).exitValue()
def !< = run(true).exitValue()
def !(log: ProcessLogger) = runBuffered(log, false)
def !<(log: ProcessLogger) = runBuffered(log, true)
private[this] def runBuffered(log: ProcessLogger, connectInput: Boolean) =
log.buffer { run(log, connectInput).exitValue() }
def !(log: ProcessLogger) = runBuffered(log, false).exitValue()
def !<(log: ProcessLogger) = runBuffered(log, true).exitValue()
def runBuffered(log: ProcessLogger, connectInput: Boolean) =
log.buffer { run(log, connectInput) }
def !(io: ProcessIO) = run(io).exitValue()
def canPipeTo = false