diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 54b8b5216..a1a54f095 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -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 = { diff --git a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala index 0a04ce4d4..823cad5fe 100644 --- a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala +++ b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala @@ -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() diff --git a/main/src/main/scala/sbt/internal/server/Definition.scala b/main/src/main/scala/sbt/internal/server/Definition.scala index 038eabc4c..b741f6feb 100644 --- a/main/src/main/scala/sbt/internal/server/Definition.scala +++ b/main/src/main/scala/sbt/internal/server/Definition.scala @@ -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 } diff --git a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala index a23435173..9a369acb8 100644 --- a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala @@ -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" =>