mirror of https://github.com/sbt/sbt.git
Avoid leaking sbt processes
On windows, it is sometimes possible to leak an sbt process if two processes are started simultaneously by a remote client at the same time. When this happens, the second process is unable to create a server because of the first process and it also has no io streams because the the client detaches its streams. We can detect this in the shell command and prevent the process from persisting as a zombie.
This commit is contained in:
parent
ae2899baae
commit
261084bbb2
|
|
@ -538,6 +538,7 @@ object Terminal {
|
|||
case _ => None
|
||||
}
|
||||
}
|
||||
private[sbt] def startedByRemoteClient = props.isDefined
|
||||
|
||||
/**
|
||||
* Creates an instance of [[Terminal]] that delegates most of its methods to an underlying
|
||||
|
|
|
|||
|
|
@ -1019,22 +1019,31 @@ object BuiltinCommands {
|
|||
val exchange = StandardMain.exchange
|
||||
val welcomeState = displayWelcomeBanner(s0)
|
||||
val s1 = exchange run welcomeState
|
||||
exchange prompt ConsolePromptEvent(s0)
|
||||
val minGCInterval = Project
|
||||
.extract(s1)
|
||||
.getOpt(Keys.minForcegcInterval)
|
||||
.getOrElse(GCUtil.defaultMinForcegcInterval)
|
||||
val exec: Exec = getExec(s1, minGCInterval)
|
||||
val newState = s1
|
||||
.copy(
|
||||
onFailure = Some(Exec(Shell, None)),
|
||||
remainingCommands = exec +: Exec(Shell, None) +: s1.remainingCommands
|
||||
)
|
||||
.setInteractive(true)
|
||||
val res =
|
||||
if (exec.commandLine.trim.isEmpty) newState
|
||||
else newState.clearGlobalLog
|
||||
res
|
||||
/*
|
||||
* It is possible for sbt processes to leak if two are started simultaneously
|
||||
* by a remote client and only one is able to start a server. This seems to
|
||||
* happen primarily on windows.
|
||||
*/
|
||||
if (Terminal.startedByRemoteClient && !exchange.hasServer) {
|
||||
Exec("shutdown", None) +: s1
|
||||
} else {
|
||||
exchange prompt ConsolePromptEvent(s0)
|
||||
val minGCInterval = Project
|
||||
.extract(s1)
|
||||
.getOpt(Keys.minForcegcInterval)
|
||||
.getOrElse(GCUtil.defaultMinForcegcInterval)
|
||||
val exec: Exec = getExec(s1, minGCInterval)
|
||||
val newState = s1
|
||||
.copy(
|
||||
onFailure = Some(Exec(Shell, None)),
|
||||
remainingCommands = exec +: Exec(Shell, None) +: s1.remainingCommands
|
||||
)
|
||||
.setInteractive(true)
|
||||
val res =
|
||||
if (exec.commandLine.trim.isEmpty) newState
|
||||
else newState.clearGlobalLog
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
def startServer: Command =
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ private[sbt] final class CommandExchange {
|
|||
private val nextChannelId: AtomicInteger = new AtomicInteger(0)
|
||||
private[this] val lastState = new AtomicReference[State]
|
||||
private[this] val currentExecRef = new AtomicReference[Exec]
|
||||
private[sbt] def hasServer = server.isDefined
|
||||
|
||||
def channels: List[CommandChannel] = channelBuffer.toList
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue