summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2016-03-30 17:37:57 +1000
committerJason Zaugg <jzaugg@gmail.com>2016-03-30 17:37:57 +1000
commit6181525f60588228ce99ab3ef2593ecfcfd35066 (patch)
treed85751d95ca93406768d4045c8d6655b4800d222
parent7e57bcc1818a402ad6ec32416af2b4522671f9ab (diff)
downloadscala-6181525f60588228ce99ab3ef2593ecfcfd35066.tar.gz
scala-6181525f60588228ce99ab3ef2593ecfcfd35066.tar.bz2
scala-6181525f60588228ce99ab3ef2593ecfcfd35066.zip
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.
-rw-r--r--src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala14
-rw-r--r--src/reflect/scala/reflect/internal/SymbolTable.scala28
-rw-r--r--src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala4
3 files changed, 36 insertions, 10 deletions
diff --git a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala
index 5fd9c0db34..d4b8175d28 100644
--- a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala
+++ b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala
@@ -1,8 +1,12 @@
package scala.reflect.macros
package runtime
+import java.net.URLClassLoader
+
import scala.reflect.internal.Flags._
+import scala.reflect.internal.util.ScalaClassLoader
import scala.reflect.runtime.ReflectionUtils
+import scala.reflect.internal.util.AbstractFileClassLoader
trait MacroRuntimes extends JavaReflectionRuntimes {
self: scala.tools.nsc.typechecker.Analyzer =>
@@ -44,7 +48,15 @@ trait MacroRuntimes extends JavaReflectionRuntimes {
* which compiles implementations into a virtual directory (very much like REPL does) and then conjures
* a classloader mapped to that virtual directory.
*/
- lazy val defaultMacroClassloader: ClassLoader = findMacroClassLoader()
+ private lazy val defaultMacroClassloaderCache = {
+ def attemptClose(loader: ClassLoader): Unit = loader match {
+ case u: URLClassLoader => debuglog("Closing macro runtime classloader"); u.close()
+ case afcl: AbstractFileClassLoader => attemptClose(afcl.getParent)
+ case _ => ???
+ }
+ perRunCaches.newGeneric(findMacroClassLoader, attemptClose _)
+ }
+ def defaultMacroClassloader: ClassLoader = defaultMacroClassloaderCache()
/** Abstracts away resolution of macro runtimes.
*/
diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala
index 88a94ab530..6d988479af 100644
--- a/src/reflect/scala/reflect/internal/SymbolTable.scala
+++ b/src/reflect/scala/reflect/internal/SymbolTable.scala
@@ -375,20 +375,30 @@ abstract class SymbolTable extends macros.Universe
def newWeakSet[K <: AnyRef]() = recordCache(new WeakHashSet[K]())
def newAnyRefMap[K <: AnyRef, V]() = recordCache(mutable.AnyRefMap[K, V]())
- def newGeneric[T](f: => T): () => T = {
+ /**
+ * Register a cache specified by a factory function and (optionally) a cleanup function.
+ *
+ * @return A function that will return cached value, or create a fresh value when a new run is started.
+ */
+ def newGeneric[T](f: => T, cleanup: T => Unit = (x: Any) => ()): () => T = {
val NoCached: T = null.asInstanceOf[T]
var cached: T = NoCached
var cachedRunId = NoRunId
- recordCache(new Clearable {
- def clear(): Unit = cached = NoCached
- })
- () => {
- if (currentRunId != cachedRunId || cached == NoCached) {
- cached = f
- cachedRunId = currentRunId
+ val clearable = new Clearable with (() => T) {
+ def clear(): Unit = {
+ if (cached != NoCached)
+ cleanup(cached)
+ cached = NoCached
+ }
+ def apply(): T = {
+ if (currentRunId != cachedRunId || cached == NoCached) {
+ cached = f
+ cachedRunId = currentRunId
+ }
+ cached
}
- cached
}
+ recordCache(clearable)
}
}
diff --git a/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala b/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala
index cc5be92489..f3db2017be 100644
--- a/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala
+++ b/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala
@@ -139,6 +139,10 @@ object ScalaClassLoader {
classloaderURLs :+= url
super.addURL(url)
}
+ override def close(): Unit = {
+ super.close()
+ classloaderURLs = null
+ }
}
def fromURLs(urls: Seq[URL], parent: ClassLoader = null): URLClassLoader =