public static void releaseReferences() { for (ReferenceEntry referenceEntry : _referenceEntries) { try { referenceEntry.setValue(null); } catch (Exception e) { _log.error("Failed to release reference for " + referenceEntry, e); } } _referenceEntries.clear(); }
V getOrCompute(K key, int hash, Function<? super K, ? extends V> computingFunction) throws ExecutionException { try { outer: while (true) { // don't call getLiveEntry, which would ignore computing values ReferenceEntry<K, V> e = getEntry(key, hash); if (e != null) { V value = getLiveValue(e); if (value != null) { recordRead(e); return value; } } // at this point e is either null, computing, or expired; // avoid locking if it's already computing if (e == null || !e.getValueReference().isComputingReference()) { boolean createNewEntry = true; ComputingValueReference<K, V> computingValueReference = null; lock(); try { preWriteCleanup(); int newCount = this.count - 1; AtomicReferenceArray<ReferenceEntry<K, V>> table = this.table; int index = hash & (table.length() - 1); ReferenceEntry<K, V> first = table.get(index); for (e = first; e != null; e = e.getNext()) { K entryKey = e.getKey(); if (e.getHash() == hash && entryKey != null && map.keyEquivalence.equivalent(key, entryKey)) { ValueReference<K, V> valueReference = e.getValueReference(); if (valueReference.isComputingReference()) { createNewEntry = false; } else { V value = e.getValueReference().get(); if (value == null) { enqueueNotification(entryKey, hash, value, RemovalCause.COLLECTED); } else if (map.expires() && map.isExpired(e)) { // This is a duplicate check, as preWriteCleanup already purged expired // entries, but let's accomodate an incorrect expiration queue. enqueueNotification(entryKey, hash, value, RemovalCause.EXPIRED); } else { recordLockedRead(e); return value; } // immediately reuse invalid entries evictionQueue.remove(e); expirationQueue.remove(e); this.count = newCount; // write-volatile } break; } } if (createNewEntry) { computingValueReference = new ComputingValueReference<K, V>(computingFunction); if (e == null) { e = newEntry(key, hash, first); e.setValueReference(computingValueReference); table.set(index, e); } else { e.setValueReference(computingValueReference); } } } finally { unlock(); postWriteCleanup(); } if (createNewEntry) { // This thread solely created the entry. return compute(key, hash, e, computingValueReference); } } // The entry already exists. Wait for the computation. checkState(!Thread.holdsLock(e), "Recursive computation"); // don't consider expiration as we're concurrent with computation V value = e.getValueReference().waitForValue(); if (value != null) { recordRead(e); return value; } // else computing thread will clearValue continue outer; } } finally { postReadCleanup(); } }