Skip to content

Commit 2a3ad8d

Browse files
authored
Clean Linker caches when ScalaJSWorker is closed (#5933)
This is a smaller step compared to #5931. Still doesn't fix all the problems with linkers, but it's enough to guarantee teardown happens correctly and Scala.js linker caches are cleaned. Pull Request: #5933
1 parent d81f5e4 commit 2a3ad8d

File tree

4 files changed

+33
-34
lines changed

4 files changed

+33
-34
lines changed

libs/scalajslib/api/src/mill/scalajslib/worker/api/ScalaJSWorkerApi.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package mill.scalajslib.worker.api
33
import java.io.File
44
import java.nio.file.Path
55

6-
private[scalajslib] trait ScalaJSWorkerApi {
6+
private[scalajslib] trait ScalaJSWorkerApi extends AutoCloseable {
77
def link(
88
runClasspath: Seq[Path],
99
dest: File,

libs/scalajslib/package.mill

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ object `package` extends MillStableScalaModule with BuildInfo {
4949
def scalajsWorkerVersion = crossValue
5050
def moduleDir: os.Path = super.moduleDir / scalajsWorkerVersion
5151
def compileModuleDeps =
52-
Seq(build.libs.scalajslib.api, build.core.constants, build.core.api.daemon)
52+
Seq(build.libs.scalajslib.api, build.libs.util, build.core.constants, build.core.api.daemon)
5353
def mandatoryMvnDeps = Seq.empty[Dep]
5454
def mvnDeps = Seq(Deps.scalafmtDynamic)
5555
def compileMvnDeps = super.mandatoryMvnDeps() ++ Seq(

libs/scalajslib/src/mill/scalajslib/worker/ScalaJSWorker.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ private[scalajslib] class ScalaJSWorker(jobs: Int)
2424
)
2525
val bridge = cl
2626
.loadClass("mill.scalajslib.worker.ScalaJSWorkerImpl")
27-
.getDeclaredConstructor()
28-
.newInstance()
27+
.getDeclaredConstructor(classOf[Int])
28+
.newInstance(jobs)
2929
.asInstanceOf[workerApi.ScalaJSWorkerApi]
3030

3131
(cl, bridge)
@@ -35,7 +35,9 @@ private[scalajslib] class ScalaJSWorker(jobs: Int)
3535
key: Seq[PathRef],
3636
value: (URLClassLoader, workerApi.ScalaJSWorkerApi)
3737
): Unit = {
38-
value._1.close()
38+
val (classloader, workerApi) = value
39+
workerApi.close()
40+
classloader.close()
3941
}
4042

4143
override def maxCacheSize: Int = jobs

libs/scalajslib/worker/1/src/mill/scalajslib/worker/ScalaJSWorkerImpl.scala

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package mill.scalajslib.worker
22

3-
import scala.concurrent.Await
4-
import scala.concurrent.duration.Duration
53
import java.io.File
64
import java.nio.file.Path
5+
import scala.concurrent.Await
6+
import scala.concurrent.duration.Duration
7+
8+
import com.armanbilge.sjsimportmap.ImportMappedIRFile
9+
import mill.constants.InputPumper
710
import mill.scalajslib.worker.api.*
811
import mill.scalajslib.worker.jsenv.*
12+
import mill.util.CachedFactory
913
import org.scalajs.ir.ScalaJSVersions
1014
import org.scalajs.linker.{PathIRContainer, PathOutputDirectory, PathOutputFile, StandardImpl}
1115
import org.scalajs.linker.interface.{
@@ -22,12 +26,7 @@ import org.scalajs.jsenv.{Input, JSEnv, RunConfig}
2226
import org.scalajs.testing.adapter.TestAdapter
2327
import org.scalajs.testing.adapter.TestAdapterInitializer as TAI
2428

25-
import scala.collection.mutable
26-
import scala.ref.SoftReference
27-
import com.armanbilge.sjsimportmap.ImportMappedIRFile
28-
import mill.constants.InputPumper
29-
30-
class ScalaJSWorkerImpl extends ScalaJSWorkerApi {
29+
class ScalaJSWorkerImpl(jobs: Int) extends ScalaJSWorkerApi {
3130
private case class LinkerInput(
3231
isFullLinkJS: Boolean,
3332
optimizer: Boolean,
@@ -44,15 +43,12 @@ class ScalaJSWorkerImpl extends ScalaJSWorkerApi {
4443
case s"1.$n.$_" if n.toIntOption.exists(_ < number) => false
4544
case _ => true
4645
}
47-
private object ScalaJSLinker {
46+
private object ScalaJSLinker extends CachedFactory[LinkerInput, (Linker, IRFileCache.Cache)] {
4847
private val irFileCache = StandardImpl.irFileCache()
49-
private val cache = mutable.Map.empty[LinkerInput, SoftReference[(Linker, IRFileCache.Cache)]]
50-
def reuseOrCreate(input: LinkerInput): (Linker, IRFileCache.Cache) = cache.get(input) match {
51-
case Some(SoftReference((linker, irFileCacheCache))) => (linker, irFileCacheCache)
52-
case _ =>
53-
val newResult = createLinker(input)
54-
cache.update(input, SoftReference(newResult))
55-
newResult
48+
override def maxCacheSize: Int = jobs
49+
override def setup(key: LinkerInput): (Linker, IRFileCache.Cache) = createLinker(key)
50+
override def teardown(key: LinkerInput, value: (Linker, IRFileCache.Cache)): Unit = {
51+
value._2.free()
5652
}
5753
private def createLinker(input: LinkerInput): (Linker, IRFileCache.Cache) = {
5854
val semantics = input.isFullLinkJS match {
@@ -197,23 +193,22 @@ class ScalaJSWorkerImpl extends ScalaJSWorkerApi {
197193
minify: Boolean,
198194
importMap: Seq[ESModuleImportMapping],
199195
experimentalUseWebAssembly: Boolean
200-
): Either[String, Report] = {
196+
): Either[String, Report] = ScalaJSLinker.withValue(LinkerInput(
197+
isFullLinkJS = isFullLinkJS,
198+
optimizer = optimizer,
199+
sourceMap = sourceMap,
200+
moduleKind = moduleKind,
201+
esFeatures = esFeatures,
202+
moduleSplitStyle = moduleSplitStyle,
203+
outputPatterns = outputPatterns,
204+
minify = minify,
205+
dest = dest,
206+
experimentalUseWebAssembly = experimentalUseWebAssembly
207+
)) { (linker, irFileCacheCache) =>
201208
// On Scala.js 1.2- we want to use the legacy mode either way since
202209
// the new mode is not supported and in tests we always use legacy = false
203210
val useLegacy = forceOutJs || !minorIsGreaterThanOrEqual(3)
204211
import scala.concurrent.ExecutionContext.Implicits.global
205-
val (linker, irFileCacheCache) = ScalaJSLinker.reuseOrCreate(LinkerInput(
206-
isFullLinkJS = isFullLinkJS,
207-
optimizer = optimizer,
208-
sourceMap = sourceMap,
209-
moduleKind = moduleKind,
210-
esFeatures = esFeatures,
211-
moduleSplitStyle = moduleSplitStyle,
212-
outputPatterns = outputPatterns,
213-
minify = minify,
214-
dest = dest,
215-
experimentalUseWebAssembly = experimentalUseWebAssembly
216-
))
217212
val irContainersAndPathsFuture = PathIRContainer.fromClasspath(runClasspath)
218213
val testInitializer =
219214
if (testBridgeInit)
@@ -390,4 +385,6 @@ class ScalaJSWorkerImpl extends ScalaJSWorkerApi {
390385
}
391386
Seq(input)
392387
}
388+
389+
override def close(): Unit = ScalaJSLinker.close()
393390
}

0 commit comments

Comments
 (0)