Skip to content

ConcurrentModificationException in SequenceDiagramFactory under parallel test execution #142

Description

@paul-brooks

Problem

SequenceDiagramFactory iterates the live MutableList<UmlDirective> from configuration.sequenceDiagram.directives (wired in KensaLifecycleManager). create() walks it via umlDirectives.flatMap { it.asUml() } with no synchronization, while another thread structurally mutates the same list (add via participant()/title()/box(), clear via reset()).

Under JUnit parallel execution, concurrent invocations ending on the ForkJoinPool throw ConcurrentModificationException.

java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1050)
    at dev.kensa.render.diagram.SequenceDiagramFactory.create(SequenceDiagramFactory.kt)
    at dev.kensa.state.TestInvocationFactory.create(TestInvocationFactory.kt:39)
    at dev.kensa.context.KensaLifecycleManager.endInvocation(KensaLifecycleManager.kt:61)
    at dev.kensa.junit.KensaExtension.afterTestExecution(KensaExtension.kt:46)

A defensive toList() inside create() does not fix it — the copy itself races. The lock must live with the data.

Fix

  • Guard all directive mutations in SequenceDiagramConfiguration with a lock; add a synchronized directivesSnapshot().
  • SequenceDiagramFactory consumes a () -> List<UmlDirective> provider returning that snapshot, so create() never iterates live state.

Regression test reproduces the CME reliably (4000+ occurrences) pre-fix and passes post-fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions