<K, V> void copyAccessEntry(ReferenceEntry<K, V> original, ReferenceEntry<K, V> newEntry) { // TODO(fry): when we link values instead of entries this method can go // away, as can connectAccessOrder, nullifyAccessOrder. newEntry.setAccessTime(original.getAccessTime()); connectAccessOrder(original.getPreviousInAccessQueue(), newEntry); connectAccessOrder(newEntry, original.getNextInAccessQueue()); nullifyAccessOrder(original); }
@GuardedBy("this") void removeCollectedEntry(ReferenceEntry<K, V> entry) { enqueueNotification( entry.getKey(), entry.getHash(), entry.getValueReference().get(), entry.getValueReference().getWeight(), RemovalCause.COLLECTED); writeQueue.remove(entry); accessQueue.remove(entry); }
@VisibleForTesting @GuardedBy("this") boolean removeEntry(ReferenceEntry<K, V> entry, int hash, RemovalCause cause) { 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 (ReferenceEntry<K, V> e = first; e != null; e = e.getNext()) { if (e == entry) { ++modCount; ReferenceEntry<K, V> newFirst = removeValueFromChain( first, e, e.getKey(), hash, e.getValueReference().get(), e.getValueReference(), cause); newCount = this.count - 1; table.set(index, newFirst); this.count = newCount; // write-volatile return true; } } return false; }
@Override public void clear() { ReferenceEntry<K, V> e = head.getNextInAccessQueue(); while (e != head) { ReferenceEntry<K, V> next = e.getNextInAccessQueue(); nullifyAccessOrder(e); e = next; } head.setNextInAccessQueue(head); head.setPreviousInAccessQueue(head); }
@Override public void clear() { ReferenceEntry<K, V> e = head.getNextInWriteQueue(); while (e != head) { ReferenceEntry<K, V> next = e.getNextInWriteQueue(); nullifyWriteOrder(e); e = next; } head.setNextInWriteQueue(head); head.setPreviousInWriteQueue(head); }
/** * Copies an entry, assigning it a new {@code next} entry. * * @param original the entry to copy * @param newNext entry in the same bucket */ // Guarded By Segment.this <K, V> ReferenceEntry<K, V> copyEntry( Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) { return newEntry(segment, original.getKey(), original.getHash(), newNext); }
boolean removeLoadingValue(K key, int hash, LoadingValueReference<K, V> valueReference) { lock(); try { AtomicReferenceArray<ReferenceEntry<K, V>> table = this.table; int index = hash & (table.length() - 1); ReferenceEntry<K, V> first = table.get(index); for (ReferenceEntry<K, V> 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> v = e.getValueReference(); if (v == valueReference) { if (valueReference.isActive()) { e.setValueReference(valueReference.getOldValue()); } else { ReferenceEntry<K, V> newFirst = removeEntryFromChain(first, e); table.set(index, newFirst); } return true; } return false; } } return false; } finally { unlock(); postWriteCleanup(); } }
assertTrue(entries.add(current)); if (prev != null) { assertSame(prev, current.getPreviousInWriteQueue()); assertSame(prev.getNextInWriteQueue(), current); assertThat(prev.getWriteTime()).isAtMost(current.getWriteTime()); Object key = current.getKey(); if (key != null) { assertSame(current, segment.getEntry(key, current.getHash())); assertTrue(entries.add(current)); if (prev != null) { assertSame(prev, current.getPreviousInAccessQueue()); assertSame(prev.getNextInAccessQueue(), current); prev.getAccessTime() <= current.getAccessTime() || prev.getAccessTime() - current.getAccessTime() < 1000); Object key = current.getKey(); if (key != null) { assertSame(current, segment.getEntry(key, current.getHash()));
<K, V> void copyWriteEntry(ReferenceEntry<K, V> original, ReferenceEntry<K, V> newEntry) { // TODO(fry): when we link values instead of entries this method can go // away, as can connectWriteOrder, nullifyWriteOrder. newEntry.setWriteTime(original.getWriteTime()); connectWriteOrder(original.getPreviousInWriteQueue(), newEntry); connectWriteOrder(newEntry, original.getNextInWriteQueue()); nullifyWriteOrder(original); } }
@Override public boolean offer(ReferenceEntry<K, V> entry) { // unlink connectWriteOrder(entry.getPreviousInWriteQueue(), entry.getNextInWriteQueue()); // add to tail connectWriteOrder(head.getPreviousInWriteQueue(), entry); connectWriteOrder(entry, head); return true; }
@Override public boolean offer(ReferenceEntry<K, V> entry) { // unlink connectAccessOrder(entry.getPreviousInAccessQueue(), entry.getNextInAccessQueue()); // add to tail connectAccessOrder(head.getPreviousInAccessQueue(), entry); connectAccessOrder(entry, head); return true; }
/** Returns true if the entry has expired. */ boolean isExpired(ReferenceEntry<K, V> entry, long now) { checkNotNull(entry); if (expiresAfterAccess() && (now - entry.getAccessTime() >= expireAfterAccessNanos)) { return true; } if (expiresAfterWrite() && (now - entry.getWriteTime() >= expireAfterWriteNanos)) { return true; } return false; }
static void checkEviction(LocalCache<?, ?> map) { if (map.evictsBySize()) { for (Segment<?, ?> segment : map.segments) { drainRecencyQueue(segment); assertEquals(0, segment.recencyQueue.size()); assertEquals(0, segment.readCount.get()); ReferenceEntry<?, ?> prev = null; for (ReferenceEntry<?, ?> current : segment.accessQueue) { if (prev != null) { assertSame(prev, current.getPreviousInAccessQueue()); assertSame(prev.getNextInAccessQueue(), current); } Object key = current.getKey(); if (key != null) { assertSame(current, segment.getEntry(key, current.getHash())); } prev = current; } } } else { for (Segment<?, ?> segment : map.segments) { assertEquals(0, segment.recencyQueue.size()); } } }
/** * Gets the value from an entry. Returns null if the entry is invalid, partially-collected, * loading, or expired. Unlike {@link Segment#getLiveValue} this method does not attempt to * cleanup stale entries. As such it should only be called outside of a segment context, such as * during iteration. */ @Nullable V getLiveValue(ReferenceEntry<K, V> entry, long now) { if (entry.getKey() == null) { return null; } V value = entry.getValueReference().get(); if (value == null) { return null; } if (isExpired(entry, now)) { return null; } return value; }
@Nullable ReferenceEntry<K, V> getEntry(Object key, int hash) { for (ReferenceEntry<K, V> e = getFirst(hash); e != null; e = e.getNext()) { if (e.getHash() != hash) { continue; } K entryKey = e.getKey(); if (entryKey == null) { tryDrainReferenceQueues(); continue; } if (map.keyEquivalence.equivalent(key, entryKey)) { return e; } } return null; }
/** * Copies {@code original} into a new entry chained to {@code newNext}. Returns the new entry, * or {@code null} if {@code original} was already garbage collected. */ @GuardedBy("this") ReferenceEntry<K, V> copyEntry(ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) { if (original.getKey() == null) { // key collected return null; } ValueReference<K, V> valueReference = original.getValueReference(); V value = valueReference.get(); if ((value == null) && valueReference.isActive()) { // value collected return null; } ReferenceEntry<K, V> newEntry = map.entryFactory.copyEntry(this, original, newNext); newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, value, newEntry)); return newEntry; }
V scheduleRefresh( ReferenceEntry<K, V> entry, K key, int hash, V oldValue, long now, CacheLoader<? super K, V> loader) { if (map.refreshes() && (now - entry.getWriteTime() > map.refreshNanos) && !entry.getValueReference().isLoading()) { V newValue = refresh(key, hash, loader, true); if (newValue != null) { return newValue; } } return oldValue; }
/** * This method is a convenience for testing. Code should call {@link Segment#setValue} instead. */ // Guarded By Segment.this @VisibleForTesting ValueReference<K, V> newValueReference(ReferenceEntry<K, V> entry, V value, int weight) { int hash = entry.getHash(); return valueStrength.referenceValue(segmentFor(hash), entry, checkNotNull(value), weight); }
@Override protected ReferenceEntry<K, V> computeNext(ReferenceEntry<K, V> previous) { ReferenceEntry<K, V> next = previous.getNextInWriteQueue(); return (next == head) ? null : next; } };
@Override public ReferenceEntry<K, V> peek() { ReferenceEntry<K, V> next = head.getNextInAccessQueue(); return (next == head) ? null : next; }