Don't use global execution context

Because we are sharing the scala library classloader with test and run,
it is possible that sbt will be competing with for resources with the
test and run tasks when trying to get threads from the global execution
context. Also, by using our own execution context, we can shut it down
when sbt exits.

The motivation for this change is that I was looking at the active jvm
threads of an idle sbt process and noticed a bunch of global execution
context threads.
This commit is contained in:
Ethan Atkins 2019-05-30 12:43:40 -07:00
parent 63956827e0
commit 3de3cc15cf
4 changed files with 18 additions and 5 deletions

View File

@ -9,6 +9,7 @@ package sbt
import java.io.{ File, IOException }
import java.net.URI
import java.util.concurrent.{ Executors, ForkJoinPool }
import java.util.concurrent.atomic.AtomicBoolean
import java.util.{ Locale, Properties }
@ -96,11 +97,21 @@ object StandardMain {
private[sbt] lazy val exchange = new CommandExchange()
import scalacache.caffeine._
private[sbt] lazy val cache: scalacache.Cache[Any] = CaffeineCache[Any]
// The access to the pool should be thread safe because lazy val instantiation is thread safe
// and pool is only referenced directly in closeRunnable after the executionContext is sure
// to have been instantiated
private[this] var pool: Option[ForkJoinPool] = None
private[sbt] lazy val executionContext: ExecutionContext = ExecutionContext.fromExecutor({
val p = new ForkJoinPool
pool = Some(p)
p
})
private[this] val closeRunnable = () => {
cache.close()(scalacache.modes.sync.mode)
cache.close()(scalacache.modes.scalaFuture.mode(ExecutionContext.global))
cache.close()(scalacache.modes.scalaFuture.mode(executionContext))
exchange.shutdown()
pool.foreach(_.shutdownNow())
}
def runManaged(s: State): xsbti.MainResult = {

View File

@ -93,9 +93,9 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe
val workingDirectory: File,
val job: BackgroundJob
) extends AbstractJobHandle {
implicit val executionContext: ExecutionContext = StandardMain.executionContext
def humanReadableName: String = job.humanReadableName
// EC for onStop handler below
import ExecutionContext.Implicits.global
job.onStop { () =>
// TODO: Fix this
// logger.close()

View File

@ -234,7 +234,7 @@ private[sbt] object Definition {
private[sbt] def getAnalyses: Future[Seq[Analysis]] = {
import scalacache.modes.scalaFuture._
import scala.concurrent.ExecutionContext.Implicits.global
implicit val executionContext: ExecutionContext = StandardMain.executionContext
AnalysesAccess
.getFrom(StandardMain.cache)
.collect { case Some(a) => a }

View File

@ -13,7 +13,7 @@ import sjsonnew.JsonFormat
import sjsonnew.shaded.scalajson.ast.unsafe.JValue
import sjsonnew.support.scalajson.unsafe.Converter
import sbt.protocol.Serialization
import sbt.protocol.{ SettingQuery => Q, CompletionParams => CP }
import sbt.protocol.{ CompletionParams => CP, SettingQuery => Q }
import sbt.internal.langserver.{ CancelRequestParams => CRP }
import sbt.internal.protocol._
import sbt.internal.protocol.codec._
@ -21,6 +21,8 @@ import sbt.internal.langserver._
import sbt.internal.util.ObjectEvent
import sbt.util.Logger
import scala.concurrent.ExecutionContext
private[sbt] final case class LangServerError(code: Long, message: String)
extends Throwable(message)
@ -70,7 +72,7 @@ private[sbt] object LanguageServerProtocol {
jsonRpcRespond(InitializeResult(serverCapabilities), Option(r.id))
case r: JsonRpcRequestMessage if r.method == "textDocument/definition" =>
import scala.concurrent.ExecutionContext.Implicits.global
implicit val executionContext: ExecutionContext = StandardMain.executionContext
Definition.lspDefinition(json(r), r.id, CommandSource(name), log)
()
case r: JsonRpcRequestMessage if r.method == "sbt/exec" =>