Skip to content

Commit 9fb8f11

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 9fb8f11

File tree

2 files changed

+601
-30
lines changed

2 files changed

+601
-30
lines changed

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

Lines changed: 30 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
}
@@ -237,26 +236,33 @@ public void close() {
237236
* closed
238237
* @since 6.0
239238
*/
239+
@SuppressWarnings("ReferenceEquality")
240240
@API(status = MAINTAINED, since = "6.0")
241241
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);
242+
requireNonNull(defaultCreator);
243+
var compositeKey = new CompositeKey<>(namespace, key);
244+
var storedValue = getStoredValue(compositeKey);
245245
var result = StoredValue.evaluateIfNotNull(storedValue);
246246
if (result == null) {
247-
StoredValue newStoredValue = this.storedValues.compute(compositeKey, (__, oldStoredValue) -> {
248-
if (StoredValue.evaluateIfNotNull(oldStoredValue) == null) {
247+
var newStoredValue = storedValues.compute(compositeKey, (__, oldStoredValue) -> {
248+
if (oldStoredValue == storedValue || oldStoredValue == null) {
249249
rejectIfClosed();
250-
var computedValue = Preconditions.notNull(defaultCreator.apply(key),
251-
"defaultCreator must not return null");
252-
return newStoredValue(() -> {
250+
return newStoredValue(new MemoizingSupplier(() -> {
253251
rejectIfClosed();
254-
return computedValue;
255-
});
252+
return requireNonNull(defaultCreator.apply(key));
253+
}));
256254
}
257255
return oldStoredValue;
258256
});
259-
return requireNonNull(newStoredValue.evaluate());
257+
try {
258+
return requireNonNull(newStoredValue.evaluate());
259+
}
260+
catch (Throwable t) { // remove failed entry to allow retry.
261+
if (newStoredValue == storedValues.get(compositeKey)) {
262+
storedValues.remove(compositeKey, newStoredValue);
263+
}
264+
throw t;
265+
}
260266
}
261267
return result;
262268
}
@@ -281,9 +287,7 @@ public <K, V> Object computeIfAbsent(N namespace, K key, Function<? super K, ? e
281287
public <K, V extends @Nullable Object> @Nullable V getOrComputeIfAbsent(N namespace, K key,
282288
Function<? super K, ? extends V> defaultCreator, Class<V> requiredType)
283289
throws NamespacedHierarchicalStoreException {
284-
285-
Object value = getOrComputeIfAbsent(namespace, key, defaultCreator);
286-
return castToRequiredType(key, value, requiredType);
290+
return castToRequiredType(key, getOrComputeIfAbsent(namespace, key, defaultCreator), requiredType);
287291
}
288292

289293
/**
@@ -306,9 +310,7 @@ public <K, V> Object computeIfAbsent(N namespace, K key, Function<? super K, ? e
306310
@API(status = MAINTAINED, since = "6.0")
307311
public <K, V> V computeIfAbsent(N namespace, K key, Function<? super K, ? extends V> defaultCreator,
308312
Class<V> requiredType) throws NamespacedHierarchicalStoreException {
309-
310-
Object value = computeIfAbsent(namespace, key, defaultCreator);
311-
return castNonNullToRequiredType(key, value, requiredType);
313+
return castNonNullToRequiredType(key, computeIfAbsent(namespace, key, defaultCreator), requiredType);
312314
}
313315

314316
/**
@@ -328,8 +330,8 @@ public <K, V> V computeIfAbsent(N namespace, K key, Function<? super K, ? extend
328330
public @Nullable Object put(N namespace, Object key, @Nullable Object value)
329331
throws NamespacedHierarchicalStoreException {
330332
rejectIfClosed();
331-
StoredValue oldValue = this.storedValues.put(new CompositeKey<>(namespace, key), newStoredValue(() -> value));
332-
return StoredValue.evaluateIfNotNull(oldValue);
333+
return StoredValue.evaluateIfNotNull(
334+
storedValues.put(new CompositeKey<>(namespace, key), newStoredValue(() -> value)));
333335
}
334336

335337
/**
@@ -347,8 +349,7 @@ public <K, V> V computeIfAbsent(N namespace, K key, Function<? super K, ? extend
347349
*/
348350
public @Nullable Object remove(N namespace, Object key) {
349351
rejectIfClosed();
350-
StoredValue previous = this.storedValues.remove(new CompositeKey<>(namespace, key));
351-
return StoredValue.evaluateIfNotNull(previous);
352+
return StoredValue.evaluateIfNotNull(storedValues.remove(new CompositeKey<>(namespace, key)));
352353
}
353354

354355
/**
@@ -368,16 +369,15 @@ public <K, V> V computeIfAbsent(N namespace, K key, Function<? super K, ? extend
368369
public <T> @Nullable T remove(N namespace, Object key, Class<T> requiredType)
369370
throws NamespacedHierarchicalStoreException {
370371
rejectIfClosed();
371-
Object value = remove(namespace, key);
372-
return castToRequiredType(key, value, requiredType);
372+
return castToRequiredType(key, remove(namespace, key), requiredType);
373373
}
374374

375375
private StoredValue newStoredValue(Supplier<@Nullable Object> value) {
376376
return new StoredValue(this.insertOrderSequence.getAndIncrement(), value);
377377
}
378378

379379
private @Nullable StoredValue getStoredValue(CompositeKey<N> compositeKey) {
380-
StoredValue storedValue = this.storedValues.get(compositeKey);
380+
StoredValue storedValue = storedValues.get(compositeKey);
381381
if (storedValue != null) {
382382
return storedValue;
383383
}

0 commit comments

Comments
 (0)