Skip to content

Commit 6181525

Browse files
committed
At the end of a run, close macro runtime's classloader
We can only do this on 2.12.x, because URLClassLoader#close is new in JDK 7. Tested manually with the REPL and resident compilers. ``` % qscalac sandbox/macro.scala && (for i in 1 2; do echo sandbox/client.scala; done; printf '\n') | qscalac -Xresident -Ylog:all -Ydebug 2>&1 | grep "Closing macro runtime classloader" [log terminal] Closing macro runtime classloader [log terminal] Closing macro runtime classloader % qscalac sandbox/macro.scala && (for i in 1 2; do echo Macro.m; done; printf '\n') | qscala -Ylog:all -Ydebug 2>&1 | grep "Closing macro runtime classloader"; stty echo [log terminal] Closing macro runtime classloader [log terminal] Closing macro runtime classloader ``` Note: this doesn't close handles to JAR files held by the compiler classpath implementation, that will require changes elsewhere.
1 parent 7e57bcc commit 6181525

File tree

3 files changed

+36
-10
lines changed

3 files changed

+36
-10
lines changed

src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package scala.reflect.macros
22
package runtime
33

4+
import java.net.URLClassLoader
5+
46
import scala.reflect.internal.Flags._
7+
import scala.reflect.internal.util.ScalaClassLoader
58
import scala.reflect.runtime.ReflectionUtils
9+
import scala.reflect.internal.util.AbstractFileClassLoader
610

711
trait MacroRuntimes extends JavaReflectionRuntimes {
812
self: scala.tools.nsc.typechecker.Analyzer =>
@@ -44,7 +48,15 @@ trait MacroRuntimes extends JavaReflectionRuntimes {
4448
* which compiles implementations into a virtual directory (very much like REPL does) and then conjures
4549
* a classloader mapped to that virtual directory.
4650
*/
47-
lazy val defaultMacroClassloader: ClassLoader = findMacroClassLoader()
51+
private lazy val defaultMacroClassloaderCache = {
52+
def attemptClose(loader: ClassLoader): Unit = loader match {
53+
case u: URLClassLoader => debuglog("Closing macro runtime classloader"); u.close()
54+
case afcl: AbstractFileClassLoader => attemptClose(afcl.getParent)
55+
case _ => ???
56+
}
57+
perRunCaches.newGeneric(findMacroClassLoader, attemptClose _)
58+
}
59+
def defaultMacroClassloader: ClassLoader = defaultMacroClassloaderCache()
4860

4961
/** Abstracts away resolution of macro runtimes.
5062
*/

src/reflect/scala/reflect/internal/SymbolTable.scala

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -375,20 +375,30 @@ abstract class SymbolTable extends macros.Universe
375375
def newWeakSet[K <: AnyRef]() = recordCache(new WeakHashSet[K]())
376376

377377
def newAnyRefMap[K <: AnyRef, V]() = recordCache(mutable.AnyRefMap[K, V]())
378-
def newGeneric[T](f: => T): () => T = {
378+
/**
379+
* Register a cache specified by a factory function and (optionally) a cleanup function.
380+
*
381+
* @return A function that will return cached value, or create a fresh value when a new run is started.
382+
*/
383+
def newGeneric[T](f: => T, cleanup: T => Unit = (x: Any) => ()): () => T = {
379384
val NoCached: T = null.asInstanceOf[T]
380385
var cached: T = NoCached
381386
var cachedRunId = NoRunId
382-
recordCache(new Clearable {
383-
def clear(): Unit = cached = NoCached
384-
})
385-
() => {
386-
if (currentRunId != cachedRunId || cached == NoCached) {
387-
cached = f
388-
cachedRunId = currentRunId
387+
val clearable = new Clearable with (() => T) {
388+
def clear(): Unit = {
389+
if (cached != NoCached)
390+
cleanup(cached)
391+
cached = NoCached
392+
}
393+
def apply(): T = {
394+
if (currentRunId != cachedRunId || cached == NoCached) {
395+
cached = f
396+
cachedRunId = currentRunId
397+
}
398+
cached
389399
}
390-
cached
391400
}
401+
recordCache(clearable)
392402
}
393403
}
394404

src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ object ScalaClassLoader {
139139
classloaderURLs :+= url
140140
super.addURL(url)
141141
}
142+
override def close(): Unit = {
143+
super.close()
144+
classloaderURLs = null
145+
}
142146
}
143147

144148
def fromURLs(urls: Seq[URL], parent: ClassLoader = null): URLClassLoader =

0 commit comments

Comments
 (0)