mirror of https://github.com/sbt/sbt.git
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:
parent
5898cba4a8
commit
64bf50cd08
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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.*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue