From 484d6d70a4c51d0ddf220c67265182949e69ca45 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 29 Jan 2013 20:47:37 +0100 Subject: SI-6240 introduces GIL to Scala reflection On a serious note, I feel really uncomfortable about having to juggle this slew of locks. Despite that I can't immediately find a deadlock, I'm 100% sure there is one hiding in the shadows. Hence, I'm abandoning all runtime reflection locks in favor of a single per-universe one. --- .../scala/reflect/internal/BaseTypeSeqs.scala | 2 +- src/reflect/scala/reflect/runtime/Gil.scala | 22 +++++ .../scala/reflect/runtime/JavaMirrors.scala | 14 ++-- .../scala/reflect/runtime/JavaUniverseForce.scala | 7 +- .../scala/reflect/runtime/SymbolLoaders.scala | 27 ++++++- .../scala/reflect/runtime/SymbolTable.scala | 2 +- .../scala/reflect/runtime/SynchronizedOps.scala | 53 +++++++----- .../reflect/runtime/SynchronizedSymbols.scala | 94 +++++++++++----------- .../scala/reflect/runtime/SynchronizedTypes.scala | 48 +++++------ .../scala/reflect/runtime/TwoWayCaches.scala | 68 ++++++++++++++++ 10 files changed, 226 insertions(+), 111 deletions(-) create mode 100644 src/reflect/scala/reflect/runtime/Gil.scala create mode 100644 src/reflect/scala/reflect/runtime/TwoWayCaches.scala (limited to 'src/reflect') diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala index 05aaa462c4..19c67879f5 100644 --- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -38,7 +38,7 @@ trait BaseTypeSeqs { * This is necessary because when run from reflection every base type sequence needs to have a * SynchronizedBaseTypeSeq as mixin. */ - class BaseTypeSeq protected[BaseTypeSeqs] (private[BaseTypeSeqs] val parents: List[Type], private[BaseTypeSeqs] val elems: Array[Type]) { + class BaseTypeSeq protected[reflect] (private[BaseTypeSeqs] val parents: List[Type], private[BaseTypeSeqs] val elems: Array[Type]) { self => if (Statistics.canEnable) Statistics.incCounter(baseTypeSeqCount) if (Statistics.canEnable) Statistics.incCounter(baseTypeSeqLenTotal, elems.length) diff --git a/src/reflect/scala/reflect/runtime/Gil.scala b/src/reflect/scala/reflect/runtime/Gil.scala new file mode 100644 index 0000000000..8a8bfebf8c --- /dev/null +++ b/src/reflect/scala/reflect/runtime/Gil.scala @@ -0,0 +1,22 @@ +package scala.reflect +package runtime + +private[reflect] trait Gil { + self: SymbolTable => + + // fixme... please... + // there are the following avenues of optimization we discussed with Roland: + // 1) replace PackageScope locks with ConcurrentHashMap, because PackageScope materializers seem to be idempotent + // 2) unlock unpickling completers by verifying that they are idempotent or moving non-idempotent parts + // 3) remove the necessity in global state for isSubType + lazy val gil = new java.util.concurrent.locks.ReentrantLock + + @inline final def gilSynchronized[T](body: => T): T = { + try { + gil.lock() + body + } finally { + gil.unlock() + } + } +} diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 487da2eb4b..7ca73505b7 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -22,7 +22,7 @@ import ReflectionUtils.{staticSingletonInstance, innerSingletonInstance, scalacS import scala.language.existentials import scala.runtime.{ScalaRunTime, BoxesRunTime} -private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { thisUniverse: SymbolTable => +private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse with TwoWayCaches { thisUniverse: SymbolTable => private lazy val mirrors = new WeakHashMap[ClassLoader, WeakReference[JavaMirror]]() @@ -44,9 +44,11 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni trait JavaClassCompleter extends FlagAssigningCompleter - def runtimeMirror(cl: ClassLoader): Mirror = mirrors get cl match { - case Some(WeakReference(m)) => m - case _ => createMirror(rootMirror.RootClass, cl) + def runtimeMirror(cl: ClassLoader): Mirror = gilSynchronized { + mirrors get cl match { + case Some(WeakReference(m)) => m + case _ => createMirror(rootMirror.RootClass, cl) + } } /** The API of a mirror for a reflective universe */ @@ -684,7 +686,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni completeRest() } - def completeRest(): Unit = thisUniverse.synchronized { + def completeRest(): Unit = gilSynchronized { val tparams = clazz.rawInfo.typeParams val parents = try { @@ -889,7 +891,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * The Scala package with given fully qualified name. Unlike `packageNameToScala`, * this one bypasses the cache. */ - private[JavaMirrors] def makeScalaPackage(fullname: String): ModuleSymbol = { + private[JavaMirrors] def makeScalaPackage(fullname: String): ModuleSymbol = gilSynchronized { val split = fullname lastIndexOf '.' val ownerModule: ModuleSymbol = if (split > 0) packageNameToScala(fullname take split) else this.RootPackage diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 856a9ad8f9..70126e713a 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -27,11 +27,10 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.settings this.treeInfo + this.gil // inaccessible: this.uniqueLock - // inaccessible: this.subsametypeLock - // inaccessible: this.lubglbLock - // inaccessible: this.indentLock - // inaccessible: this.toStringLock + // inaccessible: this.nextIdLock + // inaccessible: this.freshExistentialNameLock // inaccessible: this.mirrors this.rootMirror this.treeBuild diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index 706572ba2b..30a3855d70 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -67,6 +67,25 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => } } + + // Since runtime reflection doesn't have a luxury of enumerating all classes + // on the classpath, it has to materialize symbols for top-level definitions + // (packages, classes, objects) on demand. + // + // Someone asks us for a class named `foo.Bar`? Easy. Let's speculatively create + // a package named `foo` and then look up `newTypeName("bar")` in its decls. + // This lookup, implemented in `SymbolLoaders.PackageScope` tests the waters by + // trying to to `Class.forName("foo.Bar")` and then creates a ClassSymbol upon + // success (the whole story is a bit longer, but the rest is irrelevant here). + // + // That's all neat, but these non-deterministic mutations of the global symbol + // table give a lot of trouble in multi-threaded setting. One of the popular + // reflection crashes happens when multiple threads happen to trigger symbol + // materialization multiple times for the same symbol, making subsequent + // reflective operations stumble upon outrageous stuff like overloaded packages. + // + // Short of significantly changing SymbolLoaders I see no other way than just + // to slap a global lock on materialization in runtime reflection. class PackageScope(pkgClass: Symbol) extends Scope(initFingerPrints = -1L) // disable fingerprinting as we do not know entries beforehand with SynchronizedScope { assert(pkgClass.isType) @@ -89,9 +108,11 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => else existing.sym.asInstanceOf[T] } - // disable fingerprinting as we do not know entries beforehand - private val negatives = mutable.Set[Name]() // Syncnote: Performance only, so need not be protected. - override def lookupEntry(name: Name): ScopeEntry = { + // package scopes need to synchronize on the GIL + // because lookupEntry might cause changes to the global symbol table + override def syncLockSynchronized[T](body: => T): T = gilSynchronized(body) + private val negatives = new mutable.HashSet[Name] + override def lookupEntry(name: Name): ScopeEntry = syncLockSynchronized { val e = super.lookupEntry(name) if (e != null) e diff --git a/src/reflect/scala/reflect/runtime/SymbolTable.scala b/src/reflect/scala/reflect/runtime/SymbolTable.scala index bcd4d16cde..7630fffd16 100644 --- a/src/reflect/scala/reflect/runtime/SymbolTable.scala +++ b/src/reflect/scala/reflect/runtime/SymbolTable.scala @@ -9,7 +9,7 @@ import scala.reflect.internal.Flags._ * It can be used either from a reflexive universe (class scala.reflect.runtime.JavaUniverse), or else from * a runtime compiler that uses reflection to get a class information (class scala.tools.reflect.ReflectGlobal) */ -private[scala] trait SymbolTable extends internal.SymbolTable with JavaMirrors with SymbolLoaders with SynchronizedOps { +private[scala] trait SymbolTable extends internal.SymbolTable with JavaMirrors with SymbolLoaders with SynchronizedOps with Gil { def info(msg: => String) = if (settings.verbose) println("[reflect-compiler] "+msg) diff --git a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala index 6aa47a0405..21df973297 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala @@ -14,20 +14,25 @@ private[reflect] trait SynchronizedOps extends internal.SymbolTable // BaseTypeSeqs override protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = - new BaseTypeSeq(parents, elems) with SynchronizedBaseTypeSeq + // only need to synchronize BaseTypeSeqs if they contain refined types + if (elems.filter(_.isInstanceOf[RefinedType]).nonEmpty) new BaseTypeSeq(parents, elems) with SynchronizedBaseTypeSeq + else new BaseTypeSeq(parents, elems) trait SynchronizedBaseTypeSeq extends BaseTypeSeq { - override def apply(i: Int): Type = synchronized { super.apply(i) } - override def rawElem(i: Int) = synchronized { super.rawElem(i) } - override def typeSymbol(i: Int): Symbol = synchronized { super.typeSymbol(i) } - override def toList: List[Type] = synchronized { super.toList } - override def copy(head: Type, offset: Int): BaseTypeSeq = synchronized { super.copy(head, offset) } - override def map(f: Type => Type): BaseTypeSeq = synchronized { super.map(f) } - override def exists(p: Type => Boolean): Boolean = synchronized { super.exists(p) } - override lazy val maxDepth = synchronized { maxDepthOfElems } - override def toString = synchronized { super.toString } - - override def lateMap(f: Type => Type): BaseTypeSeq = new MappedBaseTypeSeq(this, f) with SynchronizedBaseTypeSeq + override def apply(i: Int): Type = gilSynchronized { super.apply(i) } + override def rawElem(i: Int) = gilSynchronized { super.rawElem(i) } + override def typeSymbol(i: Int): Symbol = gilSynchronized { super.typeSymbol(i) } + override def toList: List[Type] = gilSynchronized { super.toList } + override def copy(head: Type, offset: Int): BaseTypeSeq = gilSynchronized { super.copy(head, offset) } + override def map(f: Type => Type): BaseTypeSeq = gilSynchronized { super.map(f) } + override def exists(p: Type => Boolean): Boolean = gilSynchronized { super.exists(p) } + override lazy val maxDepth = gilSynchronized { maxDepthOfElems } + override def toString = gilSynchronized { super.toString } + + override def lateMap(f: Type => Type): BaseTypeSeq = + // only need to synchronize BaseTypeSeqs if they contain refined types + if (map(f).toList.filter(_.isInstanceOf[RefinedType]).nonEmpty) new MappedBaseTypeSeq(this, f) with SynchronizedBaseTypeSeq + else new MappedBaseTypeSeq(this, f) } // Scopes @@ -36,15 +41,19 @@ private[reflect] trait SynchronizedOps extends internal.SymbolTable override def newNestedScope(outer: Scope): Scope = new Scope(outer) with SynchronizedScope trait SynchronizedScope extends Scope { - override def isEmpty: Boolean = synchronized { super.isEmpty } - override def size: Int = synchronized { super.size } - override def enter[T <: Symbol](sym: T): T = synchronized { super.enter(sym) } - override def rehash(sym: Symbol, newname: Name) = synchronized { super.rehash(sym, newname) } - override def unlink(e: ScopeEntry) = synchronized { super.unlink(e) } - override def unlink(sym: Symbol) = synchronized { super.unlink(sym) } - override def lookupAll(name: Name) = synchronized { super.lookupAll(name) } - override def lookupEntry(name: Name) = synchronized { super.lookupEntry(name) } - override def lookupNextEntry(entry: ScopeEntry) = synchronized { super.lookupNextEntry(entry) } - override def toList: List[Symbol] = synchronized { super.toList } + // we can keep this lock fine-grained, because methods of Scope don't do anything extraordinary, which makes deadlocks impossible + // fancy subclasses of internal.Scopes#Scope should do synchronization themselves (e.g. see PackageScope for an example) + private lazy val syncLock = new Object + def syncLockSynchronized[T](body: => T): T = syncLock.synchronized { body } + override def isEmpty: Boolean = syncLockSynchronized { super.isEmpty } + override def size: Int = syncLockSynchronized { super.size } + override def enter[T <: Symbol](sym: T): T = syncLockSynchronized { super.enter(sym) } + override def rehash(sym: Symbol, newname: Name) = syncLockSynchronized { super.rehash(sym, newname) } + override def unlink(e: ScopeEntry) = syncLockSynchronized { super.unlink(e) } + override def unlink(sym: Symbol) = syncLockSynchronized { super.unlink(sym) } + override def lookupAll(name: Name) = syncLockSynchronized { super.lookupAll(name) } + override def lookupEntry(name: Name) = syncLockSynchronized { super.lookupEntry(name) } + override def lookupNextEntry(entry: ScopeEntry) = syncLockSynchronized { super.lookupNextEntry(entry) } + override def toList: List[Symbol] = syncLockSynchronized { super.toList } } } diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala index c0b4261d11..a52921c5c6 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala @@ -6,14 +6,18 @@ import scala.reflect.io.AbstractFile private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: SymbolTable => - override protected def nextId() = synchronized { super.nextId() } + // we can keep this lock fine-grained, because nextId is just a simple increment, which makes deadlocks impossible + private lazy val nextIdLock = new Object + override protected def nextId() = nextIdLock.synchronized { super.nextId() } + // we can keep this lock fine-grained, because freshExistentialName is just a simple increment, which makes deadlocks impossible + private lazy val freshExistentialNameLock = new Object override protected def freshExistentialName(suffix: String) = - synchronized { super.freshExistentialName(suffix) } + freshExistentialNameLock.synchronized { super.freshExistentialName(suffix) } // Set the fields which point companions at one another. Returns the module. override def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = - synchronized { super.connectModuleToClass(m, moduleClass) } + gilSynchronized { super.connectModuleToClass(m, moduleClass) } override def newFreeTermSymbol(name: TermName, value: => Any, flags: Long = 0L, origin: String = null): FreeTermSymbol = new FreeTermSymbol(name, value, origin) with SynchronizedTermSymbol initFlags flags @@ -25,27 +29,27 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb trait SynchronizedSymbol extends Symbol { - override def rawflags = synchronized { super.rawflags } - override def rawflags_=(x: Long) = synchronized { super.rawflags_=(x) } + override def rawflags = gilSynchronized { super.rawflags } + override def rawflags_=(x: Long) = gilSynchronized { super.rawflags_=(x) } - override def rawowner = synchronized { super.rawowner } - override def owner_=(owner: Symbol) = synchronized { super.owner_=(owner) } + override def rawowner = gilSynchronized { super.rawowner } + override def owner_=(owner: Symbol) = gilSynchronized { super.owner_=(owner) } - override def validTo = synchronized { super.validTo } - override def validTo_=(x: Period) = synchronized { super.validTo_=(x) } + override def validTo = gilSynchronized { super.validTo } + override def validTo_=(x: Period) = gilSynchronized { super.validTo_=(x) } - override def pos = synchronized { super.pos } - override def setPos(pos: Position): this.type = { synchronized { super.setPos(pos) }; this } + override def pos = gilSynchronized { super.pos } + override def setPos(pos: Position): this.type = { gilSynchronized { super.setPos(pos) }; this } - override def privateWithin = synchronized { super.privateWithin } - override def privateWithin_=(sym: Symbol) = synchronized { super.privateWithin_=(sym) } + override def privateWithin = gilSynchronized { super.privateWithin } + override def privateWithin_=(sym: Symbol) = gilSynchronized { super.privateWithin_=(sym) } - override def info = synchronized { super.info } - override def info_=(info: Type) = synchronized { super.info_=(info) } - override def updateInfo(info: Type): Symbol = synchronized { super.updateInfo(info) } - override def rawInfo: Type = synchronized { super.rawInfo } + override def info = gilSynchronized { super.info } + override def info_=(info: Type) = gilSynchronized { super.info_=(info) } + override def updateInfo(info: Type): Symbol = gilSynchronized { super.updateInfo(info) } + override def rawInfo: Type = gilSynchronized { super.rawInfo } - override def typeParams: List[Symbol] = synchronized { + override def typeParams: List[Symbol] = gilSynchronized { if (isCompilerUniverse) super.typeParams else { if (isMonomorphicType) Nil @@ -61,7 +65,7 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb } } } - override def unsafeTypeParams: List[Symbol] = synchronized { + override def unsafeTypeParams: List[Symbol] = gilSynchronized { if (isCompilerUniverse) super.unsafeTypeParams else { if (isMonomorphicType) Nil @@ -69,12 +73,12 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb } } - override def reset(completer: Type): this.type = synchronized { super.reset(completer) } + override def reset(completer: Type): this.type = gilSynchronized { super.reset(completer) } - override def infosString: String = synchronized { super.infosString } + override def infosString: String = gilSynchronized { super.infosString } - override def annotations: List[AnnotationInfo] = synchronized { super.annotations } - override def setAnnotations(annots: List[AnnotationInfo]): this.type = { synchronized { super.setAnnotations(annots) }; this } + override def annotations: List[AnnotationInfo] = gilSynchronized { super.annotations } + override def setAnnotations(annots: List[AnnotationInfo]): this.type = { gilSynchronized { super.setAnnotations(annots) }; this } // ------ creators ------------------------------------------------------------------- @@ -125,40 +129,40 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb // ------- subclasses --------------------------------------------------------------------- trait SynchronizedTermSymbol extends TermSymbol with SynchronizedSymbol { - override def name_=(x: Name) = synchronized { super.name_=(x) } - override def rawname = synchronized { super.rawname } - override def referenced: Symbol = synchronized { super.referenced } - override def referenced_=(x: Symbol) = synchronized { super.referenced_=(x) } + override def name_=(x: Name) = gilSynchronized { super.name_=(x) } + override def rawname = gilSynchronized { super.rawname } + override def referenced: Symbol = gilSynchronized { super.referenced } + override def referenced_=(x: Symbol) = gilSynchronized { super.referenced_=(x) } } trait SynchronizedMethodSymbol extends MethodSymbol with SynchronizedTermSymbol { - override def typeAsMemberOf(pre: Type): Type = synchronized { super.typeAsMemberOf(pre) } - override def paramss: List[List[Symbol]] = synchronized { super.paramss } - override def returnType: Type = synchronized { super.returnType } + override def typeAsMemberOf(pre: Type): Type = gilSynchronized { super.typeAsMemberOf(pre) } + override def paramss: List[List[Symbol]] = gilSynchronized { super.paramss } + override def returnType: Type = gilSynchronized { super.returnType } } trait SynchronizedTypeSymbol extends TypeSymbol with SynchronizedSymbol { - override def name_=(x: Name) = synchronized { super.name_=(x) } - override def rawname = synchronized { super.rawname } - override def typeConstructor: Type = synchronized { super.typeConstructor } - override def tpe_* : Type = synchronized { super.tpe_* } - override def tpeHK : Type = synchronized { super.tpeHK } + override def name_=(x: Name) = gilSynchronized { super.name_=(x) } + override def rawname = gilSynchronized { super.rawname } + override def typeConstructor: Type = gilSynchronized { super.typeConstructor } + override def tpe_* : Type = gilSynchronized { super.tpe_* } + override def tpeHK : Type = gilSynchronized { super.tpeHK } } trait SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol { - override def associatedFile = synchronized { super.associatedFile } - override def associatedFile_=(f: AbstractFile) = synchronized { super.associatedFile_=(f) } - override def thisSym: Symbol = synchronized { super.thisSym } - override def thisType: Type = synchronized { super.thisType } - override def typeOfThis: Type = synchronized { super.typeOfThis } - override def typeOfThis_=(tp: Type) = synchronized { super.typeOfThis_=(tp) } - override def children = synchronized { super.children } - override def addChild(sym: Symbol) = synchronized { super.addChild(sym) } + override def associatedFile = gilSynchronized { super.associatedFile } + override def associatedFile_=(f: AbstractFile) = gilSynchronized { super.associatedFile_=(f) } + override def thisSym: Symbol = gilSynchronized { super.thisSym } + override def thisType: Type = gilSynchronized { super.thisType } + override def typeOfThis: Type = gilSynchronized { super.typeOfThis } + override def typeOfThis_=(tp: Type) = gilSynchronized { super.typeOfThis_=(tp) } + override def children = gilSynchronized { super.children } + override def addChild(sym: Symbol) = gilSynchronized { super.addChild(sym) } } trait SynchronizedModuleClassSymbol extends ModuleClassSymbol with SynchronizedClassSymbol { - override def sourceModule = synchronized { super.sourceModule } - override def implicitMembers: Scope = synchronized { super.implicitMembers } + override def sourceModule = gilSynchronized { super.sourceModule } + override def implicitMembers: Scope = gilSynchronized { super.implicitMembers } } } diff --git a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala index c0146167df..4ea2d703c4 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala @@ -14,8 +14,9 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa // No sharing of map objects: override protected def commonOwnerMap = new CommonOwnerMap - private object uniqueLock - + // we can keep this lock fine-grained, because super.unique just updates the cache + // and, in particular, doesn't call any reflection APIs which makes deadlocks impossible + private lazy val uniqueLock = new Object private val uniques = WeakHashMap[Type, WeakReference[Type]]() override def unique[T <: Type](tp: T): T = uniqueLock.synchronized { // we need to have weak uniques for runtime reflection @@ -39,46 +40,35 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa } class SynchronizedUndoLog extends UndoLog { - private val actualLock = new java.util.concurrent.locks.ReentrantLock - - final override def lock(): Unit = actualLock.lock() - final override def unlock(): Unit = actualLock.unlock() + final override def lock(): Unit = gil.lock() + final override def unlock(): Unit = gil.unlock() } override protected def newUndoLog = new SynchronizedUndoLog override protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = - synchronized { super.baseTypeOfNonClassTypeRef(tpe, clazz) } - - private object subsametypeLock + gilSynchronized { super.baseTypeOfNonClassTypeRef(tpe, clazz) } override def isSameType(tp1: Type, tp2: Type): Boolean = - subsametypeLock.synchronized { super.isSameType(tp1, tp2) } + gilSynchronized { super.isSameType(tp1, tp2) } override def isDifferentType(tp1: Type, tp2: Type): Boolean = - subsametypeLock.synchronized { super.isDifferentType(tp1, tp2) } + gilSynchronized { super.isDifferentType(tp1, tp2) } override def isSubType(tp1: Type, tp2: Type, depth: Depth): Boolean = - subsametypeLock.synchronized { super.isSubType(tp1, tp2, depth) } - - private object lubglbLock + gilSynchronized { super.isSubType(tp1, tp2, depth) } override def glb(ts: List[Type]): Type = - lubglbLock.synchronized { super.glb(ts) } + gilSynchronized { super.glb(ts) } override def lub(ts: List[Type]): Type = - lubglbLock.synchronized { super.lub(ts) } - - private object indentLock - - override protected def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = { - indentLock.synchronized { super.explain(op, p, tp1, arg2) } - } + gilSynchronized { super.lub(ts) } - private object toStringLock + override protected def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = + gilSynchronized { super.explain(op, p, tp1, arg2) } override protected def typeToString(tpe: Type): String = - toStringLock.synchronized(super.typeToString(tpe)) + gilSynchronized(super.typeToString(tpe)) /* The idea of caches is as follows. * When in reflexive mode, a cache is either null, or one sentinal @@ -91,18 +81,18 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa */ override protected def defineUnderlyingOfSingleType(tpe: SingleType) = - tpe.synchronized { super.defineUnderlyingOfSingleType(tpe) } + gilSynchronized { super.defineUnderlyingOfSingleType(tpe) } override protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) = - tpe.synchronized { super.defineBaseTypeSeqOfCompoundType(tpe) } + gilSynchronized { super.defineBaseTypeSeqOfCompoundType(tpe) } override protected def defineBaseClassesOfCompoundType(tpe: CompoundType) = - tpe.synchronized { super.defineBaseClassesOfCompoundType(tpe) } + gilSynchronized { super.defineBaseClassesOfCompoundType(tpe) } override protected def defineParentsOfTypeRef(tpe: TypeRef) = - tpe.synchronized { super.defineParentsOfTypeRef(tpe) } + gilSynchronized { super.defineParentsOfTypeRef(tpe) } override protected def defineBaseTypeSeqOfTypeRef(tpe: TypeRef) = - tpe.synchronized { super.defineBaseTypeSeqOfTypeRef(tpe) } + gilSynchronized { super.defineBaseTypeSeqOfTypeRef(tpe) } } diff --git a/src/reflect/scala/reflect/runtime/TwoWayCaches.scala b/src/reflect/scala/reflect/runtime/TwoWayCaches.scala new file mode 100644 index 0000000000..6e2890e536 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/TwoWayCaches.scala @@ -0,0 +1,68 @@ +package scala.reflect +package runtime + +import scala.collection.mutable.WeakHashMap +import java.lang.ref.WeakReference + +/** A cache that maintains a bijection between Java reflection type `J` + * and Scala reflection type `S`. + * + * The cache is two-way weak (i.e. is powered by weak references), + * so that neither Java artifacts prevent Scala artifacts from being garbage collected, + * nor the other way around. + */ +private[runtime] trait TwoWayCaches { self: SymbolTable => + class TwoWayCache[J, S] { + + private val toScalaMap = new WeakHashMap[J, WeakReference[S]] + private val toJavaMap = new WeakHashMap[S, WeakReference[J]] + + def enter(j: J, s: S) = gilSynchronized { + // debugInfo("cached: "+j+"/"+s) + toScalaMap(j) = new WeakReference(s) + toJavaMap(s) = new WeakReference(j) + } + + private object SomeRef { + def unapply[T](optRef: Option[WeakReference[T]]): Option[T] = + if (optRef.nonEmpty) { + val result = optRef.get.get + if (result != null) Some(result) else None + } else None + } + + def toScala(key: J)(body: => S): S = gilSynchronized { + toScalaMap get key match { + case SomeRef(v) => + v + case _ => + val result = body + enter(key, result) + result + } + } + + def toJava(key: S)(body: => J): J = gilSynchronized { + toJavaMap get key match { + case SomeRef(v) => + v + case _ => + val result = body + enter(result, key) + result + } + } + + def toJavaOption(key: S)(body: => Option[J]): Option[J] = gilSynchronized { + toJavaMap get key match { + case SomeRef(v) => + Some(v) + case _ => + val result = body + for (value <- result) enter(value, key) + result + } + } + } +} + -- cgit v1.2.3