Skip to content

fix(maven-plugin): resolve Maven -T parallel-build deadlock - #264

Merged
dmealing merged 9 commits into
mainfrom
fix/233-maven-parallel-deadlock
Aug 2, 2026
Merged

fix(maven-plugin): resolve Maven -T parallel-build deadlock#264
dmealing merged 9 commits into
mainfrom
fix/233-maven-parallel-deadlock

Conversation

@dmealing

@dmealing dmealing commented Aug 2, 2026

Copy link
Copy Markdown
Member

Intent

Fix GitHub #233: Maven -T parallel-build deadlock in metaobjects-maven-plugin. Two parts. Part A (deadlock): new RegistryBootstrap.warmUpDefaults() deterministically initializes the three process-global registry singletons (MetaDataRegistry.getInstance, RegistryManifest.defaultLoaderRegistry, ConstraintEnforcer.getInstance) on a single thread under one lock before any parallel first-init can race their independent locks; called from MetaDataLoader.initWithConcurrencyProtection (all loader embedders) and the mojo execute()s (Maven's pre-init eager getTypeRegistry touch). Part B (correctness): MetaDataLoader gains a process-unique instanceId appended to buildLoaderKey() (now package-private for the test) so the static activeLoaders dedup only coalesces the same instance — two reactor modules sharing a name no longer share one load. generate/verify/docs mojos marked @mojo(threadSafe=true) as honest labeling that ships atomically (Maven 3.x does not serialize non-threadSafe mojos under -T, only warns). editor (direct-invocation) and agent-docs (stub) intentionally left unmarked. New tests: LoaderKeyIsolationTest, RegistryBootstrapTest, MojoThreadSafeDescriptorTest (reads generated plugin.xml since @mojo is CLASS-retention). Maven-only change (Java maven-plugin + metadata registry); behavior byte-identical for existing single-module builds. Verified: metadata 1272 + maven-plugin 24 + codegen-spring 187 + codegen-kotlin 310 green; manual -T4 reactor before/after — unfixed 8/8 fail (MetaDataLoader [shared] is not usable, Phase UNINITIALIZED), fixed 5/5 pass with per-module isolation. PR should Close #233.

What Changed

  • Added RegistryBootstrap.warmUpDefaults(), which deterministically initializes the three process-global registry singletons (MetaDataRegistry, RegistryManifest, ConstraintEnforcer) on one thread under a single lock before any parallel first-init can race their independent locks; invoked from MetaDataLoader.initWithConcurrencyProtection and each mojo's execute().
  • Gave MetaDataLoader a process-unique instanceId appended to buildLoaderKey(), so the static activeLoaders dedup only coalesces the same instance — two reactor modules sharing a <loader> name no longer collapse into one shared load.
  • Marked the generate, verify, and docs mojos @Mojo(threadSafe=true) as honest parallel-build labeling; editor and agent-docs are intentionally left unmarked.

Closes #233.

Risk Assessment

✅ Low: Well-bounded Maven-only change: the warm-up is strictly additive (idempotent single-thread serialization of an already-shared sealed registry), the per-instance key fix is internal and same-instance semantics are preserved, threadSafe is labeling-only in Maven 3.x, and all reactor entry points + the loader chokepoint warm before the singleton first-touch; behavior is byte-identical for existing single-module builds and the durable-fix invariants hold for the stated scope.

Testing

Targeted unit tests (the three new ones, all green) plus a faithful end-to-end -T4 reactor reproducer that I authored from the design doc: pre-fix base-commit plugin fails 5/5 with the exact shared-loader UNINITIALIZED collision (loser flipping between mod-a/mod-b, mod-c with a distinct name always succeeding), while the fixed tip plugin passes 6/6 with strict per-module isolation and completes without hanging — demonstrating both Part A (no deadlock under parallelism) and Part B (per-instance loader-key isolation). threadSafe mojo labeling and the warm-up call sites were additionally confirmed in the generated/installed artifacts. One setup hiccup (base build missing repo-root spec/metamodel) was fixed by re-extracting the full source tree; fixed artifacts were reinstalled and re-verified. Worktree left clean.

Evidence: Pre-fix -T4 reactor — 5/5 FAIL (exact UNINITIALIZED collision)

Pre-fix (base 6ea8deb7) under mvn -T4: 5/5 BUILD FAILURE. Loser of the shared-<loader> race errors: Execution default of goal com.metaobjects:metaobjects-maven-plugin:7.20.11:docs failed: MetaDataLoader [shared] is not usable. Phase: UNINITIALIZED (Not yet initialized), Elapsed: ~80ms, Version: 0 Runs 1-3: mod-b loses → mod-a→Alpha.md, mod-b→<none>. Runs 4-5: mod-a loses → mod-b→Bravo.md, mod-a→<none>. mod-c (distinct name) → Charlie.md every run.

===== PRE-FIX RUN 1 =====
[INFO] Reactor Summary for #233 -T4 reactor reproducer 1.0.0:
[INFO] #233 -T4 reactor reproducer ........................ SUCCESS [  0.054 s]
[INFO] mod-a .............................................. SUCCESS [  0.474 s]
[INFO] mod-b .............................................. FAILURE [  0.219 s]
[INFO] mod-c .............................................. SUCCESS [  0.474 s]
[INFO] BUILD FAILURE
[ERROR] Failed to execute goal com.metaobjects:metaobjects-maven-plugin:7.20.11:docs (default) on project mod-b: Execution default of goal com.metaobjects:metaobjects-maven-plugin:7.20.11:docs failed: MetaDataLoader [shared] is not usable. Phase: UNINITIALIZED (Not yet initialized), Elapsed: 79ms, Version: 0 -> [Help 1]
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException
-- docs files (run 1) --
  mod-a -> Alpha.md 
  mod-b -> <none>
  mod-c -> Charlie.md 
===== PRE-FIX RUN 2 =====
[INFO] Reactor Summary for #233 -T4 reactor reproducer 1.0.0:
[INFO] #233 -T4 reactor reproducer ........................ SUCCESS [  0.054 s]
[INFO] mod-a .............................................. SUCCESS [  0.484 s]
[INFO] mod-b .............................................. FAILURE [  0.216 s]
[INFO] mod-c .............................................. SUCCESS [  0.484 s]
[INFO] BUILD FAILURE
[ERROR] Failed to execute goal com.metaobjects:metaobjects-maven-plugin:7.20.11:docs (default) on project mod-b: Execution default of goal com.metaobjects:metaobjects-maven-plugin:7.20.11:docs failed: MetaDataLoader [shared] is not usable. Phase: UNINITIALIZED (Not yet initialized), Elapsed: 83ms, Version: 0 -> [Help 1]
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException
-- docs files (run 2) --
  mod-a -> Alpha.md 
  mod-b -> <none>
  mod-c -> Charlie.md 
===== PRE-FIX RUN 3 =====
[INFO] Reactor Summary for #233 -T4 reactor reproducer 1.0.0:
[INFO] #233 -T4 reactor reproducer ........................ SUCCESS [  0.055 s]
[INFO] mod-a .............................................. SUCCESS [  0.506 s]
[INFO] mod-b .............................................. FAILURE [  0.220 s]
[INFO] mod-c .............................................. SUCCESS [  0.506 s]
[INFO] BUILD FAILURE
[ERROR] Failed to execute goal com.metaobjects:metaobjects-maven-plugin:7.20.11:docs (default) on project mod-b: Execution default of goal com.metaobjects:metaobjects-maven-plugin:7.20.11:docs failed: MetaDataLoader [shared] is not usable. Phase: UNINITIALIZED (Not yet initialized), Elapsed: 86ms, Version: 0 -> [Help 1]
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException
-- docs files (run 3) --
  mod-a -> Alpha.md 
  mod-b -> <none>
  mod-c -> Charlie.md 
===== PRE-FIX RUN 4 =====
[INFO] Reactor Summary for #233 -T4 reactor reproducer 1.0.0:
[INFO] #233 -T4 reactor reproducer ........................ SUCCESS [  0.057 s]
[INFO] mod-a .............................................. FAILURE [  0.230 s]
[INFO] mod-b .............................................. SUCCESS [  0.485 s]
[INFO] mod-c .............................................. SUCCESS [  0.485 s]
[INFO] BUILD FAILURE
[ERROR] Failed to execute goal com.metaobjects:metaobjects-maven-plugin:7.20.11:docs (default) on project mod-a: Execution default of goal com.metaobjects:metaobjects-maven-plugin:7.20.11:docs failed: MetaDataLoader [shared] is not usable. Phase: UNINITIALIZED (Not yet initialized), Elapsed: 93ms, Version: 0 -> [Help 1]
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException
-- docs files (run 4) --
  mod-a -> <none>
  mod-b -> Bravo.md 
  mod-c -> Charlie.md 
===== PRE-FIX RUN 5 =====
[INFO] Reactor Summary for #233 -T4 reactor reproducer 1.0.0:
[INFO] #233 -T4 reactor reproducer ........................ SUCCESS [  0.058 s]
[INFO] mod-a .............................................. FAILURE [  0.232 s]
[INFO] mod-b .............................................. SUCCESS [  0.504 s]
[INFO] mod-c .............................................. SUCCESS [  0.503 s]
[INFO] BUILD FAILURE
[ERROR] Failed to execute goal com.metaobjects:metaobjects-maven-plugin:7.20.11:docs (default) on project mod-a: Execution default of goal com.metaobjects:metaobjects-maven-plugin:7.20.11:docs failed: MetaDataLoader [shared] is not usable. Phase: UNINITIALIZED (Not yet initialized), Elapsed: 90ms, Version: 0 -> [Help 1]
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException
-- docs files (run 5) --
  mod-a -> <none>
  mod-b -> Bravo.md 
  mod-c -> Charlie.md 
Evidence: Post-fix -T4 reactor — 6/6 PASS (per-module isolation, no hang)

Post-fix (tip a3ad3610) mvn -T4 clean package: mod-a .............................................. SUCCESS [ 0.506 s] mod-b .............................................. SUCCESS [ 0.503 s] mod-c .............................................. SUCCESS [ 0.503 s] BUILD SUCCESS Total time: 0.602 s (Wall Clock) real 1.034s / user 5.059s (real parallelism, no deadlock) Per-module docs: mod-a→Alpha.md, mod-b→Bravo.md, mod-c→Charlie.md; 0 cross-module entity references (content-checked).

$ mvn -T4 clean package
[INFO] Reactor Summary for #233 -T4 reactor reproducer 1.0.0:
[INFO] #233 -T4 reactor reproducer ........................ SUCCESS [  0.057 s]
[INFO] mod-a .............................................. SUCCESS [  0.506 s]
[INFO] mod-b .............................................. SUCCESS [  0.503 s]
[INFO] mod-c .............................................. SUCCESS [  0.503 s]
[INFO] BUILD SUCCESS
[INFO] Total time:  0.602 s (Wall Clock)

real	0m1.034s
user	0m5.059s
sys	0m0.205s
Evidence: Post-fix -T4 reactor — 5 isolation runs
===== RUN 1 =====
-- docs files (run 1) --
  mod-a -> Alpha.md 
  mod-b -> Bravo.md 
  mod-c -> Charlie.md 
===== RUN 2 =====
-- docs files (run 2) --
  mod-a -> Alpha.md 
  mod-b -> Bravo.md 
  mod-c -> Charlie.md 
===== RUN 3 =====
-- docs files (run 3) --
  mod-a -> Alpha.md 
  mod-b -> Bravo.md 
  mod-c -> Charlie.md 
===== RUN 4 =====
-- docs files (run 4) --
  mod-a -> Alpha.md 
  mod-b -> Bravo.md 
  mod-c -> Charlie.md 
===== RUN 5 =====
-- docs files (run 5) --
  mod-a -> Alpha.md 
  mod-b -> Bravo.md 
  mod-c -> Charlie.md 
Evidence: Restore-confirm -T4 pass after reinstalling fixed artifacts
[INFO] mod-a                                                              [jar]
[INFO] mod-b                                                              [jar]
[INFO] mod-c                                                              [jar]
[INFO] -----------------------------< demo:mod-a >-----------------------------
[INFO] Building mod-a 1.0.0                                               [2/4]
[INFO] -----------------------------< demo:mod-b >-----------------------------
[INFO]   from mod-a/pom.xml
[INFO] -----------------------------< demo:mod-c >-----------------------------
[INFO] Building mod-b 1.0.0                                               [3/4]
[INFO] Building mod-c 1.0.0                                               [4/4]
[INFO]   from mod-b/pom.xml
[INFO]   from mod-c/pom.xml
[INFO] --- clean:3.2.0:clean (default-clean) @ mod-b ---
[INFO] --- clean:3.2.0:clean (default-clean) @ mod-a ---
[INFO] --- clean:3.2.0:clean (default-clean) @ mod-c ---
[INFO] Deleting /tmp/_233_reactor/mod-b/target
[INFO] Deleting /tmp/_233_reactor/mod-c/target
[INFO] --- metaobjects:7.20.11:docs (default) @ mod-a ---
[INFO] --- metaobjects:7.20.11:docs (default) @ mod-b ---
[INFO] --- metaobjects:7.20.11:docs (default) @ mod-c ---
[INFO] metaobjects:docs — wrote 3 api pages into /tmp/_233_reactor/mod-a/target/docs/api/java
[INFO] metaobjects:docs — wrote 3 api pages into /tmp/_233_reactor/mod-b/target/docs/api/java
[INFO] metaobjects:docs — wrote 3 api pages into /tmp/_233_reactor/mod-c/target/docs/api/java
[INFO] --- resources:3.3.1:resources (default-resources) @ mod-c ---
[INFO] --- resources:3.3.1:resources (default-resources) @ mod-a ---
[INFO] --- resources:3.3.1:resources (default-resources) @ mod-b ---
[INFO] --- compiler:3.13.0:compile (default-compile) @ mod-a ---
[INFO] --- compiler:3.13.0:compile (default-compile) @ mod-b ---
[INFO] --- compiler:3.13.0:compile (default-compile) @ mod-c ---
[INFO] --- resources:3.3.1:testResources (default-testResources) @ mod-b ---
[INFO] --- resources:3.3.1:testResources (default-testResources) @ mod-c ---
[INFO] --- resources:3.3.1:testResources (default-testResources) @ mod-a ---
[INFO] skip non existing resourceDirectory /tmp/_233_reactor/mod-b/src/test/resources
[INFO] skip non existing resourceDirectory /tmp/_233_reactor/mod-c/src/test/resources
[INFO] skip non existing resourceDirectory /tmp/_233_reactor/mod-a/src/test/resources
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ mod-b ---
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ mod-c ---
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ mod-a ---
[INFO] --- surefire:3.2.5:test (default-test) @ mod-b ---
[INFO] --- surefire:3.2.5:test (default-test) @ mod-c ---
[INFO] --- surefire:3.2.5:test (default-test) @ mod-a ---
[INFO] --- jar:3.4.1:jar (default-jar) @ mod-a ---
[INFO] --- jar:3.4.1:jar (default-jar) @ mod-b ---
[INFO] --- jar:3.4.1:jar (default-jar) @ mod-c ---
[INFO] Building jar: /tmp/_233_reactor/mod-a/target/mod-a-1.0.0.jar
[INFO] Building jar: /tmp/_233_reactor/mod-b/target/mod-b-1.0.0.jar
[INFO] Building jar: /tmp/_233_reactor/mod-c/target/mod-c-1.0.0.jar
[INFO] Reactor Summary for #233 -T4 reactor reproducer 1.0.0:
[INFO] mod-a .............................................. SUCCESS [  0.495 s]
[INFO] mod-b .............................................. SUCCESS [  0.495 s]
[INFO] mod-c .............................................. SUCCESS [  0.494 s]
[INFO] BUILD SUCCESS
  mod-a -> Alpha.md 
  mod-b -> Bravo.md 
  mod-c -> Charlie.md 
Evidence: Consolidated before/after validation summary
# #233 — Maven `-T` parallel-build deadlock: test-phase validation

Reactor reproducer: 3 modules binding `metaobjects:docs` — **mod-a** + **mod-b**
share `<loader><name>shared</name>` (the Part B collision key); **mod-c** uses
`name=distinct`. Each declares one distinct entity (Alpha / Bravo / Charlie) so
per-module isolation is visible in the emitted docs. Built with `mvn -T4` against
`metaobjects-maven-plugin:7.20.11` installed from the respective commit.

## Part A — deadlock (warm-up) + Part B — loader-key collision

| Build | -T4 result | Failure signature |
|---|---|---|
| **Pre-fix** (base `6ea8deb7`) | **5/5 FAIL** | `MetaDataLoader [shared] is not usable. Phase: UNINITIALIZED (Not yet initialized)` |
| **Post-fix** (tip `a3ad3610`) | **6/6 PASS** (5 + 1 restore-confirm) | none — each module emits its own entity |

### Pre-fix behavior (the bug)
mod-a and mod-b race for one `activeLoaders` entry (old `buildLoaderKey()` =
`simpleName:subType:name`, no instance id). Whichever module **loses** the race has
its loader stay `UNINITIALIZED` and the `docs` goal errors out. The loser flips
run-to-run (mod-b lost in runs 1–3 → Alpha.md only; mod-a lost in runs 4–5 →
Bravo.md only). **mod-c** (distinct name, no collision) **always** succeeds —
proving the collision is scoped to the shared `<loader>` name. This is the exact
error named in the issue intent.

### Post-fix behavior (the fix)
All three modules build concurrently and emit **only their own** entity:
`mod-a → Alpha.md`, `mod-b → Bravo.md`, `mod-c → Charlie.md`. Zero
`Alpha` references leak into mod-b (content-checked). Reactor completes in ~0.6 s
wall-clock (~5 s user time ⇒ real parallelism, **no hang/deadlock**).

> The Part A cold first-init deadlock is probabilistic and, per the design doc,
> never surfaces end-to-end because the Part B collision fails the build first.
> Warm-up correctness (idempotent, single-threaded, thread-safe under 8 concurrent
> callers, initializes all three singletons) is proven by `RegistryBootstrapTest`;
> the absence of a hang under `-T4` is proven by the post-fix build completing.
> Faithful cold-init reproduction needs a fresh JVM — which is exactly what each
> `mvn -T4` reactor invocation is here.

## Part A/B — `threadSafe` mojo labeling (generate/verify/docs)

From the generated `META-INF/maven/plugin.xml`:
- `generate`, `verify`, `docs` → `<threadSafe>true</threadSafe>`
- `editor`, `agent-docs` → `<threadSafe>false</threadSafe>` (intentionally left
  unmarked — editor is direct-invocation; agent-docs is a throw-immediately stub)

## Unit tests (all green)
- `LoaderKeyIsolationTest` — 2/2 (same instance → same key; two same-named
  instances → distinct keys).
- `RegistryBootstrapTest` — 3/3 (initializes all three singletons; idempotent;
  thread-safe under 8 concurrent callers).
- `MojoThreadSafeDescriptorTest` — 1/1 (reads generated plugin.xml since `@Mojo`
  is CLASS-retention).

## Conclusion
The change satisfies the issue intent: the `-T` parallel-build collision/deadlock
is fixed (pre-fix 5/5 fail with the exact `UNINITIALIZED` signature; post-fix
6/6 pass with per-module isolation and no hang), the reactor-bound mojos are
honestly labeled `threadSafe=true`, and editor/agent-docs are deliberately left
unmarked. Behavior is byte-identical for the serial single-module case (the change
is an idempotent warm-up call + an instance discriminator on an internal dedup key).

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • mvn -pl metadata test -Dtest='LoaderKeyIsolationTest,RegistryBootstrapTest' — 5/5 methods green (Part B key uniqueness + Part A warm-up idempotency/thread-safety)
  • mvn -pl maven-plugin test -Dtest='MojoThreadSafeDescriptorTest' — 1/1 green (reads generated plugin.xml)
  • plugin.xml threadSafe inspection: generate/verify/docs=true; editor/agent-docs=false (intent requires editor/agent-docs unmarked)
  • mvn -pl maven-plugin -am install -DskipTests (fixed) + javap/strings on installed jars: RegistryBootstrap present, package-private buildLoaderKey, warmUpDefaults compiled into AbstractMetaDataMojo+MetaDataVerifyMojo
  • Authored 3-module reactor (mod-a+mod-b share <loader name=shared>, mod-c distinct) under mvn -T4: pre-fix (base 6ea8deb7) 5/5 FAIL with 'MetaDataLoader [shared] is not usable. Phase: UNINITIALIZED'; post-fix (tip a3ad3610) 6/6 PASS, mod-a→Alpha/mod-b→Bravo/mod-c→Charlie, 0 cross-module references, ~0.6s wall-clock (no deadlock)
  • git archive 6ea8deb7 to build pre-fix artifacts (re-extracted with spec/metamodel after first attempt missed the resource); reinstalled fixed artifacts and confirmed -T4 passes once more
  • git status --short on worktree: clean (target/ gitignored); /tmp scratch dirs removed; evidence files preserved
🔧 **Document** - 1 issue found → auto-fixed ✅
  • ℹ️ docs/features/cli.md:32 - The change makes generate/verify/docs threadSafe=true and resolves the -T reactor deadlock — a real user-facing capability unlock (pre-fix mvn -T deadlocked 8/8; post-fix 5/5 pass). No documentation mentions parallel-build safety anywhere; the CLI matrix (cli.md:32-33) and server/java/README.md:70 list the goals but say nothing about -T. A one-line note in the Java CLI-matrix row (and/or the Java README) that the JVM goals support parallel reactor builds would make this discoverable. Left unfixed: this is additive (no existing fact is stale) and the repo convention records capability changes in CHANGELOG at release time, so it is a doc-policy judgment call rather than a staleness repair.

🔧 Fix: docs(#233): note parallel-build safety for JVM goals
✅ Re-checked - no issues remain.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

dmealing and others added 9 commits August 2, 2026 17:18
…key fix)

Root cause: concurrent first-init of independently-locked process-global
registry singletons (defaultLoaderRegistry / getInstance via ConstraintEnforcer
/ ServiceRegistryFactory + JVM class-init) under `mvn -T` deadlocks on
lock-ordering. Secondary: activeLoaders keys by name-without-sources, so two
reactor modules sharing a <loader> name share one load (silent wrong output).

Design (Fable-reviewed, SOUND-WITH-CHANGES):
- Part A: deterministic single-thread warm-up of the three globals
  (RegistryBootstrap.warmUpDefaults), called from MetaDataLoader.init()
  (library-wide) + the mojo execute()s (Maven pre-init eager getTypeRegistry);
  mark generate/verify/docs threadSafe (labeling, ships atomically).
- Part B: per-instance id in buildLoaderKey so dedup only coalesces the same
  instance.
- Rejected per-loader createWithCoreProviders isolation (SPI pollution + N× cost;
  sealed registry is already read-only).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HLoJkFSyoticveo5ehMUAr
…invoker IT)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HLoJkFSyoticveo5ehMUAr
Two reactor modules with the same <loader> name shared one init() future
(buildLoaderKey was class:subType:name, no instance discriminator) — module
B's init() rode module A's future and returned A, leaving B's tree empty.
Append a process-unique instanceId so dedup only coalesces the same instance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HLoJkFSyoticveo5ehMUAr
Force-initialize the three process-global registry singletons on one thread
under one lock, so a concurrent first-init cannot deadlock on their
independent locks. Idempotent; failure propagates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HLoJkFSyoticveo5ehMUAr
Call RegistryBootstrap.warmUpDefaults() at the top of
MetaDataLoader.initWithConcurrencyProtection (covers every loader embedder)
and the generate/docs/editor + verify mojo execute()s (covers Maven's
pre-init eager getTypeRegistry() first-touch). Existing metadata (1272) +
maven-plugin (24) suites stay green; behavior byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HLoJkFSyoticveo5ehMUAr
Honest declaration now that the shared-state deadlock is fixed (warm-up +
per-instance loader key). Maven 3.x does not serialize non-threadSafe mojos
under -T — it only warns — so this is labeling that ships atomically with the
fix, and suppresses the (now-accurate) warning. editor (direct-invocation)
and agent-docs (stub) are correctly left unmarked. Descriptor test asserts it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HLoJkFSyoticveo5ehMUAr
Manual 3-module -T4 reactor (mod-a/mod-b share loader name, mod-c distinct):
pre-fix 8/8 runs fail on the shared-name module ("MetaDataLoader [shared] is
not usable. Phase: UNINITIALIZED" — the Part B collision), post-fix 5/5 pass
with per-module isolation. Declined a committed maven-invoker IT: Part B is
already deterministic in LoaderKeyIsolationTest and Part A's deadlock is
probabilistic, so the invoker infra adds mostly-redundant coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HLoJkFSyoticveo5ehMUAr
…old-init limit

Code review (report-only) found no correctness issues. Two minor fixes:
- public-repo hygiene: drop the embedded name/email/session-URL from the plan
  doc's example commit blocks (commit-message trailers are unaffected).
- honesty: javadoc on warmUpIsThreadSafeUnderConcurrentCallers noting it can't
  reproduce the cold first-init deadlock (singletons warm once per JVM); the
  faithful before/after is the manual -T4 reactor recorded in the design doc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HLoJkFSyoticveo5ehMUAr
@dmealing
dmealing merged commit a722b7f into main Aug 2, 2026
1 check passed
@dmealing
dmealing deleted the fix/233-maven-parallel-deadlock branch August 2, 2026 22:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

maven-plugin: generate/verify/docs mojos deadlock under parallel builds (mvn -T) — process-global registry singletons, not thread-safe

1 participant