if (!evicts()) { Supplier<Iterator<Node<K, V>>> iteratorSupplier = () -> oldest ? accessOrderEdenDeque().iterator() : accessOrderEdenDeque().descendingIterator(); return fixedSnapshot(iteratorSupplier, limit, transformer); PeekingIterator<Node<K, V>> first, second, third; if (oldest) { first = accessOrderEdenDeque().iterator(); second = accessOrderProbationDeque().iterator(); third = accessOrderProtectedDeque().iterator(); } else { comparator = comparator.reversed(); first = accessOrderEdenDeque().descendingIterator(); second = accessOrderProbationDeque().descendingIterator(); third = accessOrderProtectedDeque().descendingIterator();
private static void updateRecency(BoundedLocalCache<Integer, Integer> cache, CacheContext context, Runnable operation) { Node<Integer, Integer> first = firstBeforeAccess(cache, context); operation.run(); cache.maintenance(/* ignored */ null); if (context.isZeroWeighted()) { assertThat(cache.accessOrderEdenDeque().peekFirst(), is(not(first))); assertThat(cache.accessOrderEdenDeque().peekLast(), is(first)); } else { assertThat(cache.accessOrderProbationDeque().peekFirst(), is(not(first))); assertThat(cache.accessOrderProtectedDeque().peekLast(), is(first)); } }
/** Promote the node from probation to protected on an access. */ @GuardedBy("evictionLock") void reorderProbation(Node<K, V> node) { if (!accessOrderProbationDeque().contains(node)) { // Ignore stale accesses for an entry that is no longer present return; } else if (node.getPolicyWeight() > mainProtectedMaximum()) { return; } long mainProtectedWeightedSize = mainProtectedWeightedSize() + node.getPolicyWeight(); accessOrderProbationDeque().remove(node); accessOrderProtectedDeque().add(node); node.makeMainProtected(); long mainProtectedMaximum = mainProtectedMaximum(); while (mainProtectedWeightedSize > mainProtectedMaximum) { Node<K, V> demoted = accessOrderProtectedDeque().pollFirst(); if (demoted == null) { break; } demoted.makeMainProbation(); accessOrderProbationDeque().add(demoted); mainProtectedWeightedSize -= demoted.getPolicyWeight(); } lazySetMainProtectedWeightedSize(mainProtectedWeightedSize); }
/** * Evicts entries from the eden space into the main space while the eden size exceeds a maximum. * * @return the number of candidate entries evicted from the eden space */ @GuardedBy("evictionLock") int evictFromEden() { int candidates = 0; Node<K, V> node = accessOrderEdenDeque().peek(); while (edenWeightedSize() > edenMaximum()) { // The pending operations will adjust the size to reflect the correct weight if (node == null) { break; } Node<K, V> next = node.getNextInAccessOrder(); if (node.getWeight() != 0) { node.makeMainProbation(); accessOrderEdenDeque().remove(node); accessOrderProbationDeque().add(node); candidates++; lazySetEdenWeightedSize(edenWeightedSize() - node.getPolicyWeight()); } node = next; } return candidates; }
@Override @SuppressWarnings("unchecked") public boolean remove(Object o) { return (o instanceof AccessOrder<?>) && remove((E) o); }
/** Expires entries in an access-order queue. */ @GuardedBy("evictionLock") void expireAfterAccessEntries(AccessOrderDeque<Node<K, V>> accessOrderDeque, long now) { long duration = expiresAfterAccessNanos(); for (;;) { Node<K, V> node = accessOrderDeque.peekFirst(); if ((node == null) || ((now - node.getAccessTime()) < duration)) { return; } evictEntry(node, RemovalCause.EXPIRED, now); } }
@Test(dataProvider = "full") public void comparing_uneven(LinkedDeque<LinkedValue> deque) { PeekingIterator<LinkedValue> empty = new AccessOrderDeque<LinkedValue>().iterator(); PeekingIterator<?> left = PeekingIterator.comparing(deque.iterator(), empty, (a, b) -> 1); PeekingIterator<?> right = PeekingIterator.comparing(deque.iterator(), empty, (a, b) -> 1); assertThat(left.peek(), is(deque.getFirst())); assertThat(right.peek(), is(deque.getFirst())); }
@Override public boolean contains(Object o) { return (o instanceof AccessOrder<?>) && contains((AccessOrder<?>) o); }
/** Promote the node from probation to protected on an access. */ @GuardedBy("evictionLock") void reorderProbation(Node<K, V> node) { if (!accessOrderProbationDeque().contains(node)) { // Ignore stale accesses for an entry that is no longer present return; } else if (node.getPolicyWeight() > mainProtectedMaximum()) { return; } long mainProtectedWeightedSize = mainProtectedWeightedSize() + node.getPolicyWeight(); accessOrderProbationDeque().remove(node); accessOrderProtectedDeque().add(node); node.makeMainProtected(); long mainProtectedMaximum = mainProtectedMaximum(); while (mainProtectedWeightedSize > mainProtectedMaximum) { Node<K, V> demoted = accessOrderProtectedDeque().pollFirst(); if (demoted == null) { break; } demoted.makeMainProbation(); accessOrderProbationDeque().add(demoted); mainProtectedWeightedSize -= demoted.getPolicyWeight(); } lazySetMainProtectedWeightedSize(mainProtectedWeightedSize); }
/** * Evicts entries from the eden space into the main space while the eden size exceeds a maximum. * * @return the number of candidate entries evicted from the eden space */ @GuardedBy("evictionLock") int evictFromEden() { int candidates = 0; Node<K, V> node = accessOrderEdenDeque().peek(); while (edenWeightedSize() > edenMaximum()) { // The pending operations will adjust the size to reflect the correct weight if (node == null) { break; } Node<K, V> next = node.getNextInAccessOrder(); if (node.getWeight() != 0) { node.makeMainProbation(); accessOrderEdenDeque().remove(node); accessOrderProbationDeque().add(node); candidates++; lazySetEdenWeightedSize(edenWeightedSize() - node.getPolicyWeight()); } node = next; } return candidates; }
@Override @SuppressWarnings("unchecked") public boolean remove(Object o) { return (o instanceof AccessOrder<?>) && remove((E) o); }
/** Expires entries in an access-order queue. */ @GuardedBy("evictionLock") void expireAfterAccessEntries(AccessOrderDeque<Node<K, V>> accessOrderDeque, long now) { long duration = expiresAfterAccessNanos(); for (;;) { Node<K, V> node = accessOrderDeque.peekFirst(); if ((node == null) || ((now - node.getAccessTime()) < duration)) { return; } evictEntry(node, RemovalCause.EXPIRED, now); } }
@Override public boolean contains(Object o) { return (o instanceof AccessOrder<?>) && contains((AccessOrder<?>) o); }
void evictFromMain(int candidates) { int victimQueue = PROBATION; Node<K, V> victim = accessOrderProbationDeque().peekFirst(); Node<K, V> candidate = accessOrderProbationDeque().peekLast(); while (weightedSize() > maximum()) { victim = accessOrderProtectedDeque().peekFirst(); victimQueue = PROTECTED; continue; } else if (victimQueue == PROTECTED) { victim = accessOrderEdenDeque().peekFirst(); victimQueue = EDEN; continue;
/** * Returns an unmodifiable snapshot map ordered in eviction order, either ascending or descending. * Beware that obtaining the mappings is <em>NOT</em> a constant-time operation. * * @param limit the maximum number of entries * @param transformer a function that unwraps the value * @param hottest the iteration order * @return an unmodifiable snapshot in a specified order */ @SuppressWarnings("GuardedByChecker") Map<K, V> evictionOrder(int limit, Function<V, V> transformer, boolean hottest) { Supplier<Iterator<Node<K, V>>> iteratorSupplier = () -> { Comparator<Node<K, V>> comparator = Comparator.comparingInt(node -> { K key = node.getKey(); return (key == null) ? 0 : frequencySketch().frequency(key); }); if (hottest) { PeekingIterator<Node<K, V>> secondary = PeekingIterator.comparing( accessOrderProbationDeque().descendingIterator(), accessOrderEdenDeque().descendingIterator(), comparator); return PeekingIterator.concat(accessOrderProtectedDeque().descendingIterator(), secondary); } else { PeekingIterator<Node<K, V>> primary = PeekingIterator.comparing( accessOrderEdenDeque().iterator(), accessOrderProbationDeque().iterator(), comparator.reversed()); return PeekingIterator.concat(primary, accessOrderProtectedDeque().iterator()); } }; return fixedSnapshot(iteratorSupplier, limit, transformer); }
void evictFromMain(int candidates) { int victimQueue = PROBATION; Node<K, V> victim = accessOrderProbationDeque().peekFirst(); Node<K, V> candidate = accessOrderProbationDeque().peekLast(); while (weightedSize() > maximum()) { victim = accessOrderProtectedDeque().peekFirst(); victimQueue = PROTECTED; continue; } else if (victimQueue == PROTECTED) { victim = accessOrderEdenDeque().peekFirst(); victimQueue = EDEN; continue;