Skip to content

Commit 673e831

Browse files
author
Vincent Potucek
committed
[tryout fix] concurrency bug in NamespacedHierarchicalStore#computeIfAbsent(Object, Object, Function) junit-team#5171 junit-team#5209
Signed-off-by: Vincent Potucek <[email protected]>
1 parent c9454e3 commit 673e831

File tree

2 files changed

+600
-30
lines changed

2 files changed

+600
-30
lines changed

junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStore.java

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public void close() {
133133
try {
134134
if (this.closeAction != null) {
135135
List<Throwable> failures = new ArrayList<>();
136-
this.storedValues.entrySet().stream() //
136+
storedValues.entrySet().stream() //
137137
.map(e -> e.getValue().evaluateSafely(e.getKey())) //
138138
.filter(it -> it != null && it.value != null) //
139139
.sorted(EvaluatedValue.REVERSE_INSERT_ORDER) //
@@ -213,11 +213,10 @@ public void close() {
213213
CompositeKey<N> compositeKey = new CompositeKey<>(namespace, key);
214214
StoredValue storedValue = getStoredValue(compositeKey);
215215
if (storedValue == null) {
216-
storedValue = this.storedValues.computeIfAbsent(compositeKey,
217-
__ -> newStoredValue(new MemoizingSupplier(() -> {
218-
rejectIfClosed();
219-
return defaultCreator.apply(key);
220-
})));
216+
storedValue = storedValues.computeIfAbsent(compositeKey, __ -> newStoredValue(new MemoizingSupplier(() -> {
217+
rejectIfClosed();
218+
return defaultCreator.apply(key);
219+
})));
221220
}
222221
return storedValue.evaluate();
223222
}
@@ -239,24 +238,30 @@ public void close() {
239238
*/
240239
@API(status = MAINTAINED, since = "6.0")
241240
public <K, V> Object computeIfAbsent(N namespace, K key, Function<? super K, ? extends V> defaultCreator) {
242-
Preconditions.notNull(defaultCreator, "defaultCreator must not be null");
243-
CompositeKey<N> compositeKey = new CompositeKey<>(namespace, key);
244-
StoredValue storedValue = getStoredValue(compositeKey);
241+
requireNonNull(defaultCreator);
242+
var compositeKey = new CompositeKey<>(namespace, key);
243+
var storedValue = getStoredValue(compositeKey);
245244
var result = StoredValue.evaluateIfNotNull(storedValue);
246245
if (result == null) {
247-
StoredValue newStoredValue = this.storedValues.compute(compositeKey, (__, oldStoredValue) -> {
248-
if (StoredValue.evaluateIfNotNull(oldStoredValue) == null) {
246+
var newStoredValue = storedValues.compute(compositeKey, (__, oldStoredValue) -> {
247+
if (oldStoredValue == null || oldStoredValue.equals(storedValue)) {
249248
rejectIfClosed();
250-
var computedValue = Preconditions.notNull(defaultCreator.apply(key),
251-
"defaultCreator must not return null");
252-
return newStoredValue(() -> {
249+
return newStoredValue(new MemoizingSupplier(() -> {
253250
rejectIfClosed();
254-
return computedValue;
255-
});
251+
return requireNonNull(defaultCreator.apply(key));
252+
}));
256253
}
257254
return oldStoredValue;
258255
});
259-
return requireNonNull(newStoredValue.evaluate());
256+
try {
257+
return requireNonNull(newStoredValue.evaluate());
258+
}
259+
catch (Throwable t) { // remove failed entry to allow retry.
260+
if (newStoredValue.equals(storedValues.get(compositeKey))) {
261+
storedValues.remove(compositeKey, newStoredValue);
262+
}
263+
throw t;
264+
}
260265
}
261266
return result;
262267
}
@@ -281,9 +286,7 @@ public <K, V> Object computeIfAbsent(N namespace, K key, Function<? super K, ? e
281286
public <K, V extends @Nullable Object> @Nullable V getOrComputeIfAbsent(N namespace, K key,
282287
Function<? super K, ? extends V> defaultCreator, Class<V> requiredType)
283288
throws NamespacedHierarchicalStoreException {
284-
285-
Object value = getOrComputeIfAbsent(namespace, key, defaultCreator);
286-
return castToRequiredType(key, value, requiredType);
289+
return castToRequiredType(key, getOrComputeIfAbsent(namespace, key, defaultCreator), requiredType);
287290
}
288291

289292
/**
@@ -306,9 +309,7 @@ public <K, V> Object computeIfAbsent(N namespace, K key, Function<? super K, ? e
306309
@API(status = MAINTAINED, since = "6.0")
307310
public <K, V> V computeIfAbsent(N namespace, K key, Function<? super K, ? extends V> defaultCreator,
308311
Class<V> requiredType) throws NamespacedHierarchicalStoreException {
309-
310-
Object value = computeIfAbsent(namespace, key, defaultCreator);
311-
return castNonNullToRequiredType(key, value, requiredType);
312+
return castNonNullToRequiredType(key, computeIfAbsent(namespace, key, defaultCreator), requiredType);
312313
}
313314

314315
/**
@@ -328,8 +329,8 @@ public <K, V> V computeIfAbsent(N namespace, K key, Function<? super K, ? extend
328329
public @Nullable Object put(N namespace, Object key, @Nullable Object value)
329330
throws NamespacedHierarchicalStoreException {
330331
rejectIfClosed();
331-
StoredValue oldValue = this.storedValues.put(new CompositeKey<>(namespace, key), newStoredValue(() -> value));
332-
return StoredValue.evaluateIfNotNull(oldValue);
332+
return StoredValue.evaluateIfNotNull(
333+
storedValues.put(new CompositeKey<>(namespace, key), newStoredValue(() -> value)));
333334
}
334335

335336
/**
@@ -347,8 +348,7 @@ public <K, V> V computeIfAbsent(N namespace, K key, Function<? super K, ? extend
347348
*/
348349
public @Nullable Object remove(N namespace, Object key) {
349350
rejectIfClosed();
350-
StoredValue previous = this.storedValues.remove(new CompositeKey<>(namespace, key));
351-
return StoredValue.evaluateIfNotNull(previous);
351+
return StoredValue.evaluateIfNotNull(storedValues.remove(new CompositeKey<>(namespace, key)));
352352
}
353353

354354
/**
@@ -368,16 +368,15 @@ public <K, V> V computeIfAbsent(N namespace, K key, Function<? super K, ? extend
368368
public <T> @Nullable T remove(N namespace, Object key, Class<T> requiredType)
369369
throws NamespacedHierarchicalStoreException {
370370
rejectIfClosed();
371-
Object value = remove(namespace, key);
372-
return castToRequiredType(key, value, requiredType);
371+
return castToRequiredType(key, remove(namespace, key), requiredType);
373372
}
374373

375374
private StoredValue newStoredValue(Supplier<@Nullable Object> value) {
376375
return new StoredValue(this.insertOrderSequence.getAndIncrement(), value);
377376
}
378377

379378
private @Nullable StoredValue getStoredValue(CompositeKey<N> compositeKey) {
380-
StoredValue storedValue = this.storedValues.get(compositeKey);
379+
StoredValue storedValue = storedValues.get(compositeKey);
381380
if (storedValue != null) {
382381
return storedValue;
383382
}

0 commit comments

Comments
 (0)