IJPL-568 ConcurrentRefValueHashMap.putIfAbsent does not get rid of obsolete references when the insertion succeeds from the first try

GitOrigin-RevId: 377c170b64fc92f732d33ce811601b6d20ea1a73
This commit is contained in:
Alexey Kudravtsev
2024-01-31 13:26:48 +01:00
committed by intellij-monorepo-bot
parent 9a6807daab
commit 0fe1ce6791
2 changed files with 21 additions and 1 deletions

View File

@@ -17,6 +17,8 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TestRule; import org.junit.rules.TestRule;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@@ -179,6 +181,24 @@ public class ContainerUtilCollectionsTest extends Assert {
checkValueTossedEventually(map); checkValueTossedEventually(map);
} }
@Test(timeout = TIMEOUT)
public void testValueTossedEvenInCaseOfSuccessfulPutIfAbsentInConcurrentSoftValueMap() {
ConcurrentSoftValueHashMap<Object, Object> map = new ConcurrentSoftValueHashMap<>();
Object value = new Object();
Reference<Object> ref = new SoftReference<>(value);
map.put(1, value);
assertEquals(1, map.size());
value = null;
do {
GCUtil.tryGcSoftlyReachableObjects();
}
while (ref.get()!=null);
Object old = map.putIfAbsent(2, new Object());
assertNull(old);
assertFalse(map.processQueue());
}
@SuppressWarnings("ConstantValue") // Map contract is tested, not implied here @SuppressWarnings("ConstantValue") // Map contract is tested, not implied here
private void checkClearsEventuallyAfterGCPressure(Map<Object, Object> map, @NotNull Runnable putKey) { private void checkClearsEventuallyAfterGCPressure(Map<Object, Object> map, @NotNull Runnable putKey) {
assertTrue(map.isEmpty()); assertTrue(map.isEmpty());

View File

@@ -61,9 +61,9 @@ abstract class ConcurrentRefValueHashMap<K, V> implements ConcurrentMap<K, V> {
public V putIfAbsent(@NotNull K key, @NotNull V value) { public V putIfAbsent(@NotNull K key, @NotNull V value) {
ValueReference<K, V> newRef = createValueReference(key, value); ValueReference<K, V> newRef = createValueReference(key, value);
while (true) { while (true) {
processQueue();
ValueReference<K, V> oldRef = myMap.putIfAbsent(key, newRef); ValueReference<K, V> oldRef = myMap.putIfAbsent(key, newRef);
if (oldRef == null) return null; if (oldRef == null) return null;
processQueue();
V oldVal = oldRef.get(); V oldVal = oldRef.get();
if (oldVal == null) { if (oldVal == null) {
if (myMap.replace(key, oldRef, newRef)) return null; if (myMap.replace(key, oldRef, newRef)) return null;