From 018e5f86eaf9f3f27f1a4f4f988bae7090c98347 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Tue, 6 Feb 2024 00:42:59 +0400 Subject: [PATCH] introduce ref-value maps with evictionListener GitOrigin-RevId: 9f20025f191386a9d0c964c871a7c470fa4255a9 --- .../ContainerUtilCollectionsTest.java | 4 ++-- .../util/containers/CollectionFactory.java | 17 +++++++++++++++-- .../containers/ConcurrentRefValueHashMap.java | 14 ++++++++++++-- .../containers/ConcurrentSoftValueHashMap.java | 7 +++++++ .../containers/ConcurrentWeakValueHashMap.java | 7 +++++++ 5 files changed, 43 insertions(+), 6 deletions(-) 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 c3b5eb5ad511..99a606abc6c7 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/containers/ContainerUtilCollectionsTest.java +++ b/platform/platform-tests/testSrc/com/intellij/util/containers/ContainerUtilCollectionsTest.java @@ -183,7 +183,7 @@ public class ContainerUtilCollectionsTest extends Assert { @Test(timeout = TIMEOUT) public void testValueTossedEvenInCaseOfSuccessfulPutIfAbsentInConcurrentSoftValueMap() { - ConcurrentSoftValueHashMap map = new ConcurrentSoftValueHashMap<>(); + ConcurrentMap map = CollectionFactory.createConcurrentSoftValueMap(); Object value = new Object(); Reference ref = new SoftReference<>(value); map.put(1, value); @@ -196,7 +196,7 @@ public class ContainerUtilCollectionsTest extends Assert { while (ref.get()!=null); Object old = map.putIfAbsent(2, new Object()); assertNull(old); - assertFalse(map.processQueue()); + assertFalse(((ConcurrentRefValueHashMap)map).processQueue()); } @SuppressWarnings("ConstantValue") // Map contract is tested, not implied here diff --git a/platform/util/base/src/com/intellij/util/containers/CollectionFactory.java b/platform/util/base/src/com/intellij/util/containers/CollectionFactory.java index 484922ef59bc..ce14d8c942a5 100644 --- a/platform/util/base/src/com/intellij/util/containers/CollectionFactory.java +++ b/platform/util/base/src/com/intellij/util/containers/CollectionFactory.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.ConcurrentMap; +import java.util.function.Consumer; // ContainerUtil requires trove in classpath public final class CollectionFactory { @@ -30,12 +31,24 @@ public final class CollectionFactory { @Contract(value = " -> new", pure = true) public static @NotNull ConcurrentMap<@NotNull K, @NotNull V> createConcurrentWeakValueMap() { - return new ConcurrentWeakValueHashMap<>(); + return new ConcurrentWeakValueHashMap<>(null); } @Contract(value = " -> new", pure = true) public static @NotNull ConcurrentMap<@NotNull K, @NotNull V> createConcurrentSoftValueMap() { - return new ConcurrentSoftValueHashMap<>(); + return new ConcurrentSoftValueHashMap<>(null); + } + + @Contract(value = "_ -> new", pure = true) + public static @NotNull ConcurrentMap<@NotNull K, @NotNull V> createConcurrentWeakValueMap( + @NotNull Consumer evictionListener) { + return new ConcurrentWeakValueHashMap<>(evictionListener); + } + + @Contract(value = "_ -> new", pure = true) + public static @NotNull ConcurrentMap<@NotNull K, @NotNull V> createConcurrentSoftValueMap( + @NotNull Consumer evictionListener) { + return new ConcurrentSoftValueHashMap<>(evictionListener); } @Contract(value = " -> new", pure = true) 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 7acce96fae06..da2fe4e55708 100644 --- a/platform/util/base/src/com/intellij/util/containers/ConcurrentRefValueHashMap.java +++ b/platform/util/base/src/com/intellij/util/containers/ConcurrentRefValueHashMap.java @@ -4,12 +4,13 @@ package com.intellij.util.containers; import com.intellij.openapi.util.text.Strings; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.lang.ref.ReferenceQueue; -import java.util.HashSet; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.Consumer; /** * Base class for concurrent strong key:K -> (soft/weak) value:V map @@ -17,9 +18,15 @@ import java.util.concurrent.ConcurrentMap; * Null values are NOT allowed */ abstract class ConcurrentRefValueHashMap implements ConcurrentMap { + private final ConcurrentMap> myMap = new ConcurrentHashMap<>(); + private final Consumer myEvictionListener; protected final ReferenceQueue myQueue = new ReferenceQueue<>(); + ConcurrentRefValueHashMap(@Nullable Consumer evictionListener) { + myEvictionListener = evictionListener; + } + interface ValueReference { @NotNull K getKey(); @@ -35,7 +42,10 @@ abstract class ConcurrentRefValueHashMap implements ConcurrentMap { //noinspection unchecked ValueReference ref = (ValueReference)myQueue.poll(); if (ref == null) break; - myMap.remove(ref.getKey(), ref); + K key = ref.getKey(); + if (myMap.remove(key, ref) && myEvictionListener != null) { + myEvictionListener.accept(key); + } processed = true; } return processed; diff --git a/platform/util/base/src/com/intellij/util/containers/ConcurrentSoftValueHashMap.java b/platform/util/base/src/com/intellij/util/containers/ConcurrentSoftValueHashMap.java index dca294bc50c3..f85386ff1ff7 100644 --- a/platform/util/base/src/com/intellij/util/containers/ConcurrentSoftValueHashMap.java +++ b/platform/util/base/src/com/intellij/util/containers/ConcurrentSoftValueHashMap.java @@ -2,9 +2,11 @@ package com.intellij.util.containers; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; +import java.util.function.Consumer; /** * Concurrent strong key:K -> soft value:V map @@ -13,6 +15,11 @@ import java.lang.ref.SoftReference; * Use {@link CollectionFactory#createConcurrentSoftValueMap()} to create this */ final class ConcurrentSoftValueHashMap extends ConcurrentRefValueHashMap { + + ConcurrentSoftValueHashMap(@Nullable Consumer evictionListener) { + super(evictionListener); + } + private static final class MySoftReference extends SoftReference implements ValueReference { private final K key; private MySoftReference(@NotNull K key, @NotNull V referent, @NotNull ReferenceQueue q) { diff --git a/platform/util/base/src/com/intellij/util/containers/ConcurrentWeakValueHashMap.java b/platform/util/base/src/com/intellij/util/containers/ConcurrentWeakValueHashMap.java index ddc72132fe4c..e9a068f963b0 100644 --- a/platform/util/base/src/com/intellij/util/containers/ConcurrentWeakValueHashMap.java +++ b/platform/util/base/src/com/intellij/util/containers/ConcurrentWeakValueHashMap.java @@ -3,9 +3,11 @@ package com.intellij.util.containers; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; +import java.util.function.Consumer; /** * Concurrent strong key:K -> weak value:V map @@ -14,6 +16,11 @@ import java.lang.ref.WeakReference; * Use {@link ContainerUtil#createConcurrentWeakValueMap()} to create this */ final class ConcurrentWeakValueHashMap extends ConcurrentRefValueHashMap { + + ConcurrentWeakValueHashMap(@Nullable Consumer evictionListener) { + super(evictionListener); + } + private static final class MyWeakReference extends WeakReference implements ValueReference { private final K key; private MyWeakReference(@NotNull K key, @NotNull V referent, @NotNull ReferenceQueue q) {