8000 Move LazyVar to BTypes, synchronize on frontednLock · scala/scala@9dd1933 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9dd1933

Browse files
committed
Move LazyVar to BTypes, synchronize on frontednLock
1 parent 233231d commit 9dd1933

14 files changed

+186
-168
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala

Lines changed: 85 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ import scala.tools.nsc.backend.jvm.opt._
2525
* be queried by concurrent threads.
2626
*/
2727
abstract class BTypes {
28-
val postProcessorFrontendAccess: PostProcessorFrontendAccess
29-
import postProcessorFrontendAccess.{frontendSynch, recordPerRunCache}
28+
val frontendAccess: PostProcessorFrontendAccess
29+
import frontendAccess.{frontendSynch, recordPerRunCache}
3030

3131
val coreBTypes: CoreBTypes { val bTypes: BTypes.this.type }
3232
import coreBTypes._
@@ -907,46 +907,6 @@ abstract class BTypes {
907907
nestedClasses: Lazy[List[ClassBType]], nestedInfo: Lazy[Option[NestedInfo]],
908908
inlineInfo: InlineInfo)
909909

910-
object Lazy {
911-
def apply[T <: AnyRef](t: => T): Lazy[T] = new Lazy[T](() => t)
912-
}
913-
914-
final class Lazy[T <: AnyRef](t: () => T) {
915-
private var value: T = null.asInstanceOf[T]
916-
917-
private var function = {
918-
val tt = t // prevent allocating a field for t
919-
() => { value = tt() }
920-
}
921-
922-
override def toString = if (value == null) "<?>" else value.toString
923-
924-
def onForce(f< 10000 /span>: T => Unit): Unit = {
925-
if (value != null) f(value)
926-
else frontendSynch {
927-
if (value != null) f(value)
928-
else {
929-
val prev = function
930-
function = () => {
931-
prev()
932-
f(value)
933-
}
934-
}
935-
}
936-
}
937-
938-
def force: T = {
939-
if (value != null) value
940-
else frontendSynch {
941-
if (value == null) {
942-
function()
943-
function = null
944-
}
945-
value
946-
}
947-
}
948-
}
949-
950910
/**
951911
* Information required to add a class to an InnerClass table.
952912
* The spec summary above explains what information is required for the InnerClass entry.
@@ -1011,6 +971,89 @@ abstract class BTypes {
1011971
* Used only in assertions. Abstract here because its implementation depends on global.
1012972
*/
1013973
def isCompilingPrimitive: Boolean
974+
975+
// The [[Lazy]] and [[LazyVar]] classes would conceptually be better placed within
976+
// PostProcessorFrontendAccess (they access the `frontendLock` defined in that class). However,
977+
// for every component in which we define nested classes, we need to make sure that the compiler
978+
// knows that all component instances (val frontendAccess) in various classes are all the same,
979+
// otherwise the prefixes don't match and we get type mismatch errors.
980+
// Since we already do this dance (val bTypes: GenBCode.this.bTypes.type = GenBCode.this.bTypes)
981+
// for BTypes, it's easier to add those nested classes to BTypes.
982+
983+
object Lazy {
984+
def apply[T <: AnyRef](t: => T): Lazy[T] = new Lazy[T](() => t)
985+
}
986+
987+
/**
988+
* A lazy value that synchronizes on the `frontendLock`, and supports accumulating actions
989+
* to be executed when it's forced.
990+
*/
991+
final class Lazy[T <: AnyRef](t: () => T) {
992+
@volatile private var value: T = _
993+
994+
private var initFunction = {
995+
val tt = t // prevent allocating a field for t
996+
() => { value = tt() }
997+
}
998+
999+
override def toString = if (value == null) "<?>" else value.toString
1000+
1001+
def onForce(f: T => Unit): Unit = {
1002+
if (value != null) f(value)
1003+
else frontendSynch {
1004+
if (value != null) f(value)
1005+
else {
1006+
val prev = initFunction
1007+
initFunction = () => {
1008+
prev()
1009+
f(value)
1010+
}
1011+
}
1012+
}
1013+
}
1014+
1015+
def force: T = {
1016+
if (value != null) value
1017+
else frontendSynch {
1018+
if (value == null) {
1019+
initFunction()
1020+
initFunction = null
1021+
}
1022+
value
1023+
}
1024+
}
1025+
}
1026+
1027+
/**
1028+
* Create state that lazily evaluated (to work around / not worry about initialization ordering
1029+
* issues). The state is re-initialized in each compiler run when the component is initialized.
1030+
*/
1031+
def perRunLazy[T](component: PerRunInit)(init: => T): LazyVar[T] = {
1032+
val r = new LazyVar(() => init)
1033+
component.perRunInit(r.reInitialize())
1034+
r
1035+
}
1036+
1037+
/**
1038+
* This implements a lazy value that can be reset and re-initialized.
1039+
* It synchronizes on `frontendLock` so that lazy state created through this utility can
1040+
* be safely initialized in the post-processor.
1041+
*/
1042+
class LazyVar[T](init: () => T) {
1043+
@volatile private[this] var isInit: Boolean = false
1044+
private[this] var v: T = _
1045+
1046+
def get: T = {
1047+
if (isInit) v
1048+
else frontendSynch {
1049+
if (!isInit) v = init()
1050+
isInit = true
1051+
v
1052+
}
1053+
}
1054+
1055+
def reInitialize(): Unit = frontendSynch(isInit = false)
1056+
}
10141057
}
10151058

10161059
object BTypes {

src/compiler/scala/tools/nsc/backend/jvm/BTypesFromClassfile.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import scala.tools.nsc.backend.jvm.opt.{BytecodeUtils, InlineInfoAttribute}
1111
abstract class BTypesFromClassfile {
1212
val postProcessor: PostProcessor
1313

14-
import postProcessor.frontendAccess.compilerSettings
1514
import postProcessor.{bTypes, byteCodeRepository, inlinerHeuristics}
1615
import bTypes._
1716
import coreBTypes._
17+
import frontendAccess.compilerSettings
1818

1919
/**
2020
* Obtain the BType for a type descriptor or internal name. For class descriptors, the ClassBType

src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ import scala.tools.nsc.backend.jvm.BackendReporting._
2323
* of the core btypes. They are declared in BTypes as abstract members. Note that BTypes does
2424
* not have access to the compiler instance.
2525
*/
26-
class BTypesFromSymbols[G <: Global](val global: G, val postProcessorFrontendAccess: PostProcessorFrontendAccess) extends BTypes {
26+
abstract class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
27+
val frontendAccess: PostProcessorFrontendAccess
28+
2729
import global._
2830
import definitions._
2931
import genBCode._

src/compiler/scala/tools/nsc/backend/jvm/CodeGen.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package backend.jvm
33

44
import scala.tools.asm.tree.ClassNode
55

6-
abstract class CodeGen[G <: Global](val global: G) extends PerRunLazy {
6+
abstract class CodeGen[G <: Global](val global: G) extends PerRunInit {
77
val bTypes: BTypesFromSymbols[global.type]
88

99
import global._
@@ -12,9 +12,9 @@ abstract class CodeGen[G <: Global](val global: G) extends PerRunLazy {
1212
private val caseInsensitively = perRunCaches.newMap[String, Symbol]()
1313

1414
// TODO: do we really need a new instance per run? Is there state that depends on the compiler frontend (symbols, types, settings)?
15-
private[this] lazy val mirrorCodeGen: LazyVar[CodeGenImpl.JMirrorBuilder] = perRunLazy(new CodeGenImpl.JMirrorBuilder())
15+
private[this] lazy val mirrorCodeGen: LazyVar[CodeGenImpl.JMirrorBuilder] = perRunLazy(this)(new CodeGenImpl.JMirrorBuilder())
1616

17-
private[this] lazy val beanInfoCodeGen: LazyVar[CodeGenImpl.JBeanInfoBuilder] = perRunLazy(new CodeGenImpl.JBeanInfoBuilder())
17+
private[this] lazy val beanInfoCodeGen: LazyVar[CodeGenImpl.JBeanInfoBuilder] = perRunLazy(this)(new CodeGenImpl.JBeanInfoBuilder())
1818

1919
def genUnit(unit: CompilationUnit): Unit = {
2020
import genBCode.postProcessor.generatedClasses

0 commit comments

Comments
 (0)
0