apache / apache/maven-toolchains-plugin

Race condition on cacheModified flag in ToolchainDiscoverer from parallelStream()

Open
#173 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
27
Forks
31
PR merge metrics
No merged PRs in 30d

Description

## Summary

`ToolchainDiscoverer.getToolchainModel()` is called from a `parallelStream()` in `discoverToolchains()`. The `cacheModified` volatile field is written concurrently by multiple threads without atomic coordination, which can cause cache updates to be lost.

## Location

`ToolchainDiscoverer.java:217-227`

https://github.com/apache/maven-toolchains-plugin/blob/master/src/main/java/org/apache/maven/plugins/toolchain/jdk/ToolchainDiscoverer.java#L217-L227

## Code

```java
ToolchainModel getToolchainModel(Path jdk) {
ToolchainModel model = cache.get(jdk);
if (model == null) {
model = doGetToolchainModel(jdk);
if (model != null) {
cache.put(jdk, model);
cacheModified = true;
}
}
return model;
}
```

Called from `discoverToolchains()` via parallel stream:

```java
List tcs = jdks.parallelStream()
.map(s -> {
ToolchainModel tc = getToolchainModel(s);
// ...
})
.sorted(...)
.collect(Collectors.toList());
writeCache();
```

## Problem

1. `cacheModified = true` is written from multiple threads in the parallel stream without synchronization
2. After the parallel stream completes, `writeCache()` reads `cacheModified`, writes the cache file, and sets it back to `false`
3. If a thread writes `cacheModified = true` after `writeCache()` has read it but before it sets it to `false`, the cache update is silently lost, and the newly discovered JDK won't be persisted
4. The `ConcurrentHashMap` itself is fine (thread-safe), but the coordination between `cacheModified` and the cache file write is not atomic

## Impact

Under concurrent load (which is the default for `parallelStream()`), some discovered JDK toolchains may not be persisted to the cache file. This means `writeCache()` may be called with `cacheModified == false` even though new entries were added during the stream.

## Suggested Fix

Use an `AtomicBoolean` for `cacheModified` or re-check cache for new entries at write time:

```java
private final AtomicBoolean cacheModified = new AtomicBoolean();

// In getToolchainModel:
cacheModified.set(true);

// In writeCache:
cacheModified.set(false);
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in ToolchainDiscoverer.java:217-227 and read how discoverToolchains() calls getToolchainModel() through the parallel stream before writeCache(). Run the existing project tests, then verify under parallel discovery that every newly cached JDK is persisted and no cache update is lost.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
build-system, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.