Skip to content

Fix deadlock in lazy intialization of CoreBTypes #19297

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 38 additions & 33 deletions compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package jvm
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.transform.Erasure
import scala.tools.asm.{Handle, Opcodes}
import scala.annotation.threadUnsafe
import dotty.tools.dotc.core.StdNames
import BTypes.InternalName

Expand Down Expand Up @@ -81,7 +82,7 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
* Map from primitive types to their boxed class type. Useful when pushing class literals onto the
* operand stack (ldc instruction taking a class literal), see genConstant.
*/
lazy val boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = frontendSynch(Map(
@threadUnsafe lazy val boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = frontendSynch(Map(
UNIT -> classBTypeFromSymbol(requiredClass[java.lang.Void]),
BOOL -> classBTypeFromSymbol(requiredClass[java.lang.Boolean]),
BYTE -> classBTypeFromSymbol(requiredClass[java.lang.Byte]),
Expand Down Expand Up @@ -128,36 +129,40 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
* Therefore, when srNothingRef or srNullRef are to be emitted, a mapping is needed: the internal
* names of NothingClass and NullClass can't be emitted as-is.
* 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$`
*
* We use threadUnsafe annotation here to prevent double synchronization while performing lazy val initialization
* All of these require Context which might change between compilation units.
* Synchronization on `frontendSynch` (providing a lock for context change) is sufficient to ensure thread-safety
*/
lazy val srNothingRef : ClassBType = synchClassBTypeFromSymbol(requiredClass("scala.runtime.Nothing$"))
lazy val srNullRef : ClassBType = synchClassBTypeFromSymbol(requiredClass("scala.runtime.Null$"))

lazy val ObjectRef : ClassBType = synchClassBTypeFromSymbol(defn.ObjectClass)
lazy val StringRef : ClassBType = synchClassBTypeFromSymbol(defn.StringClass)

lazy val jlStringBuilderRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.StringBuilder])
lazy val jlStringBufferRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.StringBuffer])
lazy val jlCharSequenceRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.CharSequence])
lazy val jlClassRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.Class[?]])
lazy val jlThrowableRef : ClassBType = synchClassBTypeFromSymbol(defn.ThrowableClass)
lazy val jlCloneableRef : ClassBType = synchClassBTypeFromSymbol(defn.JavaCloneableClass)
lazy val jiSerializableRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.io.Serializable])
lazy val jlClassCastExceptionRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.ClassCastException])
lazy val jlIllegalArgExceptionRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.IllegalArgumentException])
lazy val jliSerializedLambdaRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.invoke.SerializedLambda])

lazy val srBoxesRuntimeRef: ClassBType = synchClassBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime])

private lazy val jliCallSiteRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.invoke.CallSite])
private lazy val jliLambdaMetafactoryRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.invoke.LambdaMetafactory])
private lazy val jliMethodHandleRef : ClassBType = synchClassBTypeFromSymbol(defn.MethodHandleClass)
private lazy val jliMethodHandlesLookupRef : ClassBType = synchClassBTypeFromSymbol(defn.MethodHandlesLookupClass)
private lazy val jliMethodTypeRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType])
private lazy val jliStringConcatFactoryRef : ClassBType = synchClassBTypeFromSymbol(requiredClass("java.lang.invoke.StringConcatFactory")) // since JDK 9

lazy val srLambdaDeserialize : ClassBType = synchClassBTypeFromSymbol(requiredClass[scala.runtime.LambdaDeserialize])

lazy val jliLambdaMetaFactoryMetafactoryHandle = frontendSynch{ new Handle(
@threadUnsafe lazy val srNothingRef : ClassBType = synchClassBTypeFromSymbol(requiredClass("scala.runtime.Nothing$"))
@threadUnsafe lazy val srNullRef : ClassBType = synchClassBTypeFromSymbol(requiredClass("scala.runtime.Null$"))

@threadUnsafe lazy val ObjectRef : ClassBType = synchClassBTypeFromSymbol(defn.ObjectClass)
@threadUnsafe lazy val StringRef : ClassBType = synchClassBTypeFromSymbol(defn.StringClass)

@threadUnsafe lazy val jlStringBuilderRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.StringBuilder])
@threadUnsafe lazy val jlStringBufferRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.StringBuffer])
@threadUnsafe lazy val jlCharSequenceRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.CharSequence])
@threadUnsafe lazy val jlClassRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.Class[?]])
@threadUnsafe lazy val jlThrowableRef : ClassBType = synchClassBTypeFromSymbol(defn.ThrowableClass)
@threadUnsafe lazy val jlCloneableRef : ClassBType = synchClassBTypeFromSymbol(defn.JavaCloneableClass)
@threadUnsafe lazy val jiSerializableRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.io.Serializable])
@threadUnsafe lazy val jlClassCastExceptionRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.ClassCastException])
@threadUnsafe lazy val jlIllegalArgExceptionRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.IllegalArgumentException])
@threadUnsafe lazy val jliSerializedLambdaRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.invoke.SerializedLambda])

@threadUnsafe lazy val srBoxesRuntimeRef: ClassBType = synchClassBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime])

@threadUnsafe private lazy val jliCallSiteRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.invoke.CallSite])
@threadUnsafe private lazy val jliLambdaMetafactoryRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.invoke.LambdaMetafactory])
@threadUnsafe private lazy val jliMethodHandleRef : ClassBType = synchClassBTypeFromSymbol(defn.MethodHandleClass)
@threadUnsafe private lazy val jliMethodHandlesLookupRef : ClassBType = synchClassBTypeFromSymbol(defn.MethodHandlesLookupClass)
@threadUnsafe private lazy val jliMethodTypeRef : ClassBType = synchClassBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType])
@threadUnsafe private lazy val jliStringConcatFactoryRef : ClassBType = synchClassBTypeFromSymbol(requiredClass("java.lang.invoke.StringConcatFactory")) // since JDK 9

@threadUnsafe lazy val srLambdaDeserialize : ClassBType = synchClassBTypeFromSymbol(requiredClass[scala.runtime.LambdaDeserialize])

@threadUnsafe lazy val jliLambdaMetaFactoryMetafactoryHandle = frontendSynch{ new Handle(
Opcodes.H_INVOKESTATIC,
jliLambdaMetafactoryRef.internalName,
"metafactory",
Expand All @@ -167,7 +172,7 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
).descriptor,
/* itf = */ false)}

lazy val jliLambdaMetaFactoryAltMetafactoryHandle = frontendSynch{ new Handle(
@threadUnsafe lazy val jliLambdaMetaFactoryAltMetafactoryHandle = frontendSynch{ new Handle(
Opcodes.H_INVOKESTATIC,
jliLambdaMetafactoryRef.internalName,
"altMetafactory",
Expand All @@ -177,7 +182,7 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
).descriptor,
/* itf = */ false)}

lazy val jliLambdaDeserializeBootstrapHandle: Handle = frontendSynch{ new Handle(
@threadUnsafe lazy val jliLambdaDeserializeBootstrapHandle: Handle = frontendSynch{ new Handle(
Opcodes.H_INVOKESTATIC,
srLambdaDeserialize.internalName,
"bootstrap",
Expand All @@ -187,7 +192,7 @@ abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTy
).descriptor,
/* itf = */ false)}

lazy val jliStringConcatFactoryMakeConcatWithConstantsHandle = frontendSynch{ new Handle(
@threadUnsafe lazy val jliStringConcatFactoryMakeConcatWithConstantsHandle = frontendSynch{ new Handle(
Opcodes.H_INVOKESTATIC,
jliStringConcatFactoryRef.internalName,
"makeConcatWithConstants",
Expand Down
3 changes: 1 addition & 2 deletions compiler/test/dotty/tools/dotc/CompilationTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package dotc

import scala.language.unsafeNulls

import org.junit.{ Test, BeforeClass, AfterClass, Ignore }
import org.junit.{ Test, BeforeClass, AfterClass }
import org.junit.Assert._
import org.junit.Assume._
import org.junit.experimental.categories.Category
Expand Down Expand Up @@ -250,7 +250,6 @@ class CompilationTests {
}

// parallel backend tests
@Ignore("Temporarily disabled due to frequent timeouts")
@Test def parallelBackend: Unit = {
given TestGroup = TestGroup("parallelBackend")
val parallelism = Runtime.getRuntime().availableProcessors().min(16)
Expand Down