@Override public String toString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < wheel.length; i++) { Map<Integer, List<K>> buckets = new TreeMap<>(); for (int j = 0; j < wheel[i].length; j++) { List<K> events = new ArrayList<>(); for (Node<K, V> node = wheel[i][j].getNextInVariableOrder(); node != wheel[i][j]; node = node.getNextInVariableOrder()) { events.add(node.getKey()); } if (!events.isEmpty()) { buckets.put(j, events); } } builder.append("Wheel #").append(i + 1).append(": ").append(buckets).append('\n'); } return builder.deleteCharAt(builder.length() - 1).toString(); }
private void checkNode(BoundedLocalCache<K, V> cache, Node<K, V> node, DescriptionBuilder desc) { Weigher<? super K, ? super V> weigher = cache.weigher; V value = node.getValue(); K key = node.getKey();
@Override public boolean containsValue(Object value) { requireNonNull(value); long now = expirationTicker().read(); for (Node<K, V> node : data.values()) { if (node.containsValue(value) && !hasExpired(node, now) && (node.getKey() != null)) { return true; } } return false; }
while ((map.size() < limit) && iterator.hasNext()) { Node<K, V> node = iterator.next(); K key = node.getKey(); V value = transformer.apply(node.getValue()); if ((key != null) && (value != null) && node.isAlive()) {
@Override public boolean hasNext() { if (next != null) { return true; } for (;;) { if (iterator.hasNext()) { next = iterator.next(); value = next.getValue(); key = next.getKey(); if (cache.hasExpired(next, now) || (key == null) || (value == null) || !next.isAlive()) { value = null; next = null; key = null; continue; } return true; } return false; } }
@Override public void forEachRemaining(Consumer<? super K> action) { requireNonNull(action); Consumer<Node<K, V>> consumer = node -> { K key = node.getKey(); V value = node.getValue(); long now = cache.expirationTicker().read(); if ((key != null) && (value != null) && node.isAlive() && !cache.hasExpired(node, now)) { action.accept(key); } }; spliterator.forEachRemaining(consumer); }
@Override public void forEachRemaining(Consumer<? super V> action) { requireNonNull(action); Consumer<Node<K, V>> consumer = node -> { K key = node.getKey(); V value = node.getValue(); long now = cache.expirationTicker().read(); if ((key != null) && (value != null) && node.isAlive() && !cache.hasExpired(node, now)) { action.accept(value); } }; spliterator.forEachRemaining(consumer); }
@Override public void forEachRemaining(Consumer<? super Entry<K, V>> action) { requireNonNull(action); Consumer<Node<K, V>> consumer = node -> { K key = node.getKey(); V value = node.getValue(); long now = cache.expirationTicker().read(); if ((key != null) && (value != null) && node.isAlive() && !cache.hasExpired(node, now)) { action.accept(new WriteThroughEntry<>(cache, key, value)); } }; spliterator.forEachRemaining(consumer); }
K key = node.getKey(); V value = transformer.apply(node.getValue()); if ((key != null) && (value != null) && node.isAlive()) {
@Override public final String toString() { return String.format("%s=[key=%s, value=%s, weight=%d, queueType=%,d, accessTimeNS=%,d, " + "writeTimeNS=%,d, varTimeNs=%,d, prevInAccess=%s, nextInAccess=%s, prevInWrite=%s, " + "nextInWrite=%s]", getClass().getSimpleName(), getKey(), getValue(), getWeight(), getQueueType(), getAccessTime(), getWriteTime(), getVariableTime(), getPreviousInAccessOrder() != null, getNextInAccessOrder() != null, getPreviousInWriteOrder() != null, getNextInWriteOrder() != null); } }
@Override public boolean tryAdvance(Consumer<? super V> action) { requireNonNull(action); boolean[] advanced = { false }; long now = cache.expirationTicker().read(); Consumer<Node<K, V>> consumer = node -> { K key = node.getKey(); V value = node.getValue(); if ((key != null) && (value != null) && !cache.hasExpired(node, now) && node.isAlive()) { action.accept(value); advanced[0] = true; } }; for (;;) { if (spliterator.tryAdvance(consumer)) { if (advanced[0]) { return true; } continue; } return false; } }
@Override public boolean tryAdvance(Consumer<? super K> action) { requireNonNull(action); boolean[] advanced = { false }; Consumer<Node<K, V>> consumer = node -> { K key = node.getKey(); V value = node.getValue(); long now = cache.expirationTicker().read(); if ((key != null) && (value != null) && node.isAlive() && !cache.hasExpired(node, now)) { action.accept(key); advanced[0] = true; } }; for (;;) { if (spliterator.tryAdvance(consumer)) { if (advanced[0]) { return true; } continue; } return false; } }
@Test(dataProvider = "caches") @CacheSpec(compute = Compute.SYNC, implementation = Implementation.Caffeine, population = Population.FULL, maximumSize = Maximum.FULL) public void updateRecency_onReplace(Cache<Integer, Integer> cache, CacheContext context) { BoundedLocalCache<Integer, Integer> localCache = asBoundedLocalCache(cache); Node<Integer, Integer> first = firstBeforeAccess(localCache, context); updateRecency(localCache, context, () -> localCache.replace(first.getKey(), first.getKey())); }
@Test(dataProvider = "caches") @CacheSpec(compute = Compute.SYNC, implementation = Implementation.Caffeine, population = Population.FULL, maximumSize = Maximum.FULL) public void updateRecency_onPutIfAbsent(Cache<Integer, Integer> cache, CacheContext context) { BoundedLocalCache<Integer, Integer> localCache = asBoundedLocalCache(cache); Node<Integer, Integer> first = firstBeforeAccess(localCache, context); updateRecency(localCache, context, () -> localCache.putIfAbsent(first.getKey(), first.getKey())); }
@Test(dataProvider = "caches") @CacheSpec(compute = Compute.SYNC, implementation = Implementation.Caffeine, population = Population.FULL, maximumSize = Maximum.FULL) public void updateRecency_onPut(Cache<Integer, Integer> cache, CacheContext context) { BoundedLocalCache<Integer, Integer> localCache = asBoundedLocalCache(cache); Node<Integer, Integer> first = firstBeforeAccess(localCache, context); updateRecency(localCache, context, () -> localCache.put(first.getKey(), first.getKey())); }
@Override public boolean tryAdvance(Consumer<? super Entry<K, V>> action) { requireNonNull(action); boolean[] advanced = { false }; Consumer<Node<K, V>> consumer = node -> { K key = node.getKey(); V value = node.getValue(); long now = cache.expirationTicker().read(); if ((key != null) && (value != null) && node.isAlive() && !cache.hasExpired(node, now)) { action.accept(new WriteThroughEntry<>(cache, key, value)); advanced[0] = true; } }; for (;;) { if (spliterator.tryAdvance(consumer)) { if (advanced[0]) { return true; } continue; } return false; } }
@Test(dataProvider = "caches") @CacheSpec(compute = Compute.SYNC, implementation = Implementation.Caffeine, population = Population.FULL, maximumSize = Maximum.FULL) public void updateRecency_onGet(Cache<Integer, Integer> cache, CacheContext context) { BoundedLocalCache<Integer, Integer> localCache = asBoundedLocalCache(cache); Node<Integer, Integer> first = firstBeforeAccess(localCache, context); updateRecency(localCache, context, () -> localCache.get(first.getKey())); }
@Test(dataProvider = "caches") @CacheSpec(compute = Compute.SYNC, implementation = Implementation.Caffeine, population = Population.FULL, maximumSize = Maximum.FULL) public void updateRecency_onReplaceConditionally( Cache<Integer, Integer> cache, CacheContext context) { BoundedLocalCache<Integer, Integer> localCache = asBoundedLocalCache(cache); Node<Integer, Integer> first = firstBeforeAccess(localCache, context); Integer value = first.getValue(); updateRecency(localCache, context, () -> localCache.replace(first.getKey(), value, value)); }
/** * 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); }
/** Updates the node's location in the page replacement policy. */ @GuardedBy("evictionLock") void onAccess(Node<K, V> node) { if (evicts()) { K key = node.getKey(); if (key == null) { return; } frequencySketch().increment(key); if (node.inEden()) { reorder(accessOrderEdenDeque(), node); } else if (node.inMainProbation()) { reorderProbation(node); } else { reorder(accessOrderProtectedDeque(), node); } } else if (expiresAfterAccess()) { reorder(accessOrderEdenDeque(), node); } if (expiresVariable()) { timerWheel().reschedule(node); } }