mirror of https://github.com/sbt/sbt.git
explicitly close streams on java.lang.Process to avoid descriptor leaks
This commit is contained in:
parent
d5bc40e5d9
commit
3191eedf9e
|
|
@ -55,6 +55,11 @@ object BasicIO
|
||||||
final val BufferSize = 8192
|
final val BufferSize = 8192
|
||||||
final val Newline = System.getProperty("line.separator")
|
final val Newline = System.getProperty("line.separator")
|
||||||
|
|
||||||
|
def closeProcessStreams(p: JProcess): Unit =
|
||||||
|
Seq(p.getOutputStream, p.getInputStream, p.getErrorStream) foreach { s =>
|
||||||
|
if(s ne null) close(s)
|
||||||
|
}
|
||||||
|
|
||||||
def close(c: java.io.Closeable) = try { c.close() } catch { case _: java.io.IOException => () }
|
def close(c: java.io.Closeable) = try { c.close() } catch { case _: java.io.IOException => () }
|
||||||
def processFully(buffer: Appendable): InputStream => Unit = processFully(appendLine(buffer))
|
def processFully(buffer: Appendable): InputStream => Unit = processFully(appendLine(buffer))
|
||||||
def processFully(processLine: String => Unit): InputStream => Unit =
|
def processFully(processLine: String => Unit): InputStream => Unit =
|
||||||
|
|
@ -364,8 +369,6 @@ private[sbt] class DummyProcessBuilder(override val toString: String, exitValue
|
||||||
override def run(io: ProcessIO): Process = new DummyProcess(exitValue)
|
override def run(io: ProcessIO): Process = new DummyProcess(exitValue)
|
||||||
override def canPipeTo = true
|
override def canPipeTo = true
|
||||||
}
|
}
|
||||||
/** 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. */
|
|
||||||
private class DummyProcess(action: => Int) extends Process
|
private class DummyProcess(action: => Int) extends Process
|
||||||
{
|
{
|
||||||
private[this] val exitCode = Future(action)
|
private[this] val exitCode = Future(action)
|
||||||
|
|
@ -399,18 +402,23 @@ private[sbt] class SimpleProcessBuilder(p: JProcessBuilder) extends AbstractProc
|
||||||
* returning. */
|
* returning. */
|
||||||
private class SimpleProcess(p: JProcess, inputThread: Thread, outputThreads: List[Thread]) extends Process
|
private class SimpleProcess(p: JProcess, inputThread: Thread, outputThreads: List[Thread]) extends Process
|
||||||
{
|
{
|
||||||
override def exitValue() =
|
override def exitValue(): Int =
|
||||||
{
|
{
|
||||||
try { p.waitFor() }// wait for the process to terminate
|
andCleanup { p.waitFor() }// wait for the process to terminate
|
||||||
finally { inputThread.interrupt() } // we interrupt the input thread to notify it that it can terminate
|
|
||||||
outputThreads.foreach(_.join()) // this ensures that all output is complete before returning (waitFor does not ensure this)
|
|
||||||
p.exitValue()
|
p.exitValue()
|
||||||
}
|
}
|
||||||
override def destroy() =
|
override def destroy() =
|
||||||
{
|
andCleanup { p.destroy() }
|
||||||
try { p.destroy() }
|
|
||||||
finally { inputThread.interrupt() }
|
private[this] def andCleanup[T](action: => Unit): Unit =
|
||||||
}
|
try
|
||||||
|
{
|
||||||
|
try { action }
|
||||||
|
finally { inputThread.interrupt() } // we interrupt the input thread to notify it that it can terminate
|
||||||
|
outputThreads.foreach(_.join()) // this ensures that all output is complete before returning (waitFor does not ensure this)
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
BasicIO.closeProcessStreams(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FileOutput(file: File, append: Boolean) extends OutputStreamBuilder(new FileOutputStream(file, append), file.getAbsolutePath)
|
private class FileOutput(file: File, append: Boolean) extends OutputStreamBuilder(new FileOutputStream(file, append), file.getAbsolutePath)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue