Don't poll System.in in ConsoleChannel

The ask user thread is a background thread so it's fine for it to block
on System.in. By blocking rather than polling, the cpu utilization of
sbt drops to 0 on idle. We have to explicitly handle <ctrl+d> if we
block though because the JLine console reader will return null both if
the input stream returns -1
This commit is contained in:
Ethan Atkins 2019-12-17 12:46:14 -08:00
parent 7902ec3b7d
commit 2e3a1e767d
3 changed files with 31 additions and 8 deletions

View File

@ -94,6 +94,7 @@ private[sbt] object JLine {
@deprecated("Handled by Terminal.fixTerminalProperty", "1.4.0")
private[sbt] def fixTerminalProperty(): Unit = ()
@deprecated("For binary compatibility only", "1.4.0")
private[sbt] def makeInputStream(injectThreadSleep: Boolean): InputStream =
if (injectThreadSleep) new InputStreamWrapper(originalIn, 2.milliseconds)
else originalIn
@ -176,6 +177,7 @@ final class FullReader(
val handleCONT: Boolean,
inputStream: InputStream,
) extends JLine {
@deprecated("Use the constructor with no injectThreadSleep parameter", "1.4.0")
def this(
historyPath: Option[File],
complete: Parser[_],

View File

@ -8,6 +8,7 @@
package sbt.internal.util
import java.io.{ InputStream, OutputStream }
import java.nio.channels.ClosedChannelException
import java.util.Locale
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
import java.util.concurrent.locks.ReentrantLock
@ -54,6 +55,18 @@ object Terminal {
*/
def systemInIsAttached: Boolean = attached.get
/**
* Returns an InputStream that will throw a [[ClosedChannelException]] if read returns -1.
* @return the wrapped InputStream.
*/
private[sbt] def throwOnClosedSystemIn: InputStream = new InputStream {
override def available(): Int = WrappedSystemIn.available()
override def read(): Int = WrappedSystemIn.read() match {
case -1 => throw new ClosedChannelException
case r => r
}
}
/**
* Provides a wrapper around System.in. The wrapped stream in will check if the terminal is attached
* in available and read. If a read returns -1, it will mark System.in as unattached so that

View File

@ -8,12 +8,14 @@
package sbt
package internal
import sbt.internal.util._
import BasicKeys._
import java.io.File
import java.nio.channels.ClosedChannelException
import sbt.BasicKeys._
import sbt.internal.util.Util.AnyOps
import sbt.internal.util._
import sbt.protocol.EventMessage
import sjsonnew.JsonFormat
import Util.AnyOps
private[sbt] final class ConsoleChannel(val name: String) extends CommandChannel {
private var askUserThread: Option[Thread] = None
@ -23,13 +25,19 @@ private[sbt] final class ConsoleChannel(val name: String) extends CommandChannel
case Some(pf) => pf(s)
case None => "> "
}
val reader = new FullReader(history, s.combinedParser, JLine.HandleCONT, true)
val reader =
new FullReader(history, s.combinedParser, JLine.HandleCONT, Terminal.throwOnClosedSystemIn)
override def run(): Unit = {
// This internally handles thread interruption and returns Some("")
val line = reader.readLine(prompt)
line match {
case Some(cmd) => append(Exec(cmd, Some(Exec.newExecId), Some(CommandSource(name))))
case None => append(Exec("exit", Some(Exec.newExecId), Some(CommandSource(name))))
try {
reader.readLine(prompt) match {
case Some(cmd) => append(Exec(cmd, Some(Exec.newExecId), Some(CommandSource(name))))
case None =>
println("") // Prevents server shutdown log lines from appearing on the prompt line
append(Exec("exit", Some(Exec.newExecId), Some(CommandSource(name))))
}
} catch {
case _: ClosedChannelException =>
}
askUserThread = None
}