From 0fe1ce6791270e40ad1ac6c00d882c639d37266e Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 31 Jan 2024 13:26:48 +0100 Subject: [PATCH] IJPL-568 ConcurrentRefValueHashMap.putIfAbsent does not get rid of obsolete references when the insertion succeeds from the first try GitOrigin-RevId: 377c170b64fc92f732d33ce811601b6d20ea1a73 --- .../ContainerUtilCollectionsTest.java | 20 +++++++++++++++++++ .../containers/ConcurrentRefValueHashMap.java | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/platform/platform-tests/testSrc/com/intellij/util/containers/ContainerUtilCollectionsTest.java b/platform/platform-tests/testSrc/com/intellij/util/containers/ContainerUtilCollectionsTest.java index 78143e74d200..c3b5eb5ad511 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/containers/ContainerUtilCollectionsTest.java +++ b/platform/platform-tests/testSrc/com/intellij/util/containers/ContainerUtilCollectionsTest.java @@ -17,6 +17,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; import java.util.*; import java.util.concurrent.ConcurrentMap; @@ -179,6 +181,24 @@ public class ContainerUtilCollectionsTest extends Assert { checkValueTossedEventually(map); } + @Test(timeout = TIMEOUT) + public void testValueTossedEvenInCaseOfSuccessfulPutIfAbsentInConcurrentSoftValueMap() { + ConcurrentSoftValueHashMap map = new ConcurrentSoftValueHashMap<>(); + Object value = new Object(); + Reference 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 private void checkClearsEventuallyAfterGCPressure(Map map, @NotNull Runnable putKey) { assertTrue(map.isEmpty()); diff --git a/platform/util/base/src/com/intellij/util/containers/ConcurrentRefValueHashMap.java b/platform/util/base/src/com/intellij/util/containers/ConcurrentRefValueHashMap.java index 279c184d4b0f..7acce96fae06 100644 --- a/platform/util/base/src/com/intellij/util/containers/ConcurrentRefValueHashMap.java +++ b/platform/util/base/src/com/intellij/util/containers/ConcurrentRefValueHashMap.java @@ -61,9 +61,9 @@ abstract class ConcurrentRefValueHashMap implements ConcurrentMap { public V putIfAbsent(@NotNull K key, @NotNull V value) { ValueReference newRef = createValueReference(key, value); while (true) { + processQueue(); ValueReference oldRef = myMap.putIfAbsent(key, newRef); if (oldRef == null) return null; - processQueue(); V oldVal = oldRef.get(); if (oldVal == null) { if (myMap.replace(key, oldRef, newRef)) return null;