Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,8 @@ private static class DefaultModelBuilderRequest extends BaseRequest<Session> imp
this.activeProfileIds = activeProfileIds != null ? List.copyOf(activeProfileIds) : List.of();
this.inactiveProfileIds = inactiveProfileIds != null ? List.copyOf(inactiveProfileIds) : List.of();
this.systemProperties =
systemProperties != null ? Map.copyOf(systemProperties) : session.getSystemProperties();
this.userProperties = userProperties != null ? Map.copyOf(userProperties) : session.getUserProperties();
Map.copyOf(systemProperties != null ? systemProperties : session.getSystemProperties());
this.userProperties = Map.copyOf(userProperties != null ? userProperties : session.getUserProperties());
this.repositoryMerging = repositoryMerging;
this.repositories = repositories != null ? List.copyOf(validate(repositories)) : null;
this.lifecycleBindingsInjector = lifecycleBindingsInjector;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
package org.apache.maven.impl.cache;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
Expand Down Expand Up @@ -60,7 +60,14 @@ public abstract class AbstractRequestCache implements RequestCache {
@SuppressWarnings("all")
public <REQ extends Request<?>, REP extends Result<REQ>> REP request(REQ req, Function<REQ, REP> supplier) {
CachingSupplier<REQ, REP> cs = doCache(req, supplier);
return cs.apply(req);
try {
return cs.apply(req);
} catch (CachingSupplier.CyclicCacheAccessException e) {
// Re-entrant access from the same thread (e.g., a batch requests() computation
// triggered a singular request() that found the same cached entry). Compute
// directly with the caller's supplier to break the cycle.
return supplier.apply(req);
}
}

/**
Expand All @@ -85,7 +92,7 @@ public <REQ extends Request<?>, REP extends Result<REQ>> REP request(REQ req, Fu
@SuppressWarnings("unchecked")
public <REQ extends Request<?>, REP extends Result<REQ>> List<REP> requests(
List<REQ> reqs, Function<List<REQ>, List<REP>> supplier) {
final Map<REQ, Object> nonCachedResults = new HashMap<>();
final Map<REQ, Object> nonCachedResults = new IdentityHashMap<>();
List<RequestResult<REQ, REP>> allResults = new ArrayList<>(reqs.size());

Function<REQ, REP> individualSupplier = req -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
public class CachingSupplier<REQ, REP> implements Function<REQ, REP> {
protected final Function<REQ, REP> supplier;
protected volatile Object value;
// Guarded by synchronized(this) — tracks which thread is currently computing
private Thread computingThread;

public CachingSupplier(Function<REQ, REP> supplier) {
this.supplier = supplier;
Expand All @@ -46,10 +48,18 @@ public REP apply(REQ req) {
if ((v = value) == null) {
synchronized (this) {
if ((v = value) == null) {
if (computingThread == Thread.currentThread()) {
throw new CyclicCacheAccessException();
}
computingThread = Thread.currentThread();
try {
v = value = supplier.apply(req);
} catch (CyclicCacheAccessException e) {
throw e;
} catch (Exception e) {
v = value = new AltRes(e);
} finally {
computingThread = null;
}
}
}
Expand All @@ -60,6 +70,16 @@ public REP apply(REQ req) {
return (REP) v;
}

/**
* Thrown when a re-entrant call is detected on the same thread that is already
* computing this supplier's value. Prevents self-deadlock when a batch
* {@code requests()} computation triggers a singular {@code request()} that
* finds the same CachingSupplier in the cache.
*/
public static class CyclicCacheAccessException extends RuntimeException {
CyclicCacheAccessException() {}
}

/**
* Special holder class for exceptions that occur during supplier execution.
* Allows caching and re-throwing of exceptions on subsequent calls.
Expand Down
Loading