mirror of https://github.com/sbt/sbt.git
Shutdown background job on error
When running a main method, if the user inputs ctrl+c then the `run` task will exit but the main method is not interrupted so it continues running even once sbt has returned to the shell. If the main method is a webserver, this prevents run from ever starting again on a fixed port. To fix this, we can modify the waitForTry method to stop the job if an exception is thrown (ctrl+c leads to an interrupted exception being thrown by waitFor). I rework the BackgroundJobService so that the default implementation of waitForTry is now usable and no longer needs to be overridden. The side effect of this change is that waitFor may now throw an exception. Within sbt, waitFor was only used in one place and I reworked it to use waitForTry instead. This could theoretically break a downstream user who relied on waitFor not throwing an exception but I suspect that there aren't many users of this api, if any at all.
This commit is contained in:
parent
181bfe8a46
commit
8d26bc73b4
|
|
@ -8,11 +8,14 @@
|
|||
package sbt
|
||||
|
||||
import java.io.Closeable
|
||||
|
||||
import sbt.util.Logger
|
||||
import Def.{ ScopedKey, Classpath }
|
||||
import Def.{ Classpath, ScopedKey }
|
||||
import sbt.internal.util.complete._
|
||||
import java.io.File
|
||||
import scala.util.Try
|
||||
|
||||
import scala.util.control.NonFatal
|
||||
import scala.util.{ Failure, Success, Try }
|
||||
|
||||
abstract class BackgroundJobService extends Closeable {
|
||||
|
||||
|
|
@ -49,9 +52,19 @@ abstract class BackgroundJobService extends Closeable {
|
|||
def jobs: Vector[JobHandle]
|
||||
def stop(job: JobHandle): Unit
|
||||
|
||||
/**
|
||||
* Delegate to waitFor but catches any exceptions and returns the result in an instance of `Try`.
|
||||
* @param job the job to wait for
|
||||
* @return the result of waiting for the job to complete.
|
||||
*/
|
||||
def waitForTry(job: JobHandle): Try[Unit] = {
|
||||
// This implementation is provided only for backward compatibility.
|
||||
Try(waitFor(job))
|
||||
try Success(waitFor(job))
|
||||
catch {
|
||||
case NonFatal(e) =>
|
||||
try stop(job)
|
||||
catch { case NonFatal(_) => }
|
||||
Failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
def waitFor(job: JobHandle): Unit
|
||||
|
|
|
|||
|
|
@ -1599,7 +1599,8 @@ object Defaults extends BuildCommon {
|
|||
}
|
||||
|
||||
def bgWaitForTask: Initialize[InputTask[Unit]] = foreachJobTask { (manager, handle) =>
|
||||
manager.waitFor(handle)
|
||||
manager.waitForTry(handle)
|
||||
()
|
||||
}
|
||||
|
||||
def docTaskSettings(key: TaskKey[File] = doc): Seq[Setting[_]] =
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
|
|||
jobSet.headOption.foreach {
|
||||
case handle: ThreadJobHandle @unchecked =>
|
||||
handle.job.shutdown()
|
||||
handle.job.awaitTermination()
|
||||
handle.job.awaitTerminationTry()
|
||||
case _ => //
|
||||
}
|
||||
}
|
||||
|
|
@ -178,24 +178,9 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
|
|||
)
|
||||
}
|
||||
|
||||
private def withHandleTry(job: JobHandle)(f: ThreadJobHandle => Try[Unit]): Try[Unit] =
|
||||
job match {
|
||||
case handle: ThreadJobHandle @unchecked => f(handle)
|
||||
case _: DeadHandle @unchecked => Try(()) // nothing to stop or wait for
|
||||
case other =>
|
||||
Try(
|
||||
sys.error(
|
||||
s"BackgroundJobHandle does not originate with the current BackgroundJobService: $other"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def stop(job: JobHandle): Unit =
|
||||
withHandle(job)(_.job.shutdown())
|
||||
|
||||
override def waitForTry(job: JobHandle): Try[Unit] =
|
||||
withHandleTry(job)(_.job.awaitTerminationTry())
|
||||
|
||||
override def waitFor(job: JobHandle): Unit =
|
||||
withHandle(job)(_.job.awaitTermination())
|
||||
|
||||
|
|
@ -398,11 +383,9 @@ private[sbt] class BackgroundThreadPool extends java.io.Closeable {
|
|||
stopListeners += result
|
||||
result
|
||||
}
|
||||
override def awaitTermination(): Unit = finishedLatch.await()
|
||||
|
||||
override def awaitTerminationTry(): Try[Unit] = {
|
||||
awaitTermination()
|
||||
exitTry.getOrElse(Try(()))
|
||||
override def awaitTermination(): Unit = {
|
||||
finishedLatch.await()
|
||||
exitTry.foreach(_.fold(e => throw e, identity))
|
||||
}
|
||||
|
||||
override def humanReadableName: String = taskName
|
||||
|
|
|
|||
Loading…
Reference in New Issue