gnodet commented on issue #12445:
URL: https://github.com/apache/maven/issues/12445#issuecomment-4916148318

   ## Detailed Root-Cause Analysis
   
   Looking at the code and thread dump more carefully, here is the full call 
chain in the deadlock:
   
   ### Stack Trace Breakdown
   
   The main thread holds locks on **three** CachingSupplier instances, nested:
   
   ```
   CS_inner locked  <0x000000070dae4860>  ← CachingSupplier for the 
ResolverRequest (innermost)
   CS_outer locked  <0x000000070dadd910>  ← CachingSupplier for 
ArtifactResolverRequest
   CS_model locked  <0x000000070dad7c40>  ← CachingSupplier for model resolution
   ```
   
   The call chain is:
   
   1. `ProcessRemoteResourcesMojo.getProjects()` → Velocity template triggers 
project building
   2. `DefaultModelBuilder.resolveParent()` → needs external parent POM
   3. `DefaultModelResolver.resolveModel()` → **`session.request()`** 
(singular) → `doCache()` → `CS_model.apply()` → runs lambda
   4. Lambda → `AbstractSession.resolveArtifact()` → 
`ArtifactResolver.resolve()` → **`session.request()`** (singular) → `doCache()` 
→ `CS_outer.apply()` → runs `doResolve()`
   5. `doResolve()` → **`session.requests()`** (plural) → creates 
`individualSupplier` that waits on `nonCachedResults` HashMap
   6. For each ResolverRequest: `doCache(req, individualSupplier)` → creates 
`CS_inner(individualSupplier)` → added to `nonCached`
   7. Batch execution: `batchSupplier.apply(nonCached)` → calls 
`resolveArtifacts()` → **returns results**
   8. Results put into `nonCachedResults`, `notifyAll()` called
   9. **Result collection**: `CS_inner.apply(req)` → 
`individualSupplier.apply(req)` → `nonCachedResults.containsKey(req)` → 
**false** → `wait()`
   10. **DEADLOCK**: main thread waits forever, all worker threads idle
   
   ### The Critical Question
   
   At step 9, `nonCachedResults.containsKey(req)` returns false even though the 
batch completed at step 7. This means either:
   
   **A)** The batch supplier returned fewer results than `nonCached.size()` — 
in `DefaultArtifactResolver.doResolve()`:
   ```java
   List<ResolverResult> res = new ArrayList<>(resolverResults.size());
   for (int i = 0; i < resolverResults.size(); i++) {
       res.add(new ResolverResult(list.get(i), resolverResults.get(i)));
   }
   ```
   This iterates `resolverResults.size()`, not `list.size()`. If 
`resolveArtifacts()` returns fewer results than requests (e.g., due to 
deduplication), some requests won't be put into `nonCachedResults`.
   
   **B)** `doCache()` returned a CachingSupplier from a **previous** 
`requests()` invocation (via the session-scoped cache). That older 
CachingSupplier wraps an `individualSupplier` from a different call, which 
waits on a different `nonCachedResults` HashMap that will never be notified 
again. This can happen if:
      - A previous `requests()` call created the CachingSupplier
      - The CachingSupplier's soft reference was GC'd and recreated incorrectly
      - Or `computeIfAbsent` on the `RefConcurrentMap` has a race condition 
with eviction
   
   **C)** An uncaught exception (not `MavenExecutionException`) was thrown and 
handled in a way that makes `nonCachedResults` empty but still reaches the 
result collection loop.
   
   ### Suggested Fix Direction
   
   The `requests()` method should not use `wait()/notifyAll()` on a local 
HashMap shared between the `individualSupplier` closure and the batch 
execution. Instead, consider:
   
   1. **Direct approach**: After batch execution, directly set each 
`CachingSupplier.value` from the batch results, bypassing `individualSupplier` 
entirely for the result collection phase.
   
   2. **Defensive fix**: In the result collection loop (line 144), check if the 
result is already in `nonCachedResults` **before** calling `cs.apply()`. If it 
is, use the value directly instead of going through the CachingSupplier.
   
   3. **Timeout-based safety**: Add a timeout to `nonCachedResults.wait()` with 
a clear error message, so builds fail fast instead of hanging forever.
   
   ### Full Thread Dump
   
   The full thread dump is in the issue description. Key observations:
   - **Only the main thread is active** — all 15 worker threads 
(BfDependencyCollector, BasicRepositoryConnector, resolver) are idle in their 
thread pool queues
   - The main thread has been stuck for 60 seconds (elapsed=60.30s, 
cpu=3900.83ms)
   - No other thread is doing any work related to artifact resolution


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to