package scala.reflect
package runtime
import scala.collection.mutable.WeakHashMap
import java.lang.ref.WeakReference
/** This trait overrides methods in reflect.internal, bracketing
* them in synchronized { ... } to make them thread-safe
*/
trait SynchronizedTypes extends internal.Types { self: SymbolTable =>
// No sharing of map objects:
override protected def commonOwnerMap = new CommonOwnerMap
private object uniqueLock
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
// because unlike the normal compiler universe, reflective universe isn't organized in runs
// therefore perRunCaches can grow infinitely large
//
// despite that toolbox universes are decorated, toolboxes are compilers,
// i.e. they have their caches cleaned up automatically on per-run basis,
// therefore they should use vanilla uniques, which are faster
if (!isCompilerUniverse) {
val inCache = uniques get tp
val result = if (inCache.isDefined) inCache.get.get else null
if (result ne null) result.asInstanceOf[T]
else {
uniques(tp) = new WeakReference(tp)
tp
}
} else {
super.unique(tp)
}
}
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()
}
override protected def newUndoLog = new SynchronizedUndoLog
override protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) =
synchronized { super.baseTypeOfNonClassTypeRef(tpe, clazz) }
private object subsametypeLock
override def isSameType(tp1: Type, tp2: Type): Boolean =
subsametypeLock.synchronized { super.isSameType(tp1, tp2) }
override def isDifferentType(tp1: Type, tp2: Type): Boolean =
subsametypeLock.synchronized { super.isDifferentType(tp1, tp2) }
override def isSubType(tp1: Type, tp2: Type, depth: Int): Boolean =
subsametypeLock.synchronized { super.isSubType(tp1, tp2, depth) }
private object lubglbLock
override def glb(ts: List[Type]): Type =
lubglbLock.synchronized { 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) }
}
private object toStringLock
override protected def typeToString(tpe: Type): String =
toStringLock.synchronized(super.typeToString(tpe))
/* The idea of caches is as follows.
* When in reflexive mode, a cache is either null, or one sentinal
* value representing undefined or the final defined
* value. Hence, we can ask in non-synchronized ode whether the cache field
* is non null and different from the sentinel (if a sentinel exists).
* If that's true, the cache value is current.
* Otherwise we arrive in one of the defined... methods listed below
* which go through all steps in synchronized mode.
*/
override protected def defineUnderlyingOfSingleType(tpe: SingleType) =
tpe.synchronized { super.defineUnderlyingOfSingleType(tpe) }
override protected def defineBaseTypeSeqOfCompoundType(tpe: CompoundType) =
tpe.synchronized { super.defineBaseTypeSeqOfCompoundType(tpe) }
override protected def defineBaseClassesOfCompoundType(tpe: CompoundType) =
tpe.synchronized { super.defineBaseClassesOfCompoundType(tpe) }
override protected def defineParentsOfTypeRef(tpe: TypeRef) =
tpe.synchronized { super.defineParentsOfTypeRef(tpe) }
override protected def defineBaseTypeSeqOfTypeRef(tpe: TypeRef) =
tpe.synchronized { super.defineBaseTypeSeqOfTypeRef(tpe) }
}