diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2013-02-02 00:27:38 +0100 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2013-10-18 17:48:51 +0200 |
commit | d05566ca327103d61b071a5022be98f1f3f045f3 (patch) | |
tree | 6023560ed3c69d52996bf16c652b3baf7247ed63 | |
parent | 484d6d70a4c51d0ddf220c67265182949e69ca45 (diff) | |
download | scala-d05566ca327103d61b071a5022be98f1f3f045f3.tar.gz scala-d05566ca327103d61b071a5022be98f1f3f045f3.tar.bz2 scala-d05566ca327103d61b071a5022be98f1f3f045f3.zip |
optimizes Scala reflection GIL
First of all, GIL should only apply to runtime reflection, because noone
is going to run toolboxes in multiple threads: a) that's impossible, b/c
the compiler isn't thread safe, b) ToolBox api prevents that.
Secondly, the only things in symbols which require synchronization are:
1) info/validTo (completers aren't thread-safe),
2) rawInfo and its dependencies (it shares a mutable field with info)
3) non-trivial caches like in typeAsMemberOfLock
If you think about it, other things like sourceModule or associatedFile
don't need synchronization, because they are either set up when a symbol
is created or cloned or when it's completed. The former is obviously safe,
while the latter is safe as well, because before acquiring init-dependent
state of symbols, the compiler calls `initialize`, which is synchronized.
We can say that symbols can be in four possible states: 1) being created,
2) created, but not yet initialized, 3) initializing, 4) initialized.
Of those only #3 is dangerous and needs protection, which is what this
commit does.
5 files changed, 41 insertions, 65 deletions
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index a7abe08c2e..a54a17901a 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -951,6 +951,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isInitialized: Boolean = validTo != NoPeriod + /** Some completers call sym.setInfo when still in-flight and then proceed with initialization (e.g. see LazyPackageType) + * setInfo sets _validTo to current period, which means that after a call to setInfo isInitialized will start returning true. + * Unfortunately, this doesn't mean that info becomes ready to be used, because subsequent initialization might change the info. + * Therefore we need this method to distinguish between initialized and really initialized symbol states. + */ + final def isFullyInitialized: Boolean = _validTo != NoPeriod && (flags & LOCKED) == 0 + /** Can this symbol be loaded by a reflective mirror? * * Scalac relies on `ScalaSignature' annotation to retain symbols across compilation runs. @@ -3129,8 +3136,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def thisType: Type = { val period = thisTypePeriod if (period != currentPeriod) { - thisTypePeriod = currentPeriod if (!isValid(period)) thisTypeCache = ThisType(this) + thisTypePeriod = currentPeriod } thisTypeCache } @@ -3218,9 +3225,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def typeOfThis = { val period = typeOfThisPeriod if (period != currentPeriod) { - typeOfThisPeriod = currentPeriod if (!isValid(period)) typeOfThisCache = singleType(owner.thisType, sourceModule) + typeOfThisPeriod = currentPeriod } typeOfThisCache } @@ -3231,9 +3238,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => // Skip a package object class, because the members are also in // the package and we wish to avoid spurious ambiguities as in pos/t3999. if (!isPackageObjectClass) { + implicitMembersCacheValue = tp.implicitMembers implicitMembersCacheKey1 = tp implicitMembersCacheKey2 = tp.decls.elems - implicitMembersCacheValue = tp.implicitMembers } } implicitMembersCacheValue diff --git a/src/reflect/scala/reflect/runtime/Gil.scala b/src/reflect/scala/reflect/runtime/Gil.scala index 8a8bfebf8c..cf6f1431d7 100644 --- a/src/reflect/scala/reflect/runtime/Gil.scala +++ b/src/reflect/scala/reflect/runtime/Gil.scala @@ -12,11 +12,14 @@ private[reflect] trait Gil { lazy val gil = new java.util.concurrent.locks.ReentrantLock @inline final def gilSynchronized[T](body: => T): T = { - try { - gil.lock() - body - } finally { - gil.unlock() + if (isCompilerUniverse) body + else { + try { + gil.lock() + body + } finally { + gil.unlock() + } } } } diff --git a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala index 21df973297..c90901410a 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala @@ -44,7 +44,7 @@ private[reflect] trait SynchronizedOps extends internal.SymbolTable // 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 } + def syncLockSynchronized[T](body: => T): T = if (isCompilerUniverse) body else 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) } diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala index a52921c5c6..7f08e8fbe2 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala @@ -29,27 +29,16 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb trait SynchronizedSymbol extends Symbol { - override def rawflags = gilSynchronized { super.rawflags } - override def rawflags_=(x: Long) = gilSynchronized { super.rawflags_=(x) } - - override def rawowner = gilSynchronized { super.rawowner } - override def owner_=(owner: Symbol) = gilSynchronized { super.owner_=(owner) } - - override def validTo = gilSynchronized { super.validTo } - override def validTo_=(x: Period) = gilSynchronized { super.validTo_=(x) } - - override def pos = gilSynchronized { super.pos } - override def setPos(pos: Position): this.type = { gilSynchronized { super.setPos(pos) }; this } - - override def privateWithin = gilSynchronized { super.privateWithin } - override def privateWithin_=(sym: Symbol) = gilSynchronized { super.privateWithin_=(sym) } + def gilSynchronizedIfNotInited[T](body: => T): T = { + if (isFullyInitialized) body + else gilSynchronized { body } + } - 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 validTo = gilSynchronizedIfNotInited { super.validTo } + override def info = gilSynchronizedIfNotInited { super.info } + override def rawInfo: Type = gilSynchronizedIfNotInited { super.rawInfo } - override def typeParams: List[Symbol] = gilSynchronized { + override def typeParams: List[Symbol] = gilSynchronizedIfNotInited { if (isCompilerUniverse) super.typeParams else { if (isMonomorphicType) Nil @@ -65,7 +54,7 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb } } } - override def unsafeTypeParams: List[Symbol] = gilSynchronized { + override def unsafeTypeParams: List[Symbol] = gilSynchronizedIfNotInited { if (isCompilerUniverse) super.unsafeTypeParams else { if (isMonomorphicType) Nil @@ -73,14 +62,6 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb } } - override def reset(completer: Type): this.type = gilSynchronized { super.reset(completer) } - - override def infosString: String = gilSynchronized { super.infosString } - - override def annotations: List[AnnotationInfo] = gilSynchronized { super.annotations } - override def setAnnotations(annots: List[AnnotationInfo]): this.type = { gilSynchronized { super.setAnnotations(annots) }; this } - - // ------ creators ------------------------------------------------------------------- override protected def createAbstractTypeSymbol(name: TypeName, pos: Position, newFlags: Long): AbstractTypeSymbol = @@ -128,41 +109,26 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb // ------- subclasses --------------------------------------------------------------------- - trait SynchronizedTermSymbol extends TermSymbol with SynchronizedSymbol { - 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 SynchronizedTermSymbol extends SynchronizedSymbol trait SynchronizedMethodSymbol extends MethodSymbol with SynchronizedTermSymbol { - 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 } + // we can keep this lock fine-grained, because it's just a cache over asSeenFrom, which makes deadlocks impossible + // unfortunately we cannot elide this lock, because the cache depends on `pre` + private lazy val typeAsMemberOfLock = new Object + override def typeAsMemberOf(pre: Type): Type = gilSynchronizedIfNotInited { typeAsMemberOfLock.synchronized { super.typeAsMemberOf(pre) } } } + trait SynchronizedModuleSymbol extends ModuleSymbol with SynchronizedTermSymbol + trait SynchronizedTypeSymbol extends TypeSymbol with SynchronizedSymbol { - 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 } + // unlike with typeConstructor, a lock is necessary here, because tpe calculation relies on + // temporarily assigning NoType to tpeCache to detect cyclic reference errors + private lazy val tpeLock = new Object + override def tpe_* : Type = gilSynchronizedIfNotInited { tpeLock.synchronized { super.tpe_* } } } - trait SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol { - 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 SynchronizedClassSymbol extends ClassSymbol with SynchronizedTypeSymbol - trait SynchronizedModuleClassSymbol extends ModuleClassSymbol with SynchronizedClassSymbol { - override def sourceModule = gilSynchronized { super.sourceModule } - override def implicitMembers: Scope = gilSynchronized { super.implicitMembers } - } + trait SynchronizedModuleClassSymbol extends ModuleClassSymbol with SynchronizedClassSymbol } diff --git a/test/files/run/t6240-universe-code-gen.check b/test/files/run/t6240-universe-code-gen.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/run/t6240-universe-code-gen.check |