@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; } }
/** Removes the entry from its bucket, if scheduled. */ void unlink(Node<K, V> node) { Node<K, V> next = node.getNextInVariableOrder(); if (next != null) { Node<K, V> prev = node.getPreviousInVariableOrder(); next.setPreviousInVariableOrder(prev); prev.setNextInVariableOrder(next); } }
@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); } }
/** Adds the entry at the tail of the bucket's list. */ void link(Node<K, V> sentinel, Node<K, V> node) { node.setPreviousInVariableOrder(sentinel.getPreviousInVariableOrder()); node.setNextInVariableOrder(sentinel); sentinel.getPreviousInVariableOrder().setNextInVariableOrder(node); sentinel.setPreviousInVariableOrder(node); }
static <K, V> Node<K, V> traverse(boolean ascending, Node<K, V> node) { return ascending ? node.getNextInVariableOrder() : node.getPreviousInVariableOrder(); }
Node<K, V> node = data.computeIfPresent(nodeFactory.newLookupKey(key), (k, n) -> { synchronized (n) { nodeKey[0] = n.getKey(); prevValue[0] = n.getValue(); oldWeight[0] = n.getWeight(); if ((nodeKey[0] == null) || (prevValue[0] == null) || !n.containsValue(oldValue) || hasExpired(n, now[0] = expirationTicker().read())) { return n; writer.write(key, newValue); n.setValue(newValue, valueReferenceQueue()); n.setWeight(weight);
@SuppressWarnings({"PMD.CollapsibleIfStatements", "GuardedByChecker"}) boolean evictEntry(Node<K, V> node, RemovalCause cause, long now) { K key = node.getKey(); @SuppressWarnings("unchecked") V[] value = (V[]) new Object[1]; data.computeIfPresent(node.getKeyReference(), (k, n) -> { if (n != node) { return n; value[0] = n.getValue(); boolean expired = false; if (expiresAfterAccess()) { expired |= ((now - n.getAccessTime()) >= expiresAfterAccessNanos()); expired |= ((now - n.getWriteTime()) >= expiresAfterWriteNanos()); expired |= (n.getVariableTime() <= now); int weight = node.getWeight(); if (weight == 0) { resurrect[0] = true; if (node.inEden() && (evicts() || expiresAfterAccess())) { accessOrderEdenDeque().remove(node); } else if (evicts()) { if (node.inMainProbation()) { accessOrderProbationDeque().remove(node);
prior = data.computeIfAbsent(node.getKeyReference(), k -> { writer.write(key, value); return computed; prior = data.putIfAbsent(node.getKeyReference(), node); if (prior == null) { afterWrite(new AddTask(node, newWeight)); boolean withinTolerance = true; synchronized (prior) { if (!prior.isAlive()) { continue; oldValue = prior.getValue(); oldWeight = prior.getWeight(); if (oldValue == null) { varTime = expireAfterCreate(key, value, expiry, now); withinTolerance = ((now - prior.getWriteTime()) > EXPIRE_WRITE_TOLERANCE); prior.setWeight(newWeight); prior.setValue(value, valueReferenceQueue());
@GuardedBy("evictionLock") @SuppressWarnings("GuardedByChecker") void removeNode(Node<K, V> node, long now) { K key = node.getKey(); @SuppressWarnings("unchecked") V[] value = (V[]) new Object[1]; RemovalCause[] cause = new RemovalCause[1]; data.computeIfPresent(node.getKeyReference(), (k, n) -> { if (n != node) { return n; value[0] = n.getValue(); if (node.inEden() && (evicts() || expiresAfterAccess())) { accessOrderEdenDeque().remove(node); } else if (evicts()) { if (node.inMainProbation()) { accessOrderProbationDeque().remove(node); } else {
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(); desc.expectThat("weight", node.getWeight(), is(greaterThanOrEqualTo(0))); desc.expectThat("weight", node.getWeight(), is(weigher.weigh(key, value))); node.getKeyReference(), instanceOf(WeakKeyReference.class)); } else { desc.expectThat("not null key", key, is(not(nullValue()))); desc.expectThat("found wrong node", cache.data.get(node.getKeyReference()), is(node)); desc.expectThat("infinite timestamp", node.getWriteTime(), is(not(Long.MAX_VALUE)));
/** * Atomically transitions the node to the <tt>dead</tt> state and decrements the * <tt>weightedSize</tt>. * * @param node the entry in the page replacement policy */ @GuardedBy("evictionLock") void makeDead(Node<K, V> node) { synchronized (node) { if (node.isDead()) { return; } if (evicts()) { // The node's policy weight may be out of sync due to a pending update waiting to be // processed. At this point the node's weight is finalized, so the weight can be safely // taken from the node's perspective and the sizes will be adjusted correctly. if (node.inEden()) { lazySetEdenWeightedSize(edenWeightedSize() - node.getWeight()); } else if (node.inMainProtected()) { lazySetMainProtectedWeightedSize(mainProtectedWeightedSize() - node.getWeight()); } lazySetWeightedSize(weightedSize() - node.getWeight()); } node.die(); } }
long oldWriteTime = node.getWriteTime(); long refreshWriteTime = (now + ASYNC_EXPIRY); if (((now - oldWriteTime) > refreshAfterWriteNanos()) && ((key = node.getKey()) != null) && ((oldValue = node.getValue()) != null) && node.casWriteTime(oldWriteTime, refreshWriteTime)) { try { CompletableFuture<V> refreshFuture; } else { node.casWriteTime(refreshWriteTime, oldWriteTime); return; if (error != null) { logger.log(Level.WARNING, "Exception thrown during refresh", error); node.casWriteTime(refreshWriteTime, oldWriteTime); statsCounter().recordLoadFailure(loadTime); return; if (currentValue == null) { return value; } else if ((currentValue == oldValue) && (node.getWriteTime() == refreshWriteTime)) { return value; node.casWriteTime(refreshWriteTime, oldWriteTime); logger.log(Level.SEVERE, "Exception thrown when submitting refresh task", t);
oldKey[0] = node.getKey(); oldValue[0] = node.getValue(); if (oldKey[0] == null) { cause[0] = RemovalCause.COLLECTED; } else if (hasExpired(node, expirationTicker().read())) { cause[0] = RemovalCause.EXPIRED; } else if (node.containsValue(value)) { cause[0] = RemovalCause.EXPLICIT; } else { node.retire(); return null;
/** * 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; }
if ((victim != null) && (victim.getPolicyWeight() == 0)) { victim = victim.getNextInAccessOrder(); continue; } else if ((candidate != null) && (candidate.getPolicyWeight() == 0)) { candidate = candidate.getPreviousInAccessOrder(); candidates--; continue; Node<K, V> previous = candidate.getPreviousInAccessOrder(); Node<K, V> evict = candidate; candidate = previous; } else if (candidate == null) { Node<K, V> evict = victim; victim = victim.getNextInAccessOrder(); evictEntry(evict, RemovalCause.SIZE, 0L); continue; K victimKey = victim.getKey(); K candidateKey = candidate.getKey(); if (victimKey == null) { @NonNull Node<K, V> evict = victim; victim = victim.getNextInAccessOrder(); evictEntry(evict, RemovalCause.COLLECTED, 0L); continue; candidates--; @NonNull Node<K, V> evict = candidate; candidate = candidate.getPreviousInAccessOrder();
/** Returns if the entry has expired. */ @SuppressWarnings("ShortCircuitBoolean") boolean hasExpired(Node<K, V> node, long now) { return (expiresAfterAccess() && (now - node.getAccessTime() >= expiresAfterAccessNanos())) | (expiresAfterWrite() && (now - node.getWriteTime() >= expiresAfterWriteNanos())) | (expiresVariable() && (now - node.getVariableTime() >= 0)); }
/** 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); } }
private void checkTimerWheel(BoundedLocalCache<K, V> cache) { if (!cache.expiresVariable()) { return; } Set<Node<K, V>> seen = Sets.newIdentityHashSet(); for (int i = 0; i < cache.timerWheel().wheel.length; i++) { for (int j = 0; j < cache.timerWheel().wheel[i].length; j++) { Node<K, V> sentinel = cache.timerWheel().wheel[i][j]; desc.expectThat("Wrong sentinel prev", sentinel.getPreviousInVariableOrder().getNextInVariableOrder(), sameInstance(sentinel)); desc.expectThat("Wrong sentinel next", sentinel.getNextInVariableOrder().getPreviousInVariableOrder(), sameInstance(sentinel)); desc.expectThat("Sentinel must be first element", sentinel, instanceOf(Sentinel.class)); for (Node<K, V> node = sentinel.getNextInVariableOrder(); node != sentinel; node = node.getNextInVariableOrder()) { Node<K, V> next = node.getNextInVariableOrder(); Node<K, V> prev = node.getPreviousInVariableOrder(); long duration = node.getVariableTime() - cache.timerWheel().nanos; desc.expectThat("Expired", duration, greaterThan(0L)); desc.expectThat("Loop detected", seen.add(node), is(true)); desc.expectThat("Wrong prev", prev.getNextInVariableOrder(), is(sameInstance(node))); desc.expectThat("Wrong next", next.getPreviousInVariableOrder(), is(sameInstance(node))); } } } desc.expectThat("Timers != Entries", seen, hasSize(cache.size())); }
@Override public boolean contains(Object obj) { if (!(obj instanceof Entry<?, ?>)) { return false; } Entry<?, ?> entry = (Entry<?, ?>) obj; Node<K, V> node = cache.data.get(cache.nodeFactory.newLookupKey(entry.getKey())); return (node != null) && Objects.equals(node.getValue(), entry.getValue()); }