Permalink
Browse files

initializes lazy vals and inner objects in advance

As discussed at http://groups.google.com/group/scala-internals/browse_thread/thread/97840ba4fd37b52e,
`synchronized(this)` employed by lazy val and inner object initialization
is an excellent way to deadlock yourself in the foot.

Imagine a thread, which grabs a reflection GIL and then calls one of those
lazy vals / objects that reflection exposes (e.g. a companion module of
an innocently looking SingleType case class). Then imagine another thread,
which calls something else in SymbolTable, grabbing symbol table's monitor,
and then tries to get a reflection GIL to do something non-trivial. Hello,
we've just arrived at a deadlock.

Since, as discussed in the aforementioned thread, there's no easy way to
change lazy vals / inner objects in reflection to use GIL instead of
synchronizing on this, I bit the bullet and manually initialized all
things with deferred initialization defined in reflect.runtime.SymbolTable.

The list of all things `$lzycompute` has been mined by a simple Python script,
then I copy/pasted that list into `JavaUniverse.scala` and went ahead forcing
objects and lazy vals mentioned there. Notably, I've been able to force all
lazy vals in Definitions.scala.

There are some todos left, but I suggest we move forward without securing them,
because the 2.10.1-RC1 release date is very close, so we'd better have a 95%
solution instead of keeping reflection thread-unsafe. Though here's the list of
todo lazy vals for the reference:

  * BaseTypeSeq.maxDepth
  * WeakTypeTag.tpe
  * AnnotationInfo.forcedInfo

For each of those lazy vals we need to make sure that their initializers never
call back into themselves. Otherwise, there's a danger of a deadlock.
  • Loading branch information...
1 parent 5b37cfb commit 735634f1d634d2d6a2f8ed44f2e6185adc46c695 @xeno-by xeno-by committed Feb 7, 2013
@@ -1189,7 +1189,7 @@ trait Definitions extends api.StandardDefinitions {
/** Is the symbol that of a parent which is added during parsing? */
lazy val isPossibleSyntheticParent = ProductClass.toSet[Symbol] + ProductRootClass + SerializableClass
- private lazy val boxedValueClassesSet = boxedClass.values.toSet[Symbol] + BoxedUnitClass
+ lazy val boxedValueClassesSet = boxedClass.values.toSet[Symbol] + BoxedUnitClass
/** Is symbol a value class? */
def isPrimitiveValueClass(sym: Symbol) = ScalaValueClasses contains sym
@@ -108,7 +108,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def setter: Symbol = setter(owner)
}
- private[Symbols] case class SymbolKind(accurate: String, sanitized: String, abbreviation: String)
+ case class SymbolKind(accurate: String, sanitized: String, abbreviation: String)
/** The class for all symbols */
abstract class Symbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: Name)
@@ -3930,8 +3930,8 @@ trait Types extends api.Types { self: SymbolTable =>
/** @PP: Unable to see why these apparently constant types should need vals
* in every TypeConstraint, I lifted them out.
*/
- private lazy val numericLoBound = IntClass.tpe
- private lazy val numericHiBound = intersectionType(List(ByteClass.tpe, CharClass.tpe), ScalaPackageClass)
+ lazy val numericLoBound = IntClass.tpe
+ lazy val numericHiBound = intersectionType(List(ByteClass.tpe, CharClass.tpe), ScalaPackageClass)
/** A class expressing upper and lower bounds constraints of type variables,
* as well as their instantiations.
@@ -26,7 +26,7 @@ import scala.reflect.internal.util.Collections._
private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse with TwoWayCaches { thisUniverse: SymbolTable =>
- private lazy val mirrors = new WeakHashMap[ClassLoader, WeakReference[JavaMirror]]()
+ lazy val mirrors = new WeakHashMap[ClassLoader, WeakReference[JavaMirror]]()
private def createMirror(owner: Symbol, cl: ClassLoader): Mirror = {
val jm = new JavaMirror(owner, cl)
@@ -68,6 +68,18 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
override lazy val EmptyPackage = new EmptyPackage with SynchronizedTermSymbol
override lazy val EmptyPackageClass = new EmptyPackageClass with SynchronizedModuleClassSymbol
+ override def init() = {
+ super.init()
+
+ // see an explanation of this in JavaUniverse.init()
+ RootPackage
+ RootClass
+ EmptyPackage
+ EmptyPackageClass
+ unpickler
+ rootLoader
+ }
+
/** The lazy type for root.
*/
override lazy val rootLoader = new LazyType with FlagAgnosticCompleter {
Oops, something went wrong.

1 comment on commit 735634f

Job pr-rangepos-per-commit failed for 735634f (results):


Took 15 s.
sad kitty
to rebuild, comment "PLS REBUILD/pr-rangepos-per-commit@735634f"on PR #2083

Please sign in to comment.