Skip to content

Commit 735634f

Browse files
committed
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.
1 parent 5b37cfb commit 735634f

File tree

5 files changed

+1041
-5
lines changed

5 files changed

+1041
-5
lines changed

src/reflect/scala/reflect/internal/Definitions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1189,7 +1189,7 @@ trait Definitions extends api.StandardDefinitions {
11891189
/** Is the symbol that of a parent which is added during parsing? */
11901190
lazy val isPossibleSyntheticParent = ProductClass.toSet[Symbol] + ProductRootClass + SerializableClass
11911191

1192-
private lazy val boxedValueClassesSet = boxedClass.values.toSet[Symbol] + BoxedUnitClass
1192+
lazy val boxedValueClassesSet = boxedClass.values.toSet[Symbol] + BoxedUnitClass
11931193

11941194
/** Is symbol a value class? */
11951195
def isPrimitiveValueClass(sym: Symbol) = ScalaValueClasses contains sym

src/reflect/scala/reflect/internal/Symbols.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
108108
def setter: Symbol = setter(owner)
109109
}
110110

111-
private[Symbols] case class SymbolKind(accurate: String, sanitized: String, abbreviation: String)
111+
case class SymbolKind(accurate: String, sanitized: String, abbreviation: String)
112112

113113
/** The class for all symbols */
114114
abstract class Symbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: Name)

src/reflect/scala/reflect/internal/Types.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3930,8 +3930,8 @@ trait Types extends api.Types { self: SymbolTable =>
39303930
/** @PP: Unable to see why these apparently constant types should need vals
39313931
* in every TypeConstraint, I lifted them out.
39323932
*/
3933-
private lazy val numericLoBound = IntClass.tpe
3934-
private lazy val numericHiBound = intersectionType(List(ByteClass.tpe, CharClass.tpe), ScalaPackageClass)
3933+
lazy val numericLoBound = IntClass.tpe
3934+
lazy val numericHiBound = intersectionType(List(ByteClass.tpe, CharClass.tpe), ScalaPackageClass)
39353935

39363936
/** A class expressing upper and lower bounds constraints of type variables,
39373937
* as well as their instantiations.

src/reflect/scala/reflect/runtime/JavaMirrors.scala

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import scala.reflect.internal.util.Collections._
2626

2727
private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse with TwoWayCaches { thisUniverse: SymbolTable =>
2828

29-
private lazy val mirrors = new WeakHashMap[ClassLoader, WeakReference[JavaMirror]]()
29+
lazy val mirrors = new WeakHashMap[ClassLoader, WeakReference[JavaMirror]]()
3030

3131
private def createMirror(owner: Symbol, cl: ClassLoader): Mirror = {
3232
val jm = new JavaMirror(owner, cl)
@@ -68,6 +68,18 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
6868
override lazy val EmptyPackage = new EmptyPackage with SynchronizedTermSymbol
6969
override lazy val EmptyPackageClass = new EmptyPackageClass with SynchronizedModuleClassSymbol
7070

71+
override def init() = {
72+
super.init()
73+
74+
// see an explanation of this in JavaUniverse.init()
75+
RootPackage
76+
RootClass
77+
EmptyPackage
78+
EmptyPackageClass
79+
unpickler
80+
rootLoader
81+
}
82+
7183
/** The lazy type for root.
7284
*/
7385
override lazy val rootLoader = new LazyType with FlagAgnosticCompleter {

0 commit comments

Comments
 (0)