| /* | |
| * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package java.lang; | |
| import java.util.WeakHashMap; | |
| import java.lang.ref.WeakReference; | |
| import java.util.concurrent.atomic.AtomicInteger; | |
| import jdk.internal.misc.Unsafe; | |
| import static java.lang.ClassValue.ClassValueMap.probeHomeLocation; | |
| import static java.lang.ClassValue.ClassValueMap.probeBackupLocations; | |
| /** | |
| * Lazily associate a computed value with any {@code Class} object. | |
| * For example, if a dynamic language needs to construct a message dispatch | |
| * table for each class encountered at a message send call site, | |
| * it can use a {@code ClassValue} to cache information needed to | |
| * perform the message send quickly, for each class encountered. | |
| * <p> | |
| * The basic operation of a {@code ClassValue} is {@link #get get}, which | |
| * returns the associated value, initially created by an invocation to {@link | |
| * #computeValue computeValue}; multiple invocations may happen under race, but | |
| * exactly one value is associated to a {@code Class} and returned. | |
| * <p> | |
| * Another operation is {@link #remove remove}: it clears the associated value | |
| * (if it exists), and ensures the next associated value is computed with input | |
| * states up-to-date with the removal. | |
| * <p> | |
| * For a particular association, there is a total order for accesses to the | |
| * associated value. Accesses are atomic; they include: | |
| * <ul> | |
| * <li>A read-only access by {@code get}</li> | |
| * <li>An attempt to associate the return value of a {@code computeValue} by | |
| * {@code get}</li> | |
| * <li>Clearing of an association by {@code remove}</li> | |
| * </ul> | |
| * A {@code get} call always include at least one access; a {@code remove} call | |
| * always has exactly one access; a {@code computeValue} call always happens | |
| * between two accesses. This establishes the order of {@code computeValue} | |
| * calls with respect to {@code remove} calls and determines whether the | |
| * results of a {@code computeValue} can be successfully associated by a {@code | |
| * get}. | |
| * | |
| * @param <T> the type of the associated value | |
| * @author John Rose, JSR 292 EG | |
| * @since 1.7 | |
| */ | |
| public abstract class ClassValue<T> { | |
| /** | |
| * Sole constructor. (For invocation by subclass constructors, typically | |
| * implicit.) | |
| */ | |
| protected ClassValue() { | |
| } | |
| /** | |
| * Computes the value to associate to the given {@code Class}. | |
| * <p> | |
| * This method is invoked when the initial read-only access by {@link #get | |
| * get} finds no associated value. | |
| * <p> | |
| * If this method throws an exception, the initiating {@code get} call will | |
| * not attempt to associate a value, and may terminate by returning the | |
| * associated value if it exists, or by propagating that exception otherwise. | |
| * <p> | |
| * Otherwise, the value is computed and returned. An attempt to associate | |
| * the return value happens, with one of the following outcomes: | |
| * <ul> | |
| * <li>The associated value is present; it is returned and no association | |
| * is done.</li> | |
| * <li>The most recent {@link #remove remove} call, if it exists, does not | |
| * happen-before (JLS {@jls 17.4.5}) the finish of the {@code computeValue} | |
| * that computed the value to associate. A new invocation to {@code | |
| * computeValue}, which that {@code remove} call happens-before, will | |
| * re-establish this happens-before relationship.</li> | |
| * <li>Otherwise, this value is successfully associated and returned.</li> | |
| * </ul> | |
| * | |
| * @apiNote | |
| * A {@code computeValue} call may, due to class loading or other | |
| * circumstances, recursively call {@code get} or {@code remove} for the | |
| * same {@code type}. The recursive {@code get}, if the recursion stops, | |
| * successfully finishes and this initiating {@code get} observes the | |
| * associated value from recursion. The recursive {@code remove} is no-op, | |
| * since being on the same thread, the {@code remove} already happens-before | |
| * the finish of this {@code computeValue}; the result from this {@code | |
| * computeValue} still may be associated. | |
| * | |
| * @param type the {@code Class} to associate a value to | |
| * @return the newly computed value to associate | |
| * @see #get | |
| * @see #remove | |
| */ | |
| protected abstract T computeValue(Class<?> type); | |
| /** | |
| * {@return the value associated to the given {@code Class}} | |
| * <p> | |
| * This method first performs a read-only access, and returns the associated | |
| * value if it exists. Otherwise, this method tries to associate a value | |
| * from a {@link #computeValue computeValue} invocation until the associated | |
| * value exists, which could be associated by a competing thread. | |
| * <p> | |
| * This method may throw an exception from a {@code computeValue} invocation. | |
| * In this case, no association happens. | |
| * | |
| * @param type the {@code Class} to retrieve the associated value for | |
| * @throws NullPointerException if the argument is {@code null} | |
| * @see #remove | |
| * @see #computeValue | |
| */ | |
| public T get(Class<?> type) { | |
| // non-racing this.hashCodeForCache : final int | |
| Entry<?>[] cache; | |
| Entry<T> e = probeHomeLocation(cache = getCacheCarefully(type), this); | |
| // racing e : current value <=> stale value from current cache or from stale cache | |
| // invariant: e is null or an Entry with readable Entry.version and Entry.value | |
| if (match(e)) | |
| // invariant: No false positive matches. False negatives are OK if rare. | |
| // The key fact that makes this work: if this.version == e.version, | |
| // then this thread has a right to observe (final) e.value. | |
| return e.value; | |
| // The fast path can fail for any of these reasons: | |
| // 1. no entry has been computed yet | |
| // 2. hash code collision (before or after reduction mod cache.length) | |
| // 3. an entry has been removed (either on this type or another) | |
| // 4. the GC has somehow managed to delete e.version and clear the reference | |
| return getFromBackup(cache, type); | |
| } | |
| /** | |
| * Removes the associated value for the given {@code Class} and invalidates | |
| * all out-of-date computations. If this association is subsequently | |
| * {@linkplain #get accessed}, this removal happens-before (JLS {@jls | |
| * 17.4.5}) the finish of the {@link #computeValue computeValue} call that | |
| * returned the associated value. | |
| * | |
| * @param type the type whose class value must be removed | |
| * @throws NullPointerException if the argument is {@code null} | |
| */ | |
| public void remove(Class<?> type) { | |
| ClassValueMap map = getMap(type); | |
| map.removeAccess(this); | |
| } | |
| // Possible functionality for JSR 292 MR 1 | |
| /*public*/ void put(Class<?> type, T value) { | |
| ClassValueMap map = getMap(type); | |
| map.forcedAssociateAccess(this, value); | |
| } | |
| //| -------- | |
| //| Implementation... | |
| //| -------- | |
| /** Return the cache, if it exists, else a dummy empty cache. */ | |
| private static Entry<?>[] getCacheCarefully(Class<?> type) { | |
| // racing type.classValueMap{.cacheArray} : null => new Entry[X] <=> new Entry[Y] | |
| ClassValueMap map = type.classValueMap; | |
| if (map == null) return EMPTY_CACHE; | |
| // reads non-null due to StoreStore barrier in critical section in initializeMap | |
| Entry<?>[] cache = map.getCache(); | |
| return cache; | |
| // invariant: returned value is safe to dereference and check for an Entry | |
| } | |
| /** Initial, one-element, empty cache used by all Class instances. Must never be filled. */ | |
| private static final Entry<?>[] EMPTY_CACHE = { null }; | |
| /** | |
| * Slow tail of ClassValue.get to retry at nearby locations in the cache, | |
| * or take a slow lock and check the hash table. | |
| * Called only if the first probe was empty or a collision. | |
| * This is a separate method, so compilers can process it independently. | |
| */ | |
| private T getFromBackup(Entry<?>[] cache, Class<?> type) { | |
| Entry<T> e = probeBackupLocations(cache, this); | |
| if (e != null) | |
| return e.value; | |
| return getFromHashMap(type); | |
| } | |
| // Hack to suppress warnings on the (T) cast, which is a no-op. | |
| @SuppressWarnings("unchecked") | |
| Entry<T> castEntry(Entry<?> e) { return (Entry<T>) e; } | |
| /** Called when the fast path of get fails, and cache reprobe also fails. | |
| */ | |
| private T getFromHashMap(Class<?> type) { | |
| // The fail-safe recovery is to fall back to the underlying classValueMap. | |
| ClassValueMap map = getMap(type); | |
| var accessed = map.readAccess(this); | |
| if (accessed instanceof Entry) { | |
| @SuppressWarnings("unchecked") | |
| var cast = (Entry<T>) accessed; | |
| return cast.value; | |
| } | |
| RemovalToken token = (RemovalToken) accessed; // nullable | |
| for (; ; ) { | |
| T value; | |
| try { | |
| value = computeValue(type); | |
| } catch (Throwable ex) { | |
| // no value is associated, but there may be already associated | |
| // value. Return that if it exists. | |
| accessed = map.readAccess(this); | |
| if (accessed instanceof Entry) { | |
| @SuppressWarnings("unchecked") | |
| var cast = (Entry<T>) accessed; | |
| return cast.value; | |
| } | |
| // report failure here, but allow other callers to try again | |
| if (ex instanceof RuntimeException rte) { | |
| throw rte; | |
| } else { | |
| throw ex instanceof Error err ? err : new Error(ex); | |
| } | |
| } | |
| // computeValue succeed, proceed to associate | |
| accessed = map.associateAccess(this, token, value); | |
| if (accessed instanceof Entry) { | |
| @SuppressWarnings("unchecked") | |
| var cast = (Entry<T>) accessed; | |
| return cast.value; | |
| } else { | |
| token = (RemovalToken) accessed; | |
| // repeat | |
| } | |
| } | |
| } | |
| /** Check that e is non-null, matches this ClassValue, and is live. */ | |
| boolean match(Entry<?> e) { | |
| // racing e.version : null (blank) => unique Version token => null (GC-ed version) | |
| // non-racing this.version : v1 => v2 => ... (updates are read faithfully from volatile) | |
| return (e != null && e.version() == this.version); | |
| // invariant: No false positives on version match. Null is OK for false negative. | |
| // invariant: If version matches, then e.value is readable (final set in Entry.<init>) | |
| } | |
| /** Internal hash code for accessing Class.classValueMap.cacheArray. */ | |
| final int hashCodeForCache = nextHashCode.getAndAdd(HASH_INCREMENT) & HASH_MASK; | |
| /** Value stream for hashCodeForCache. See similar structure in ThreadLocal. */ | |
| private static final AtomicInteger nextHashCode = new AtomicInteger(); | |
| /** Good for power-of-two tables. See similar structure in ThreadLocal. */ | |
| private static final int HASH_INCREMENT = 0x61c88647; | |
| /** Mask a hash code to be positive but not too large, to prevent wraparound. */ | |
| static final int HASH_MASK = (-1 >>> 2); | |
| /** | |
| * Private key for retrieval of this object from ClassValueMap. | |
| */ | |
| static class Identity { | |
| } | |
| /** | |
| * This ClassValue's identity, expressed as an opaque object. | |
| * The main object {@code ClassValue.this} is incorrect since | |
| * subclasses may override {@code ClassValue.equals}, which | |
| * could confuse keys in the ClassValueMap. | |
| */ | |
| final Identity identity = new Identity(); | |
| /** | |
| * Current version for retrieving this class value from the cache. | |
| * Any number of computeValue calls can be cached in association with one version. | |
| * But the version changes when a remove (on any type) is executed. | |
| * A version change invalidates all cache entries for the affected ClassValue, | |
| * by marking them as stale. Stale cache entries do not force another call | |
| * to computeValue, but they do require a synchronized visit to a backing map. | |
| * <p> | |
| * All user-visible state changes on the ClassValue take place under | |
| * a lock inside the synchronized methods of ClassValueMap. | |
| * Readers (of ClassValue.get) are notified of such state changes | |
| * when this.version is bumped to a new token. | |
| * This variable must be volatile so that an unsynchronized reader | |
| * will receive the notification without delay. | |
| * <p> | |
| * If version were not volatile, one thread T1 could persistently hold onto | |
| * a stale value this.value == V1, while another thread T2 advances | |
| * (under a lock) to this.value == V2. This will typically be harmless, | |
| * but if T1 and T2 interact causally via some other channel, such that | |
| * T1's further actions are constrained (in the JMM) to happen after | |
| * the V2 event, then T1's observation of V1 will be an error. | |
| * <p> | |
| * The practical effect of making this.version be volatile is that it cannot | |
| * be hoisted out of a loop (by an optimizing JIT) or otherwise cached. | |
| * Some machines may also require a barrier instruction to execute | |
| * before this.version. | |
| */ | |
| private volatile Version<T> version = new Version<>(this); | |
| void bumpVersion() { version = new Version<>(this); } | |
| record Version<T>(/* Object identity, */ClassValue<T> classValue) { | |
| boolean isLive() { | |
| return classValue.version == this; | |
| } | |
| } | |
| /** | |
| * Besides a value (represented by an Entry), a "removal token" object, | |
| * including the value {@code null}, can be present at a ClassValue-Class | |
| * coordinate. A removal token indicates whether the value from a | |
| * computation is up-to-date; the value is up-to-date if the token is the | |
| * same before and after computation (no removal during this period), or if | |
| * the token is from the same thread (removed during computeValue). | |
| * {@code null} is the initial state, meaning all computations are valid. | |
| * Later tokens are always non-null, no matter if they replace existing | |
| * entries or outdated tokens. | |
| */ | |
| private static final class RemovalToken { | |
| // Use thread ID, which presumably don't duplicate and is cheaper than WeakReference | |
| private final long actorId; | |
| private RemovalToken() { | |
| this.actorId = Thread.currentThread().threadId(); | |
| } | |
| // Arguments are intentionally nullable, to allow initial tokens | |
| private static boolean allowsAssociation(RemovalToken current, RemovalToken start) { | |
| // No removal token after the initial can be null | |
| assert current != null || start == null : current + " : " + start; | |
| return current == start || current.actorId == Thread.currentThread().threadId(); | |
| } | |
| } | |
| /** One binding of a value to a class via a ClassValue. | |
| * Shared for the map and the cache array. | |
| * The version is only meaningful for the cache array; whatever in the map | |
| * is authentic, but state informs the cache an entry may be out-of-date. | |
| * States are:<ul> | |
| * <li> dead if version == null | |
| * <li> stale if version != classValue.version | |
| * <li> else live </ul> | |
| * Once an entry goes stale, it can be reset at any time | |
| * into the dead state. | |
| */ | |
| static final class Entry<T> { | |
| final T value; | |
| final WeakReference<Version<T>> version; // The version exists only for cache invalidation | |
| Entry(Version<T> version, T value) { | |
| this.value = value; | |
| this.version = new WeakReference<>(version); | |
| } | |
| Version<T> version() { return version.get(); } | |
| ClassValue<T> classValueOrNull() { | |
| Version<T> v = version(); | |
| return (v == null) ? null : v.classValue(); | |
| } | |
| boolean isLive() { | |
| Version<T> v = version(); | |
| if (v == null) return false; | |
| if (v.isLive()) return true; | |
| version.clear(); | |
| return false; | |
| } | |
| Entry<T> refreshVersion(Version<T> v2) { | |
| return version.refersTo(v2) ? this : new Entry<>(v2, value); | |
| } | |
| static final Entry<?> DEAD_ENTRY = new Entry<>(null, null); | |
| } | |
| /** Return the backing map associated with this type. */ | |
| private static ClassValueMap getMap(Class<?> type) { | |
| // racing type.classValueMap : null (blank) => unique ClassValueMap | |
| // if a null is observed, a map is created (lazily, synchronously, uniquely) | |
| // all further access to that map is synchronized | |
| ClassValueMap map = type.classValueMap; | |
| if (map != null) return map; | |
| return initializeMap(type); | |
| } | |
| private static final Object CRITICAL_SECTION = new Object(); | |
| private static final Unsafe UNSAFE = Unsafe.getUnsafe(); | |
| private static ClassValueMap initializeMap(Class<?> type) { | |
| ClassValueMap map; | |
| synchronized (CRITICAL_SECTION) { // private object to avoid deadlocks | |
| // happens about once per type | |
| if ((map = type.classValueMap) == null) { | |
| map = new ClassValueMap(); | |
| // getCacheCarefully anticipates entry array to be non-null when | |
| // a ClassValueMap is published to it. However, ClassValueMap | |
| // has no final field, so compiler does not emit a fence, and | |
| // we must manually issue a Store-Store barrier to prevent | |
| // the assignment below to be reordered with the store to | |
| // entry array in the constructor above | |
| UNSAFE.storeFence(); | |
| type.classValueMap = map; | |
| } | |
| } | |
| return map; | |
| } | |
| static <T> Entry<T> makeEntry(Version<T> explicitVersion, T value) { | |
| // Note that explicitVersion might be different from this.version. | |
| return new Entry<>(explicitVersion, value); | |
| // As soon as the Entry is put into the cache, the value will be | |
| // reachable via a data race (as defined by the Java Memory Model). | |
| // This race is benign, assuming the value object itself can be | |
| // read safely by multiple threads. This is up to the user. | |
| // | |
| // The entry and version fields themselves can be safely read via | |
| // a race because they are either final or have controlled states. | |
| // If the pointer from the entry to the version is still null, | |
| // or if the version goes immediately dead and is nulled out, | |
| // the reader will take the slow path and retry under a lock. | |
| } | |
| // The following class could also be top level and non-public: | |
| /** A backing map for all ClassValues. | |
| * Gives a fully serialized "true state" for each pair (ClassValue cv, Class type). | |
| * The state may be assigned value or unassigned token. | |
| * Also manages an unserialized fast-path cache. | |
| */ | |
| static final class ClassValueMap extends WeakHashMap<ClassValue.Identity, Object> { | |
| private Entry<?>[] cacheArray; | |
| private int cacheLoad, cacheLoadLimit; | |
| /** Number of entries initially allocated to each type when first used with any ClassValue. | |
| * It would be pointless to make this much smaller than the Class and ClassValueMap objects themselves. | |
| * Must be a power of 2. | |
| */ | |
| private static final int INITIAL_ENTRIES = 32; | |
| /** Build a backing map for ClassValues. | |
| * Also, create an empty cache array and install it on the class. | |
| */ | |
| ClassValueMap() { | |
| sizeCache(INITIAL_ENTRIES); | |
| } | |
| Entry<?>[] getCache() { return cacheArray; } | |
| // A simple read access to this map, for the initial step of get or failure recovery. | |
| // This may refresh the entry for the cache, but the associated value always stays the same. | |
| synchronized <T> Object readAccess(ClassValue<T> classValue) { | |
| var item = get(classValue.identity); | |
| if (item instanceof Entry) { | |
| @SuppressWarnings("unchecked") | |
| var entry = (Entry<T>) item; | |
| // cache refresh | |
| var updated = entry.refreshVersion(classValue.version); | |
| if (updated != entry) { | |
| put(classValue.identity, updated); | |
| } | |
| // Add to the cache, to enable the fast path, next time. | |
| checkCacheLoad(); | |
| addToCache(classValue, updated); | |
| } | |
| return item; | |
| } | |
| // An association attempt, for when a computeValue returns a value. | |
| synchronized <T> Object associateAccess(ClassValue<T> classValue, RemovalToken startToken, T value) { | |
| var item = readAccess(classValue); | |
| if (item instanceof Entry) | |
| return item; // value already associated | |
| var currentToken = (RemovalToken) item; | |
| if (RemovalToken.allowsAssociation(currentToken, startToken)) { | |
| var entry = makeEntry(classValue.version, value); | |
| put(classValue.identity, entry); | |
| // Add to the cache, to enable the fast path, next time. | |
| checkCacheLoad(); | |
| addToCache(classValue, entry); | |
| return entry; | |
| } | |
| return currentToken; | |
| } | |
| // A removal, requiring subsequent associations to be up-to-date with it. | |
| synchronized void removeAccess(ClassValue<?> classValue) { | |
| // Always put in a token to invalidate ongoing computations | |
| put(classValue.identity, new RemovalToken()); | |
| classValue.bumpVersion(); | |
| removeStaleEntries(classValue); | |
| } | |
| // A forced association, requires cache to flush. | |
| synchronized <T> void forcedAssociateAccess(ClassValue<T> classValue, T value) { | |
| classValue.bumpVersion(); | |
| removeStaleEntries(); | |
| var entry = makeEntry(classValue.version, value); | |
| put(classValue.identity, entry); | |
| // Add to the cache, to enable the fast path, next time. | |
| checkCacheLoad(); | |
| addToCache(classValue, entry); | |
| } | |
| //| -------- | |
| //| Cache management. | |
| //| -------- | |
| // Statics do not need synchronization. | |
| /** Load the cache entry at the given (hashed) location. */ | |
| static Entry<?> loadFromCache(Entry<?>[] cache, int i) { | |
| // non-racing cache.length : constant | |
| // racing cache[i & (mask)] : null <=> Entry | |
| return cache[i & (cache.length-1)]; | |
| // invariant: returned value is null or well-constructed (ready to match) | |
| } | |
| /** Look in the cache, at the home location for the given ClassValue. */ | |
| static <T> Entry<T> probeHomeLocation(Entry<?>[] cache, ClassValue<T> classValue) { | |
| return classValue.castEntry(loadFromCache(cache, classValue.hashCodeForCache)); | |
| } | |
| /** Given that first probe was a collision, retry at nearby locations. */ | |
| static <T> Entry<T> probeBackupLocations(Entry<?>[] cache, ClassValue<T> classValue) { | |
| if (PROBE_LIMIT <= 0) return null; | |
| // Probe the cache carefully, in a range of slots. | |
| int mask = (cache.length-1); | |
| int home = (classValue.hashCodeForCache & mask); | |
| Entry<?> e2 = cache[home]; // victim, if we find the real guy | |
| if (e2 == null) { | |
| return null; // if nobody is at home, no need to search nearby | |
| } | |
| // assume !classValue.match(e2), but do not assert, because of races | |
| int pos2 = -1; | |
| for (int i = home + 1; i < home + PROBE_LIMIT; i++) { | |
| Entry<?> e = cache[i & mask]; | |
| if (e == null) { | |
| break; // only search within non-null runs | |
| } | |
| if (classValue.match(e)) { | |
| // relocate colliding entry e2 (from cache[home]) to first empty slot | |
| cache[home] = e; | |
| if (pos2 >= 0) { | |
| cache[i & mask] = Entry.DEAD_ENTRY; | |
| } else { | |
| pos2 = i; | |
| } | |
| cache[pos2 & mask] = ((entryDislocation(cache, pos2, e2) < PROBE_LIMIT) | |
| ? e2 // put e2 here if it fits | |
| : Entry.DEAD_ENTRY); | |
| return classValue.castEntry(e); | |
| } | |
| // Remember first empty slot, if any: | |
| if (!e.isLive() && pos2 < 0) pos2 = i; | |
| } | |
| return null; | |
| } | |
| /** How far out of place is e? */ | |
| private static int entryDislocation(Entry<?>[] cache, int pos, Entry<?> e) { | |
| ClassValue<?> cv = e.classValueOrNull(); | |
| if (cv == null) return 0; // entry is not live! | |
| int mask = (cache.length-1); | |
| return (pos - cv.hashCodeForCache) & mask; | |
| } | |
| /// -------- | |
| /// Below this line all functions are private, and assume synchronized access. | |
| /// -------- | |
| private void sizeCache(int length) { | |
| assert((length & (length-1)) == 0); // must be power of 2 | |
| cacheLoad = 0; | |
| cacheLoadLimit = (int) ((double) length * CACHE_LOAD_LIMIT / 100); | |
| cacheArray = new Entry<?>[length]; | |
| } | |
| /** Make sure the cache load stays below its limit, if possible. */ | |
| private void checkCacheLoad() { | |
| if (cacheLoad >= cacheLoadLimit) { | |
| reduceCacheLoad(); | |
| } | |
| } | |
| private void reduceCacheLoad() { | |
| removeStaleEntries(); | |
| if (cacheLoad < cacheLoadLimit) | |
| return; // win | |
| Entry<?>[] oldCache = getCache(); | |
| if (oldCache.length > HASH_MASK) | |
| return; // lose | |
| sizeCache(oldCache.length * 2); | |
| for (Entry<?> e : oldCache) { | |
| if (e != null && e.isLive()) { | |
| addToCache(e); | |
| } | |
| } | |
| } | |
| /** Remove stale entries in the given range. | |
| * Should be executed under a Map lock. | |
| */ | |
| private void removeStaleEntries(Entry<?>[] cache, int begin, int count) { | |
| if (PROBE_LIMIT <= 0) return; | |
| int mask = (cache.length-1); | |
| int removed = 0; | |
| for (int i = begin; i < begin + count; i++) { | |
| Entry<?> e = cache[i & mask]; | |
| if (e == null || e.isLive()) | |
| continue; // skip null and live entries | |
| Entry<?> replacement = null; | |
| if (PROBE_LIMIT > 1) { | |
| // avoid breaking up a non-null run | |
| replacement = findReplacement(cache, i); | |
| } | |
| cache[i & mask] = replacement; | |
| if (replacement == null) removed += 1; | |
| } | |
| cacheLoad = Math.max(0, cacheLoad - removed); | |
| } | |
| /** Clearing a cache slot risks disconnecting following entries | |
| * from the head of a non-null run, which would allow them | |
| * to be found via reprobes. Find an entry after cache[begin] | |
| * to plug into the hole, or return null if none is needed. | |
| */ | |
| private Entry<?> findReplacement(Entry<?>[] cache, int home1) { | |
| Entry<?> replacement = null; | |
| int haveReplacement = -1, replacementPos = 0; | |
| int mask = (cache.length-1); | |
| for (int i2 = home1 + 1; i2 < home1 + PROBE_LIMIT; i2++) { | |
| Entry<?> e2 = cache[i2 & mask]; | |
| if (e2 == null) break; // End of non-null run. | |
| if (!e2.isLive()) continue; // Doomed anyway. | |
| int dis2 = entryDislocation(cache, i2, e2); | |
| if (dis2 == 0) continue; // e2 already optimally placed | |
| int home2 = i2 - dis2; | |
| if (home2 <= home1) { | |
| // e2 can replace entry at cache[home1] | |
| if (home2 == home1) { | |
| // Put e2 exactly where he belongs. | |
| haveReplacement = 1; | |
| replacementPos = i2; | |
| replacement = e2; | |
| } else if (haveReplacement <= 0) { | |
| haveReplacement = 0; | |
| replacementPos = i2; | |
| replacement = e2; | |
| } | |
| // And keep going, so we can favor larger dislocations. | |
| } | |
| } | |
| if (haveReplacement >= 0) { | |
| if (cache[(replacementPos+1) & mask] != null) { | |
| // Be conservative, to avoid breaking up a non-null run. | |
| cache[replacementPos & mask] = Entry.DEAD_ENTRY; | |
| } else { | |
| cache[replacementPos & mask] = null; | |
| cacheLoad -= 1; | |
| } | |
| } | |
| return replacement; | |
| } | |
| /** Remove stale entries in the range near classValue. */ | |
| private void removeStaleEntries(ClassValue<?> classValue) { | |
| removeStaleEntries(getCache(), classValue.hashCodeForCache, PROBE_LIMIT); | |
| } | |
| /** Remove all stale entries, everywhere. */ | |
| private void removeStaleEntries() { | |
| Entry<?>[] cache = getCache(); | |
| removeStaleEntries(cache, 0, cache.length + PROBE_LIMIT - 1); | |
| } | |
| /** Add the given entry to the cache, in its home location, unless it is out of date. */ | |
| private <T> void addToCache(Entry<T> e) { | |
| ClassValue<T> classValue = e.classValueOrNull(); | |
| if (classValue != null) | |
| addToCache(classValue, e); | |
| } | |
| /** Add the given entry to the cache, in its home location. */ | |
| private <T> void addToCache(ClassValue<T> classValue, Entry<T> e) { | |
| if (PROBE_LIMIT <= 0) return; // do not fill cache | |
| // Add e to the cache. | |
| Entry<?>[] cache = getCache(); | |
| int mask = (cache.length-1); | |
| int home = classValue.hashCodeForCache & mask; | |
| Entry<?> e2 = placeInCache(cache, home, e, false); | |
| if (e2 == null) return; // done | |
| if (PROBE_LIMIT > 1) { | |
| // try to move e2 somewhere else in his probe range | |
| int dis2 = entryDislocation(cache, home, e2); | |
| int home2 = home - dis2; | |
| for (int i2 = home2; i2 < home2 + PROBE_LIMIT; i2++) { | |
| if (placeInCache(cache, i2 & mask, e2, true) == null) { | |
| return; | |
| } | |
| } | |
| } | |
| // Note: At this point, e2 is just dropped from the cache. | |
| } | |
| /** Store the given entry. Update cacheLoad, and return any live victim. | |
| * 'Gently' means return self rather than dislocating a live victim. | |
| */ | |
| private Entry<?> placeInCache(Entry<?>[] cache, int pos, Entry<?> e, boolean gently) { | |
| Entry<?> e2 = overwrittenEntry(cache[pos]); | |
| if (gently && e2 != null) { | |
| // do not overwrite a live entry | |
| return e; | |
| } else { | |
| cache[pos] = e; | |
| return e2; | |
| } | |
| } | |
| /** Note an entry that is about to be overwritten. | |
| * If it is not live, quietly replace it by null. | |
| * If it is an actual null, increment cacheLoad, | |
| * because the caller is going to store something | |
| * in its place. | |
| */ | |
| private <T> Entry<T> overwrittenEntry(Entry<T> e2) { | |
| if (e2 == null) cacheLoad += 1; | |
| else if (e2.isLive()) return e2; | |
| return null; | |
| } | |
| /** Percent loading of cache before resize. */ | |
| private static final int CACHE_LOAD_LIMIT = 67; // 0..100 | |
| /** Maximum number of probes to attempt. */ | |
| private static final int PROBE_LIMIT = 6; // 1.. | |
| // N.B. Set PROBE_LIMIT=0 to disable all fast paths. | |
| } | |
| } |