diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2013-02-04 12:11:20 +0100 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2013-10-18 17:48:52 +0200 |
commit | 37f4e9ce3a05a5821d2a5e20af28f34355d12dfb (patch) | |
tree | 6885c3ce8cb22efda0cd5ffdc2285158f8b377c7 /src/reflect/scala/reflect/runtime | |
parent | 174f1465335bead724da6bf2ae823830422dd51c (diff) | |
download | scala-37f4e9ce3a05a5821d2a5e20af28f34355d12dfb.tar.gz scala-37f4e9ce3a05a5821d2a5e20af28f34355d12dfb.tar.bz2 scala-37f4e9ce3a05a5821d2a5e20af28f34355d12dfb.zip |
thread locals instead of locks for vars in Types.scala
This is one of the changes suggested by Roland in order to reduce contention
caused by reflection GIL.
Locks optimized away here are indirectly used for such fundamental operations
as subtyping tests, so the optimization looks quite important.
Diffstat (limited to 'src/reflect/scala/reflect/runtime')
4 files changed, 83 insertions, 27 deletions
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 70126e713a..5f004330c3 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -29,6 +29,19 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.treeInfo this.gil // inaccessible: this.uniqueLock + // inaccessible: this._skolemizationLevel + // inaccessible: this._undoLog + // inaccessible: this._intersectionWitness + // inaccessible: this._volatileRecursions + // inaccessible: this._pendingVolatiles + // inaccessible: this._subsametypeRecursions + // inaccessible: this._pendingSubTypes + // inaccessible: this._basetypeRecursions + // inaccessible: this._pendingBaseTypes + // inaccessible: this._lubResults + // inaccessible: this._glbResults + // inaccessible: this._indent + // inaccessible: this._tostringRecursions // inaccessible: this.nextIdLock // inaccessible: this.freshExistentialNameLock // inaccessible: this.mirrors @@ -166,7 +179,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.unwrapToStableClass this.unwrapWrapperTypes this.RecoverableCyclicReference - this.undoLog + // inaccessible: this._undoLog // inaccessible: this.numericLoBound // inaccessible: this.numericHiBound this.TypeConstraint diff --git a/src/reflect/scala/reflect/runtime/SymbolTable.scala b/src/reflect/scala/reflect/runtime/SymbolTable.scala index 7630fffd16..ddbf3bd629 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 with Gil { +private[scala] trait SymbolTable extends internal.SymbolTable with JavaMirrors with SymbolLoaders with SynchronizedOps with Gil with ThreadLocalStorage { def info(msg: => String) = if (settings.verbose) println("[reflect-compiler] "+msg) diff --git a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala index 4ea2d703c4..de78e527a7 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala @@ -2,8 +2,9 @@ package scala package reflect package runtime -import scala.collection.mutable.WeakHashMap -import java.lang.ref.WeakReference +import scala.collection.mutable +import java.lang.ref.{WeakReference => jWeakRef} +import scala.ref.{WeakReference => sWeakRef} import scala.reflect.internal.Depth /** This trait overrides methods in reflect.internal, bracketing @@ -17,7 +18,7 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa // 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]]() + private val uniques = mutable.WeakHashMap[Type, jWeakRef[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 @@ -31,7 +32,7 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa val result = if (inCache.isDefined) inCache.get.get else null if (result ne null) result.asInstanceOf[T] else { - uniques(tp) = new WeakReference(tp) + uniques(tp) = new jWeakRef(tp) tp } } else { @@ -39,36 +40,50 @@ private[reflect] trait SynchronizedTypes extends internal.Types { self: SymbolTa } } - class SynchronizedUndoLog extends UndoLog { - final override def lock(): Unit = gil.lock() - final override def unlock(): Unit = gil.unlock() - } + private lazy val _skolemizationLevel = mkThreadLocalStorage(0) + override def skolemizationLevel = _skolemizationLevel.get + override def skolemizationLevel_=(value: Int) = _skolemizationLevel.set(value) + + private lazy val _undoLog = mkThreadLocalStorage(new UndoLog) + override def undoLog = _undoLog.get + + private lazy val _intersectionWitness = mkThreadLocalStorage(perRunCaches.newWeakMap[List[Type], sWeakRef[Type]]()) + override def intersectionWitness = _intersectionWitness.get + + private lazy val _volatileRecursions = mkThreadLocalStorage(0) + override def volatileRecursions = _volatileRecursions.get + override def volatileRecursions_=(value: Int) = _volatileRecursions.set(value) - override protected def newUndoLog = new SynchronizedUndoLog + private lazy val _pendingVolatiles = mkThreadLocalStorage(new mutable.HashSet[Symbol]) + override def pendingVolatiles = _pendingVolatiles.get - override protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = - gilSynchronized { super.baseTypeOfNonClassTypeRef(tpe, clazz) } + private lazy val _subsametypeRecursions = mkThreadLocalStorage(0) + override def subsametypeRecursions = _subsametypeRecursions.get + override def subsametypeRecursions_=(value: Int) = _subsametypeRecursions.set(value) - override def isSameType(tp1: Type, tp2: Type): Boolean = - gilSynchronized { super.isSameType(tp1, tp2) } + private lazy val _pendingSubTypes = mkThreadLocalStorage(new mutable.HashSet[SubTypePair]) + override def pendingSubTypes = _pendingSubTypes.get - override def isDifferentType(tp1: Type, tp2: Type): Boolean = - gilSynchronized { super.isDifferentType(tp1, tp2) } + private lazy val _basetypeRecursions = mkThreadLocalStorage(0) + override def basetypeRecursions = _basetypeRecursions.get + override def basetypeRecursions_=(value: Int) = _basetypeRecursions.set(value) - override def isSubType(tp1: Type, tp2: Type, depth: Depth): Boolean = - gilSynchronized { super.isSubType(tp1, tp2, depth) } + private lazy val _pendingBaseTypes = mkThreadLocalStorage(new mutable.HashSet[Type]) + override def pendingBaseTypes = _pendingBaseTypes.get - override def glb(ts: List[Type]): Type = - gilSynchronized { super.glb(ts) } + private lazy val _lubResults = mkThreadLocalStorage(new mutable.HashMap[(Depth, List[Type]), Type]) + override def lubResults = _lubResults.get - override def lub(ts: List[Type]): Type = - gilSynchronized { super.lub(ts) } + private lazy val _glbResults = mkThreadLocalStorage(new mutable.HashMap[(Depth, List[Type]), Type]) + override def glbResults = _glbResults.get - override protected def explain[T](op: String, p: (Type, T) => Boolean, tp1: Type, arg2: T): Boolean = - gilSynchronized { super.explain(op, p, tp1, arg2) } + private lazy val _indent = mkThreadLocalStorage("") + override def indent = _indent.get + override def indent_=(value: String) = _indent.set(value) - override protected def typeToString(tpe: Type): String = - gilSynchronized(super.typeToString(tpe)) + private lazy val _tostringRecursions = mkThreadLocalStorage(0) + override def tostringRecursions = _tostringRecursions.get + override def tostringRecursions_=(value: Int) = _tostringRecursions.set(value) /* The idea of caches is as follows. * When in reflexive mode, a cache is either null, or one sentinal diff --git a/src/reflect/scala/reflect/runtime/ThreadLocalStorage.scala b/src/reflect/scala/reflect/runtime/ThreadLocalStorage.scala new file mode 100644 index 0000000000..5edc051461 --- /dev/null +++ b/src/reflect/scala/reflect/runtime/ThreadLocalStorage.scala @@ -0,0 +1,28 @@ +package scala.reflect +package runtime + +import java.lang.Thread._ + +private[reflect] trait ThreadLocalStorage { + self: SymbolTable => + + // see a discussion at scala-internals for more information: + // http://groups.google.com/group/scala-internals/browse_thread/thread/337ce68aa5e51f79 + trait ThreadLocalStorage[T] { def get: T; def set(newValue: T): Unit } + private class MyThreadLocalStorage[T](initialValue: => T) extends ThreadLocalStorage[T] { + // TODO: how do we use org.cliffc.high_scale_lib.NonBlockingHashMap here? + val values = new java.util.concurrent.ConcurrentHashMap[Thread, T]() + def get: T = { + if (values containsKey currentThread) values.get(currentThread) + else { + val value = initialValue + values.putIfAbsent(currentThread, value) + value + } + } + def set(newValue: T): Unit = { + values.put(currentThread, newValue) + } + } + @inline final def mkThreadLocalStorage[T](x: => T): ThreadLocalStorage[T] = new MyThreadLocalStorage(x) +} |