Skip to content

Commit 33bdaac

Browse files
Fix deadlock in initialization of CoreBTypes using Lazy container (#19298)
Replaces #19297 and fixes #19293 The deadlocks are now fixed by introduction of `PostProcessorFrontendAccess.Lazy[T]` container for which initialization is synchronized with the frontend Context, while providing a thread-safe access lacking in original solution. It now also audits where the unrestricted access to context was used and replaces these usages with safer access. Reverts #19292
1 parent eae8831 commit 33bdaac

File tree

6 files changed

+131
-68
lines changed

6 files changed

+131
-68
lines changed

Diff for: compiler/src/dotty/tools/backend/jvm/BTypes.scala

-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import scala.tools.asm
1616
*/
1717
abstract class BTypes { self =>
1818
val frontendAccess: PostProcessorFrontendAccess
19-
import frontendAccess.{frontendSynch}
20-
2119
val int: DottyBackendInterface
2220
import int.given
2321
/**

Diff for: compiler/src/dotty/tools/backend/jvm/CodeGen.scala

+1-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import dotty.tools.dotc.util.NoSourcePosition
3838
class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)( val bTypes: BTypesFromSymbols[int.type]) { self =>
3939
import DottyBackendInterface.symExtensions
4040
import bTypes.*
41-
import int.given
4241

4342
private lazy val mirrorCodeGen = Impl.JMirrorBuilder()
4443

@@ -125,7 +124,7 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)(
125124
}
126125

127126
// Creates a callback that will be evaluated in PostProcessor after creating a file
128-
private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: util.SourceFile): AbstractFile => Unit = {
127+
private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: util.SourceFile)(using Context): AbstractFile => Unit = {
129128
val (fullClassName, isLocal) = atPhase(sbtExtractDependenciesPhase) {
130129
(ExtractDependencies.classNameAsString(claszSymbol), claszSymbol.isLocal)
131130
}

Diff for: compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala

+88-43
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import dotty.tools.dotc.transform.Erasure
88
import scala.tools.asm.{Handle, Opcodes}
99
import dotty.tools.dotc.core.StdNames
1010
import BTypes.InternalName
11+
import PostProcessorFrontendAccess.Lazy
1112

1213
abstract class CoreBTypes {
1314
val bTypes: BTypes
@@ -56,16 +57,16 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
5657
val bTypes: BTypesFromSymbols[I]
5758

5859
import bTypes.*
59-
import int.given
6060
import DottyBackendInterface.*
61-
import frontendAccess.frontendSynch
6261
import dotty.tools.dotc.core.Contexts.Context
63-
62+
import frontendAccess.perRunLazy
6463
/**
6564
* Maps primitive types to their corresponding PrimitiveBType. The map is defined lexically above
6665
* the first use of `classBTypeFromSymbol` because that method looks at the map.
6766
*/
68-
lazy val primitiveTypeMap: Map[Symbol, PrimitiveBType] = Map(
67+
override def primitiveTypeMap: Map[Symbol, bTypes.PrimitiveBType] = _primitiveTypeMap.get
68+
private lazy val _primitiveTypeMap: Lazy[Map[Symbol, PrimitiveBType]] = perRunLazy:
69+
Map(
6970
defn.UnitClass -> UNIT,
7071
defn.BooleanClass -> BOOL,
7172
defn.CharClass -> CHAR,
@@ -81,7 +82,8 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
8182
* Map from primitive types to their boxed class type. Useful when pushing class literals onto the
8283
* operand stack (ldc instruction taking a class literal), see genConstant.
8384
*/
84-
lazy val boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = frontendSynch(Map(
85+
override def boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = _boxedClassOfPrimitive.get
86+
private lazy val _boxedClassOfPrimitive: Lazy[Map[PrimitiveBType, ClassBType]] = perRunLazy(Map(
8587
UNIT -> classBTypeFromSymbol(requiredClass[java.lang.Void]),
8688
BOOL -> classBTypeFromSymbol(requiredClass[java.lang.Boolean]),
8789
BYTE -> classBTypeFromSymbol(requiredClass[java.lang.Byte]),
@@ -99,7 +101,8 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
99101
* Maps the method symbol for a box method to the boxed type of the result. For example, the
100102
* method symbol for `Byte.box()` is mapped to the ClassBType `java/lang/Byte`.
101103
*/
102-
lazy val boxResultType: Map[Symbol, ClassBType] = {
104+
override def boxResultType: Map[Symbol, ClassBType] = _boxResultType.get
105+
private lazy val _boxResultType: Lazy[Map[Symbol, ClassBType]] = perRunLazy{
103106
val boxMethods = defn.ScalaValueClasses().map{x => // @darkdimius Are you sure this should be a def?
104107
(x, Erasure.Boxing.boxMethod(x.asClass))
105108
}.toMap
@@ -110,17 +113,14 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
110113
/**
111114
* Maps the method symbol for an unbox method to the primitive type of the result.
112115
* For example, the method symbol for `Byte.unbox()`) is mapped to the PrimitiveBType BYTE. */
113-
lazy val unboxResultType: Map[Symbol, PrimitiveBType] = {
116+
override def unboxResultType: Map[Symbol, PrimitiveBType] = _unboxResultType.get
117+
private lazy val _unboxResultType = perRunLazy[Map[Symbol, PrimitiveBType]]{
114118
val unboxMethods: Map[Symbol, Symbol] =
115119
defn.ScalaValueClasses().map(x => (x, Erasure.Boxing.unboxMethod(x.asClass))).toMap
116120
for ((valueClassSym, unboxMethodSym) <- unboxMethods)
117121
yield unboxMethodSym -> primitiveTypeMap(valueClassSym)
118122
}
119123

120-
// Used to synchronize initialization of Context dependent ClassBTypes which can be accessed from multiple-threads
121-
// Unsychronized initialization might lead errors in either CodeGen or PostProcessor
122-
inline private def synchClassBTypeFromSymbol(inline sym: Symbol) = frontendSynch(classBTypeFromSymbol(sym))
123-
124124
/*
125125
* srNothingRef and srNullRef exist at run-time only. They are the bytecode-level manifestation (in
126126
* method signatures only) of what shows up as NothingClass (scala.Nothing) resp. NullClass (scala.Null) in Scala ASTs.
@@ -129,35 +129,76 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
129129
* names of NothingClass and NullClass can't be emitted as-is.
130130
* TODO @lry Once there's a 2.11.3 starr, use the commented argument list. The current starr crashes on the type literal `scala.runtime.Nothing$`
131131
*/
132-
lazy val srNothingRef : ClassBType = synchClassBTypeFromSymbol(requiredClass("scala.runtime.Nothing$"))
133-
lazy val srNullRef : ClassBType = synchClassBTypeFromSymbol(requiredClass("scala.runtime.Null$"))
134-
135-
lazy val ObjectRef : ClassBType = synchClassBTypeFromSymbol(defn.ObjectClass)
136-
lazy val StringRef : ClassBType = synchClassBTypeFromSymbol(defn.StringClass)
137-
138-
lazy val jlStringBuilderRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.StringBuilder])
139-
lazy val jlStringBufferRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.StringBuffer])
140-
lazy val jlCharSequenceRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.CharSequence])
141-
lazy val jlClassRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.Class[?]])
142-
lazy val jlThrowableRef : ClassBType = synchClassBTypeFromSymbol(defn.ThrowableClass)
143-
lazy val jlCloneableRef : ClassBType = synchClassBTypeFromSymbol(defn.JavaCloneableClass)
144-
lazy val jiSerializableRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.io.Serializable])
145-
lazy val jlClassCastExceptionRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.ClassCastException])
146-
lazy val jlIllegalArgExceptionRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.IllegalArgumentException])
147-
lazy val jliSerializedLambdaRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.invoke.SerializedLambda])
148-
149-
lazy val srBoxesRuntimeRef: ClassBType = synchClassBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime])
150-
151-
private lazy val jliCallSiteRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.invoke.CallSite])
152-
private lazy val jliLambdaMetafactoryRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.invoke.LambdaMetafactory])
153-
private lazy val jliMethodHandleRef : ClassBType = synchClassBTypeFromSymbol(defn.MethodHandleClass)
154-
private lazy val jliMethodHandlesLookupRef : ClassBType = synchClassBTypeFromSymbol(defn.MethodHandlesLookupClass)
155-
private lazy val jliMethodTypeRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType])
156-
private lazy val jliStringConcatFactoryRef : ClassBType = synchClassBTypeFromSymbol(requiredClass("java.lang.invoke.StringConcatFactory")) // since JDK 9
157-
158-
lazy val srLambdaDeserialize : ClassBType = synchClassBTypeFromSymbol(requiredClass[scala.runtime.LambdaDeserialize])
159-
160-
lazy val jliLambdaMetaFactoryMetafactoryHandle = frontendSynch{ new Handle(
132+
override def srNothingRef: ClassBType = _srNothingRef.get
133+
private lazy val _srNothingRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass("scala.runtime.Nothing$")))
134+
135+
override def srNullRef: ClassBType = _srNullRef.get
136+
private lazy val _srNullRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass("scala.runtime.Null$")))
137+
138+
override def ObjectRef: ClassBType = _ObjectRef.get
139+
private lazy val _ObjectRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(defn.ObjectClass))
140+
141+
override def StringRef: ClassBType = _StringRef.get
142+
private lazy val _StringRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(defn.StringClass))
143+
144+
override def jlStringBuilderRef: ClassBType = _jlStringBuilderRef.get
145+
private lazy val _jlStringBuilderRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[java.lang.StringBuilder]))
146+
147+
override def jlStringBufferRef: ClassBType = _jlStringBufferRef.get
148+
private lazy val _jlStringBufferRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[java.lang.StringBuffer]))
149+
150+
override def jlCharSequenceRef: ClassBType = _jlCharSequenceRef.get
151+
private lazy val _jlCharSequenceRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[java.lang.CharSequence]))
152+
153+
override def jlClassRef: ClassBType = _jlClassRef.get
154+
private lazy val _jlClassRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[java.lang.Class[?]]))
155+
156+
override def jlThrowableRef: ClassBType = _jlThrowableRef.get
157+
private lazy val _jlThrowableRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(defn.ThrowableClass))
158+
159+
override def jlCloneableRef: ClassBType = _jlCloneableRef.get
160+
private lazy val _jlCloneableRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(defn.JavaCloneableClass))
161+
162+
override def jiSerializableRef: ClassBType = _jiSerializableRef.get
163+
private lazy val _jiSerializableRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[java.io.Serializable]))
164+
165+
override def jlClassCastExceptionRef: ClassBType = _jlClassCastExceptionRef.get
166+
private lazy val _jlClassCastExceptionRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[java.lang.ClassCastException]))
167+
168+
override def jlIllegalArgExceptionRef: ClassBType = _jlIllegalArgExceptionRef.get
169+
private lazy val _jlIllegalArgExceptionRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[java.lang.IllegalArgumentException]))
170+
171+
override def jliSerializedLambdaRef: ClassBType = _jliSerializedLambdaRef.get
172+
private lazy val _jliSerializedLambdaRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[java.lang.invoke.SerializedLambda]))
173+
174+
override def srBoxesRuntimeRef: ClassBType = _srBoxesRuntimeRef.get
175+
private lazy val _srBoxesRuntimeRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime]))
176+
177+
private def jliCallSiteRef: ClassBType = _jliCallSiteRef.get
178+
private lazy val _jliCallSiteRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[java.lang.invoke.CallSite]))
179+
180+
private def jliLambdaMetafactoryRef: ClassBType = _jliLambdaMetafactoryRef.get
181+
private lazy val _jliLambdaMetafactoryRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[java.lang.invoke.LambdaMetafactory]))
182+
183+
private def jliMethodHandleRef: ClassBType = _jliMethodHandleRef.get
184+
private lazy val _jliMethodHandleRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(defn.MethodHandleClass))
185+
186+
private def jliMethodHandlesLookupRef: ClassBType = _jliMethodHandlesLookupRef.get
187+
private lazy val _jliMethodHandlesLookupRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(defn.MethodHandlesLookupClass))
188+
189+
private def jliMethodTypeRef: ClassBType = _jliMethodTypeRef.get
190+
private lazy val _jliMethodTypeRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType]))
191+
192+
// since JDK 9
193+
private def jliStringConcatFactoryRef: ClassBType = _jliStringConcatFactoryRef.get
194+
private lazy val _jliStringConcatFactoryRef: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass("java.lang.invoke.StringConcatFactory")))
195+
196+
private def srLambdaDeserialize: ClassBType = _srLambdaDeserialize.get
197+
private lazy val _srLambdaDeserialize: Lazy[ClassBType] = perRunLazy(classBTypeFromSymbol(requiredClass[scala.runtime.LambdaDeserialize]))
198+
199+
200+
override def jliLambdaMetaFactoryMetafactoryHandle = _jliLambdaMetaFactoryMetafactoryHandle.get
201+
private lazy val _jliLambdaMetaFactoryMetafactoryHandle: Lazy[Handle] = perRunLazy{new Handle(
161202
Opcodes.H_INVOKESTATIC,
162203
jliLambdaMetafactoryRef.internalName,
163204
"metafactory",
@@ -167,7 +208,8 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
167208
).descriptor,
168209
/* itf = */ false)}
169210

170-
lazy val jliLambdaMetaFactoryAltMetafactoryHandle = frontendSynch{ new Handle(
211+
override def jliLambdaMetaFactoryAltMetafactoryHandle = _jliLambdaMetaFactoryAltMetafactoryHandle.get
212+
private lazy val _jliLambdaMetaFactoryAltMetafactoryHandle: Lazy[Handle] = perRunLazy{ new Handle(
171213
Opcodes.H_INVOKESTATIC,
172214
jliLambdaMetafactoryRef.internalName,
173215
"altMetafactory",
@@ -177,7 +219,8 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
177219
).descriptor,
178220
/* itf = */ false)}
179221

180-
lazy val jliLambdaDeserializeBootstrapHandle: Handle = frontendSynch{ new Handle(
222+
override def jliLambdaDeserializeBootstrapHandle: Handle = _jliLambdaDeserializeBootstrapHandle.get
223+
private lazy val _jliLambdaDeserializeBootstrapHandle: Lazy[Handle] = perRunLazy{ new Handle(
181224
Opcodes.H_INVOKESTATIC,
182225
srLambdaDeserialize.internalName,
183226
"bootstrap",
@@ -187,7 +230,8 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
187230
).descriptor,
188231
/* itf = */ false)}
189232

190-
lazy val jliStringConcatFactoryMakeConcatWithConstantsHandle = frontendSynch{ new Handle(
233+
override def jliStringConcatFactoryMakeConcatWithConstantsHandle = _jliStringConcatFactoryMakeConcatWithConstantsHandle.get
234+
private lazy val _jliStringConcatFactoryMakeConcatWithConstantsHandle: Lazy[Handle] = perRunLazy{ new Handle(
191235
Opcodes.H_INVOKESTATIC,
192236
jliStringConcatFactoryRef.internalName,
193237
"makeConcatWithConstants",
@@ -199,6 +243,7 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
199243

200244
/**
201245
* Methods in scala.runtime.BoxesRuntime
246+
* No need to wrap in Lazy to synchronize access, symbols won't change
202247
*/
203248
lazy val asmBoxTo : Map[BType, MethodNameAndType] = Map(
204249
BOOL -> MethodNameAndType("boxToBoolean", MethodBType(List(BOOL), boxedClassOfPrimitive(BOOL))),

Diff for: compiler/src/dotty/tools/backend/jvm/GenBCode.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class GenBCode extends Phase { self =>
7575
}
7676

7777
override def run(using Context): Unit =
78-
frontendAccess.frontendSynch {
78+
frontendAccess.frontendSynchWithoutContext {
7979
backendInterface.ctx
8080
.asInstanceOf[FreshContext]
8181
.setCompilationUnit(ctx.compilationUnit)

Diff for: compiler/src/dotty/tools/backend/jvm/PostProcessor.scala

+2-3
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ import scala.tools.asm.tree.ClassNode
1616
*/
1717
class PostProcessor(val frontendAccess: PostProcessorFrontendAccess, val bTypes: BTypes) {
1818
self =>
19-
import bTypes.{classBTypeFromInternalName, int}
19+
import bTypes.{classBTypeFromInternalName}
2020
import frontendAccess.{backendReporting, compilerSettings}
21-
import int.given
2221

2322
val backendUtils = new BackendUtils(this)
2423
val classfileWriters = new ClassfileWriters(frontendAccess)
@@ -27,7 +26,7 @@ class PostProcessor(val frontendAccess: PostProcessorFrontendAccess, val bTypes:
2726
type ClassnamePosition = (String, SourcePosition)
2827
private val caseInsensitively = new ConcurrentHashMap[String, ClassnamePosition]
2928

30-
def sendToDisk(clazz: GeneratedClass, sourceFile: AbstractFile): Unit = if !ctx.settings.YoutputOnlyTasty.value then {
29+
def sendToDisk(clazz: GeneratedClass, sourceFile: AbstractFile): Unit = if !compilerSettings.outputOnlyTasty then {
3130
val classNode = clazz.classNode
3231
val internalName = classNode.name.nn
3332
val bytes =

0 commit comments

Comments
 (0)