introduce ref-value maps with evictionListener

GitOrigin-RevId: 9f20025f191386a9d0c964c871a7c470fa4255a9
This commit is contained in:
Gregory.Shrago
2024-02-06 00:42:59 +04:00
committed by intellij-monorepo-bot
parent 989a7dc786
commit 018e5f86ea
5 changed files with 43 additions and 6 deletions

View File

@@ -183,7 +183,7 @@ public class ContainerUtilCollectionsTest extends Assert {
@Test(timeout = TIMEOUT) @Test(timeout = TIMEOUT)
public void testValueTossedEvenInCaseOfSuccessfulPutIfAbsentInConcurrentSoftValueMap() { public void testValueTossedEvenInCaseOfSuccessfulPutIfAbsentInConcurrentSoftValueMap() {
ConcurrentSoftValueHashMap<Object, Object> map = new ConcurrentSoftValueHashMap<>(); ConcurrentMap<Object, Object> map = CollectionFactory.createConcurrentSoftValueMap();
Object value = new Object(); Object value = new Object();
Reference<Object> ref = new SoftReference<>(value); Reference<Object> ref = new SoftReference<>(value);
map.put(1, value); map.put(1, value);
@@ -196,7 +196,7 @@ public class ContainerUtilCollectionsTest extends Assert {
while (ref.get()!=null); while (ref.get()!=null);
Object old = map.putIfAbsent(2, new Object()); Object old = map.putIfAbsent(2, new Object());
assertNull(old); assertNull(old);
assertFalse(map.processQueue()); assertFalse(((ConcurrentRefValueHashMap<?, ?>)map).processQueue());
} }
@SuppressWarnings("ConstantValue") // Map contract is tested, not implied here @SuppressWarnings("ConstantValue") // Map contract is tested, not implied here

View File

@@ -10,6 +10,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;
// ContainerUtil requires trove in classpath // ContainerUtil requires trove in classpath
public final class CollectionFactory { public final class CollectionFactory {
@@ -30,12 +31,24 @@ public final class CollectionFactory {
@Contract(value = " -> new", pure = true) @Contract(value = " -> new", pure = true)
public static @NotNull <K, V> ConcurrentMap<@NotNull K, @NotNull V> createConcurrentWeakValueMap() { public static @NotNull <K, V> ConcurrentMap<@NotNull K, @NotNull V> createConcurrentWeakValueMap() {
return new ConcurrentWeakValueHashMap<>(); return new ConcurrentWeakValueHashMap<>(null);
} }
@Contract(value = " -> new", pure = true) @Contract(value = " -> new", pure = true)
public static @NotNull <K, V> ConcurrentMap<@NotNull K, @NotNull V> createConcurrentSoftValueMap() { public static @NotNull <K, V> ConcurrentMap<@NotNull K, @NotNull V> createConcurrentSoftValueMap() {
return new ConcurrentSoftValueHashMap<>(); return new ConcurrentSoftValueHashMap<>(null);
}
@Contract(value = "_ -> new", pure = true)
public static @NotNull <K, V> ConcurrentMap<@NotNull K, @NotNull V> createConcurrentWeakValueMap(
@NotNull Consumer<K> evictionListener) {
return new ConcurrentWeakValueHashMap<>(evictionListener);
}
@Contract(value = "_ -> new", pure = true)
public static @NotNull <K, V> ConcurrentMap<@NotNull K, @NotNull V> createConcurrentSoftValueMap(
@NotNull Consumer<K> evictionListener) {
return new ConcurrentSoftValueHashMap<>(evictionListener);
} }
@Contract(value = " -> new", pure = true) @Contract(value = " -> new", pure = true)

View File

@@ -4,12 +4,13 @@ package com.intellij.util.containers;
import com.intellij.openapi.util.text.Strings; import com.intellij.openapi.util.text.Strings;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
import java.util.HashSet;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;
/** /**
* Base class for concurrent strong key:K -> (soft/weak) value:V map * 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 * Null values are NOT allowed
*/ */
abstract class ConcurrentRefValueHashMap<K, V> implements ConcurrentMap<K, V> { abstract class ConcurrentRefValueHashMap<K, V> implements ConcurrentMap<K, V> {
private final ConcurrentMap<K, ValueReference<K, V>> myMap = new ConcurrentHashMap<>(); private final ConcurrentMap<K, ValueReference<K, V>> myMap = new ConcurrentHashMap<>();
private final Consumer<K> myEvictionListener;
protected final ReferenceQueue<V> myQueue = new ReferenceQueue<>(); protected final ReferenceQueue<V> myQueue = new ReferenceQueue<>();
ConcurrentRefValueHashMap(@Nullable Consumer<K> evictionListener) {
myEvictionListener = evictionListener;
}
interface ValueReference<K, V> { interface ValueReference<K, V> {
@NotNull @NotNull
K getKey(); K getKey();
@@ -35,7 +42,10 @@ abstract class ConcurrentRefValueHashMap<K, V> implements ConcurrentMap<K, V> {
//noinspection unchecked //noinspection unchecked
ValueReference<K, V> ref = (ValueReference<K, V>)myQueue.poll(); ValueReference<K, V> ref = (ValueReference<K, V>)myQueue.poll();
if (ref == null) break; 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; processed = true;
} }
return processed; return processed;

View File

@@ -2,9 +2,11 @@
package com.intellij.util.containers; package com.intellij.util.containers;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference; import java.lang.ref.SoftReference;
import java.util.function.Consumer;
/** /**
* Concurrent strong key:K -> soft value:V map * Concurrent strong key:K -> soft value:V map
@@ -13,6 +15,11 @@ import java.lang.ref.SoftReference;
* Use {@link CollectionFactory#createConcurrentSoftValueMap()} to create this * Use {@link CollectionFactory#createConcurrentSoftValueMap()} to create this
*/ */
final class ConcurrentSoftValueHashMap<K,V> extends ConcurrentRefValueHashMap<K,V> { final class ConcurrentSoftValueHashMap<K,V> extends ConcurrentRefValueHashMap<K,V> {
ConcurrentSoftValueHashMap(@Nullable Consumer<K> evictionListener) {
super(evictionListener);
}
private static final class MySoftReference<K, V> extends SoftReference<V> implements ValueReference<K, V> { private static final class MySoftReference<K, V> extends SoftReference<V> implements ValueReference<K, V> {
private final K key; private final K key;
private MySoftReference(@NotNull K key, @NotNull V referent, @NotNull ReferenceQueue<V> q) { private MySoftReference(@NotNull K key, @NotNull V referent, @NotNull ReferenceQueue<V> q) {

View File

@@ -3,9 +3,11 @@
package com.intellij.util.containers; package com.intellij.util.containers;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.function.Consumer;
/** /**
* Concurrent strong key:K -> weak value:V map * Concurrent strong key:K -> weak value:V map
@@ -14,6 +16,11 @@ import java.lang.ref.WeakReference;
* Use {@link ContainerUtil#createConcurrentWeakValueMap()} to create this * Use {@link ContainerUtil#createConcurrentWeakValueMap()} to create this
*/ */
final class ConcurrentWeakValueHashMap<K,V> extends ConcurrentRefValueHashMap<K,V> { final class ConcurrentWeakValueHashMap<K,V> extends ConcurrentRefValueHashMap<K,V> {
ConcurrentWeakValueHashMap(@Nullable Consumer<K> evictionListener) {
super(evictionListener);
}
private static final class MyWeakReference<K, V> extends WeakReference<V> implements ValueReference<K, V> { private static final class MyWeakReference<K, V> extends WeakReference<V> implements ValueReference<K, V> {
private final K key; private final K key;
private MyWeakReference(@NotNull K key, @NotNull V referent, @NotNull ReferenceQueue<V> q) { private MyWeakReference(@NotNull K key, @NotNull V referent, @NotNull ReferenceQueue<V> q) {