Merge pull request #4544 from eatkins/gc-on-idle

Run gc when idle
This commit is contained in:
Ethan Atkins 2019-02-15 10:58:41 -08:00 committed by GitHub
commit 45f5019ee0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 26 deletions

View File

@ -884,7 +884,11 @@ object BuiltinCommands {
val exchange = StandardMain.exchange
val s1 = exchange run s0
exchange publishEventMessage ConsolePromptEvent(s0)
val exec: Exec = exchange.blockUntilNextExec
val minGCInterval = Project
.extract(s1)
.getOpt(Keys.minForcegcInterval)
.getOrElse(GCUtil.defaultMinForcegcInterval)
val exec: Exec = exchange.blockUntilNextExec(minGCInterval, s1.globalLogging.full)
val newState = s1
.copy(
onFailure = Some(Exec(Shell, None)),

View File

@ -11,32 +11,36 @@ package internal
import java.io.IOException
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic._
import scala.collection.mutable.ListBuffer
import scala.annotation.tailrec
import BasicKeys.{
autoStartServer,
serverHost,
serverPort,
fullServerHandlers,
logLevel,
serverAuthentication,
serverConnectionType,
serverHost,
serverLogLevel,
fullServerHandlers,
logLevel
serverPort
}
import java.net.Socket
import sbt.Watched.NullLogger
import sjsonnew.JsonFormat
import sjsonnew.shaded.scalajson.ast.unsafe._
import scala.concurrent.Await
import scala.concurrent.duration.Duration
import scala.util.{ Success, Failure, Try }
import scala.concurrent.duration._
import scala.util.{ Failure, Success, Try }
import sbt.io.syntax._
import sbt.io.{ Hash, IO }
import sbt.internal.server._
import sbt.internal.langserver.{ LogMessageParams, MessageType }
import sbt.internal.util.{ StringEvent, ObjectEvent, MainAppender }
import sbt.internal.util.{ MainAppender, ObjectEvent, StringEvent }
import sbt.internal.util.codec.JValueFormats
import sbt.protocol.{ EventMessage, ExecStatusEvent }
import sbt.util.{ Level, Logger, LogExchange }
import sbt.util.{ Level, LogExchange, Logger }
/**
* The command exchange merges multiple command channels (e.g. network and console),
@ -59,22 +63,34 @@ private[sbt] final class CommandExchange {
def channels: List[CommandChannel] = channelBuffer.toList
def subscribe(c: CommandChannel): Unit = channelBufferLock.synchronized(channelBuffer.append(c))
def blockUntilNextExec: Exec = blockUntilNextExec(Duration.Inf, NullLogger)
// periodically move all messages from all the channels
@tailrec def blockUntilNextExec: Exec = {
@tailrec def slurpMessages(): Unit =
channels.foldLeft(Option.empty[Exec]) { _ orElse _.poll } match {
case None => ()
case Some(x) =>
commandQueue.add(x)
slurpMessages
private[sbt] def blockUntilNextExec(interval: Duration, logger: Logger): Exec = {
@tailrec def impl(deadline: Option[Deadline]): Exec = {
@tailrec def slurpMessages(): Unit =
channels.foldLeft(Option.empty[Exec]) { _ orElse _.poll } match {
case None => ()
case Some(x) =>
commandQueue.add(x)
slurpMessages()
}
slurpMessages()
Option(commandQueue.poll) match {
case Some(x) => x
case None =>
Thread.sleep(2)
val newDeadline = if (deadline.fold(false)(_.isOverdue())) {
GCUtil.forceGcWithInterval(interval, logger)
None
} else deadline
impl(newDeadline)
}
slurpMessages()
Option(commandQueue.poll) match {
case Some(x) => x
case None =>
Thread.sleep(50)
blockUntilNextExec
}
// Do not manually run GC until the user has been idling for at least the min gc interval.
impl(interval match {
case d: FiniteDuration => Some(d.fromNow)
case _ => None
})
}
def run(s: State): State = {
@ -83,7 +99,7 @@ private[sbt] final class CommandExchange {
consoleChannel = Some(console0)
subscribe(console0)
}
val autoStartServerAttr = (s get autoStartServer) match {
val autoStartServerAttr = s get autoStartServer match {
case Some(bool) => bool
case None => true
}
@ -261,13 +277,13 @@ private[sbt] final class CommandExchange {
val toDel: ListBuffer[CommandChannel] = ListBuffer.empty
def json: JValue = JObject(
JField("type", JString(event.contentType)),
(Vector(JField("message", event.json), JField("level", JString(event.level.toString))) ++
Vector(JField("message", event.json), JField("level", JString(event.level.toString))) ++
(event.channelName.toVector map { channelName =>
JField("channelName", JString(channelName))
}) ++
(event.execId.toVector map { execId =>
JField("execId", JString(execId))
})): _*
}): _*
)
channels collect {
case c: ConsoleChannel =>

View File

@ -17,7 +17,7 @@ private[sbt] object GCUtil {
// Returns the default force garbage collection flag,
// as specified by system properties.
val defaultForceGarbageCollection: Boolean = true
val defaultMinForcegcInterval: Duration = 60.seconds
val defaultMinForcegcInterval: Duration = 10.minutes
val lastGcCheck: AtomicLong = new AtomicLong(0L)
def forceGcWithInterval(minForcegcInterval: Duration, log: Logger): Unit = {