support eviction listener for Concurrent(Soft/Weak)Map

GitOrigin-RevId: 97682fca3c6db1f9ac64caced9348f11b16b4343
This commit is contained in:
Alexey Kudravtsev
2024-02-21 14:45:01 +01:00
committed by intellij-monorepo-bot
parent c705e3d84f
commit 473c6b0714
5 changed files with 47 additions and 30 deletions

View File

@@ -361,7 +361,11 @@ public final class CollectionFactory {
@Contract(value = " -> new", pure = true)
public static @NotNull <K, V> ConcurrentMap<@NotNull K, @NotNull V> createConcurrentSoftMap() {
return new ConcurrentSoftHashMap<>();
return new ConcurrentSoftHashMap<>(null);
}
@Contract(value = "_ -> new", pure = true)
public static @NotNull <K, V> ConcurrentMap<@NotNull K, @NotNull V> createConcurrentSoftMap(@NotNull Consumer<? super V> evictionListener) {
return new ConcurrentSoftHashMap<>(evictionListener);
}
@Contract(value = "_,_,_,_-> new", pure = true)

View File

@@ -8,6 +8,7 @@ import java.lang.ref.ReferenceQueue;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;
/**
* Base class for concurrent (soft/weak) key:K -> strong value:V map
@@ -19,9 +20,25 @@ abstract class ConcurrentRefHashMap<K, V> extends AbstractMap<K, V> implements C
private final ConcurrentMap<KeyReference<K>, V> myMap; // hashing strategy must be canonical, we compute corresponding hash codes using our own myHashingStrategy
private final @NotNull HashingStrategy<? super K> myHashingStrategy;
private static final float LOAD_FACTOR = 0.75f;
static final float LOAD_FACTOR = 0.75f;
static final int DEFAULT_CAPACITY = 16;
static final int DEFAULT_CONCURRENCY_LEVEL = Math.min(Runtime.getRuntime().availableProcessors(), 4);
private final Consumer<? super V> myEvictionListener;
ConcurrentRefHashMap(@Nullable Consumer<? super V> evictionListener) {
myHashingStrategy = this;
myMap = new ConcurrentHashMap<>(DEFAULT_CAPACITY, LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
myEvictionListener = evictionListener;
}
ConcurrentRefHashMap(int initialCapacity,
float loadFactor,
int concurrencyLevel,
@Nullable HashingStrategy<? super K> hashingStrategy) {
myHashingStrategy = hashingStrategy == THIS ? this : (hashingStrategy == null ? HashingStrategy.canonical() : hashingStrategy);
myMap = new ConcurrentHashMap<>(initialCapacity, loadFactor, concurrencyLevel);
myEvictionListener = null;
}
@FunctionalInterface
interface KeyReference<K> {
@@ -47,19 +64,14 @@ abstract class ConcurrentRefHashMap<K, V> extends AbstractMap<K, V> implements C
boolean processed = false;
//noinspection unchecked
while ((wk = (KeyReference<K>)myReferenceQueue.poll()) != null) {
myMap.remove(wk);
V v = myMap.remove(wk);
if (myEvictionListener != null) {
myEvictionListener.accept(v);
}
processed = true;
}
return processed;
}
ConcurrentRefHashMap() {
this(DEFAULT_CAPACITY);
}
private ConcurrentRefHashMap(int initialCapacity) {
this(initialCapacity, LOAD_FACTOR);
}
private static final HashingStrategy<?> THIS = new HashingStrategy<Object>() {
@Override
public int hashCode(Object object) {
@@ -72,23 +84,6 @@ abstract class ConcurrentRefHashMap<K, V> extends AbstractMap<K, V> implements C
}
};
private ConcurrentRefHashMap(int initialCapacity, float loadFactor) {
//noinspection unchecked
this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL, (HashingStrategy<? super K>)THIS);
}
ConcurrentRefHashMap(@NotNull HashingStrategy<? super K> hashingStrategy) {
this(DEFAULT_CAPACITY, LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL, hashingStrategy);
}
ConcurrentRefHashMap(int initialCapacity,
float loadFactor,
int concurrencyLevel,
@Nullable HashingStrategy<? super K> hashingStrategy) {
myHashingStrategy = hashingStrategy == THIS ? this : (hashingStrategy == null ? HashingStrategy.canonical() : hashingStrategy);
myMap = new ConcurrentHashMap<>(initialCapacity, loadFactor, concurrencyLevel);
}
@Override
public int size() {
return entrySet().size();

View File

@@ -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 soft key:K -> strong value:V map
@@ -13,7 +15,8 @@ import java.lang.ref.SoftReference;
* Use {@link ContainerUtil#createConcurrentSoftMap()} to create this
*/
final class ConcurrentSoftHashMap<K, V> extends ConcurrentRefHashMap<K, V> {
ConcurrentSoftHashMap() {
ConcurrentSoftHashMap(@Nullable Consumer<? super V> evictionListener) {
super(evictionListener);
}
ConcurrentSoftHashMap(int initialCapacity,

View File

@@ -24,7 +24,7 @@ final class ConcurrentWeakHashMap<K, V> extends ConcurrentRefHashMap<K, V> {
}
ConcurrentWeakHashMap(@NotNull HashingStrategy<? super K> hashingStrategy) {
super(hashingStrategy);
super(DEFAULT_CAPACITY, LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL, hashingStrategy);
}
private static final class WeakKey<K> extends WeakReference<K> implements KeyReference<K> {