Skip to content

Potential race condition in NodeTestTask with parallel execution #5408

@jaschdoc

Description

@jaschdoc

Description

There seems to be a potential race condition in NodeTestTask. The issue appears to be related to concurrent access to the parentContext field.

The race condition appears to be present in the latest release too.

Steps to reproduce

While I am unable to provide consistent steps to reproduce the issue at this time, the problem was identified by ThreadSanitizer (TSAN) and is exercised by the ExampleUnitTest class below.

The test is written in Kotlin but that should not affect the result.

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Execution(ExecutionMode.CONCURRENT)
class ExampleUnitTest {

  @ParameterizedTest
  @MethodSource("numbers")
  fun testRaceCondition(n: Int) {
    assertTrue { true }
  }

  companion object {
    
    const val LIMIT = 1000

    @JvmStatic
    fun numbers(): List<Arguments> = (1..LIMIT).map { Arguments.of(it) }
  }

}

TSAN Output

SanitizerError
ThreadSanitizer: data race NodeTestTask.java:144 in org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$0()V

Details
==================
WARNING: ThreadSanitizer: data race (pid=6275)
  Read of size 4 at 0x00008fee0088 by thread T1:
    #0 org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$0()V NodeTestTask.java:144
    #1 org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda+0x000000010025b728.execute()V ??
    #2 org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(Lorg/junit/platform/engine/support/hierarchical/ThrowableCollector$Executable;)V ThrowableCollector.java:74
    #3 org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare()V NodeTestTask.java:144
    #4 org.junit.platform.engine.support.hierarchical.NodeTestTask.execute()V NodeTestTask.java:110
    #5 org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.exec()Z ForkJoinPoolHierarchicalTestExecutorService.java:253
     ...

  Previous write of size 4 at 0x00008fee0088 by thread T2:
    #0 org.junit.platform.engine.support.hierarchical.NodeTestTask.setParentContext(Lorg/junit/platform/engine/support/hierarchical/EngineExecutionContext;)V NodeTestTask.java:102
    #1 org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor.execute(Lorg/junit/platform/engine/TestDescriptor;Lorg/junit/platform/engine/EngineExecutionListener;)Ljava/util/concurrent/Future; NodeTestTask.java:266
    #2 org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor.execute(Lorg/junit/platform/engine/TestDescriptor;)V NodeTestTask.java:246
    #3 org.junit.jupiter.engine.descriptor.TemplateExecutor.execute(Lorg/junit/platform/engine/support/hierarchical/Node$DynamicTestExecutor;Lorg/junit/platform/engine/TestDescriptor;)V TemplateExecutor.java:96
    #4 org.junit.jupiter.engine.descriptor.TemplateExecutor.lambda$executeForProvider$1(Lorg/junit/platform/engine/support/hierarchical/Node$DynamicTestExecutor;Lorg/junit/platform/engine/TestDescriptor;)V TemplateExecutor.java:59
    #5 org.junit.jupiter.engine.descriptor.TemplateExecutor$$Lambda+0x00000001002a3bf8.accept(Ljava/lang/Object;)V ??

It appears there is a concurrent write in setParentContext with a concurrent read in prepare.

class NodeTestTask {
  
  private @Nullable C parentContext;

  private @Nullable C context;
  
  private void prepare() {
    requiredThrowableCollector().execute(() -> context = node.prepare(requireNonNull(parentContext)));
    parentContext = null;
  }

  void setParentContext(@Nullable C parentContext) {
    this.parentContext = parentContext;
  }
  
}

Potential Solution

Marking the parentContext field volatile within NodeTestTask fixes the warning.

I hope this information is helpful for investigating the issue.

Context

  • Used versions (Jupiter/Vintage/Platform):
    • Jupiter: 6.0.1
    • Platform: 6.0.1
  • OS: Linux

Deliverables

Metadata

Metadata

Assignees

Type

No fields configured for Bug.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions