Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Deadlock in concurrent initialization of ir.Names and ir.Types. #5135

Closed
@sjrd

Description

@sjrd

Our mutual initialization of ir.Names and ir.Types can cause a deadlock if both start concurrently from two different threads. Here is the relevant portion of a thread dump:

"pool-152-thread-1" #357 prio=5 os_prio=0 cpu=28.62ms elapsed=11.37s tid=0x00007fa9dd0fa590 nid=0x45b88 in Object.wait()  [0x00007fa846ffb000]
   java.lang.Thread.State: RUNNABLE
        at org.scalajs.ir.Types$.<init>(Types.scala:443)
        - waiting on the Class initialization monitor for org.scalajs.ir.Names$
        at org.scalajs.ir.Types$.<clinit>(Types.scala)
        at org.scalajs.ir.TestIRBuilder$.<init>(TestIRBuilder.scala:73)
        at org.scalajs.ir.TestIRBuilder$.<clinit>(TestIRBuilder.scala)
        at org.scalajs.ir.HashersTest.<init>(HashersTest.scala:48)
        ...

"pool-152-thread-4" #361 prio=5 os_prio=0 cpu=45.37ms elapsed=11.37s tid=0x00007fa9dd340a00 nid=0x45b8d in Object.wait()  [0x00007fa8487fb000]
   java.lang.Thread.State: RUNNABLE
        at org.scalajs.ir.Names$MethodName$.constructor(Names.scala:508)
        - waiting on the Class initialization monitor for org.scalajs.ir.Types$
        at org.scalajs.ir.Names$.<init>(Names.scala:656)
        at org.scalajs.ir.Names$.<clinit>(Names.scala)
        at org.scalajs.ir.Names$ClassName$.apply(Names.scala:550)
        at org.scalajs.ir.Names$ClassName$.apply(Names.scala:553)
        at org.scalajs.ir.Serializers$.<init>(Serializers.scala:54)
        at org.scalajs.ir.Serializers$.<clinit>(Serializers.scala)
        at org.scalajs.ir.Serializers$Hacks.<init>(Serializers.scala:2587)
        at org.scalajs.ir.SerializersTest.testHacksUseBelow(SerializersTest.scala:22)
        ...

I had occasionally noticed ir2_12/test to be stuck, but I had always attributed that to an sbt glitch. Fortunately (in a sense), adding new tests for Names in #5003 turned the likelihood of this happening from "very rare" to "very often". That let me diagnose the issue properly.

The problem is that we have an inter-dependency between the constructor of object Names and that of object Types.

For "Names depends on Types": in the constructor of object Names we have

final val NoArgConstructorName: MethodName =
MethodName.constructor(Nil)
/** This is used to construct a java.lang.Class. */
final val ObjectArgConstructorName: MethodName =
MethodName.constructor(List(ClassRef(ObjectClass)))
/** Name of the static initializer method. */
final val StaticInitializerName: MethodName =
MethodName(SimpleMethodName.StaticInitializer, Nil, VoidRef)
/** Name of the class initializer method. */
final val ClassInitializerName: MethodName =
MethodName(SimpleMethodName.ClassInitializer, Nil, VoidRef)

which requires the Types.XRefs of primitive types. Those are vals in object Types, so we need to initialize object Types (this wouldn't happen if they were objects, but they are vals because they are instances of a case class).

For "Types depends on Names": in the constructor of object Types we have

val BoxedClassToPrimType: Map[ClassName, PrimType] = Map(
BoxedUnitClass -> UndefType,
BoxedBooleanClass -> BooleanType,
BoxedCharacterClass -> CharType,
BoxedByteClass -> ByteType,
BoxedShortClass -> ShortType,
BoxedIntegerClass -> IntType,
BoxedLongClass -> LongType,
BoxedFloatClass -> FloatType,
BoxedDoubleClass -> DoubleType,
BoxedStringClass -> StringType
)
val PrimTypeToBoxedClass: Map[PrimType, ClassName] =
BoxedClassToPrimType.map(_.swap)

which similarly accesses vals defined in object Names.

This is not an issue in sequential initialization, because the interdependencies only require vals defined before the cycle starts anew. So whether we start from Names or from Types, we get all the things initialized in the right order. But in a concurrent scenario, the global initialization locks on JVM class initializers (rightly) create a deadlock.

We need to break this cycle somehow. My best idea so far is to move all those vals to a third object WellKnownNames, that neither Types nor Names is allowed to depend on.

Metadata

Metadata

Assignees

Labels

bugConfirmed bug. Needs to be fixed.

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions