EntryIterator(BoundedLocalCache<K, V> cache) { this.iterator = cache.data.values().iterator(); this.now = cache.expirationTicker().read(); this.cache = cache; }
/** @return the current time in milliseconds */ protected final long currentTimeMillis() { return nanosToMillis(ticker.read()); }
@Override public Map<K, V> getAll(Set<? extends K> keys) { requireNotClosed(); boolean statsEnabled = statistics.isEnabled(); long now = statsEnabled ? ticker.read() : 0L; Map<K, Expirable<V>> result = getAndFilterExpiredEntries(keys, true); if (statsEnabled) { statistics.recordGetTime(ticker.read() - now); } return copyMap(result); }
@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; }
@Override public @Nullable V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { requireNonNull(key); requireNonNull(value); requireNonNull(remappingFunction); long[] now = { expirationTicker().read() }; Object keyRef = nodeFactory.newReferenceKey(key, keyReferenceQueue()); BiFunction<? super K, ? super V, ? extends V> mergeFunction = (k, oldValue) -> (oldValue == null) ? value : statsAware(remappingFunction).apply(oldValue, value); return remap(key, keyRef, mergeFunction, now, /* computeIfAbsent */ true); }
@Override public boolean containsKey(Object key) { Node<K, V> node = data.get(nodeFactory.newLookupKey(key)); return (node != null) && (node.getValue() != null) && !hasExpired(node, expirationTicker().read()); }
@Override public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { requireNonNull(function); BiFunction<K, V, V> remappingFunction = (key, oldValue) -> { V newValue = requireNonNull(function.apply(key, oldValue)); if (oldValue != newValue) { writer.write(key, newValue); } return newValue; }; for (K key : keySet()) { long[] now = { expirationTicker().read() }; Object lookupKey = nodeFactory.newLookupKey(key); remap(key, lookupKey, remappingFunction, now, /* computeIfAbsent */ false); } }
@Override public void put(K key, V value) { requireNotClosed(); boolean statsEnabled = statistics.isEnabled(); long start = statsEnabled ? ticker.read() : 0L; int[] puts = { 0 }; putNoCopyOrAwait(key, value, true, puts); dispatcher.awaitSynchronous(); if (statsEnabled) { statistics.recordPuts(puts[0]); statistics.recordPutTime(ticker.read() - start); } }
private long expireTimeMS() { try { Duration duration = expiry.getExpiryForCreation(); if (duration.isZero()) { return 0; } else if (duration.isEternal()) { return Long.MAX_VALUE; } long millis = TimeUnit.NANOSECONDS.toMillis(ticker.read()); return duration.getAdjustedTime(millis); } catch (Exception e) { return Long.MAX_VALUE; } } }
@Override public @Nullable V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction, boolean recordMiss, boolean recordLoad) { requireNonNull(key); requireNonNull(remappingFunction); long[] now = { expirationTicker().read() }; Object keyRef = nodeFactory.newReferenceKey(key, keyReferenceQueue()); BiFunction<? super K, ? super V, ? extends V> statsAwareRemappingFunction = statsAware(remappingFunction, recordMiss, recordLoad); return remap(key, keyRef, statsAwareRemappingFunction, now, /* computeIfAbsent */ true); }
@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 boolean remove(K key) { requireNotClosed(); requireNonNull(key); boolean statsEnabled = statistics.isEnabled(); long start = statsEnabled ? ticker.read() : 0L; publishToCacheWriter(writer::delete, () -> key); V value = removeNoCopyOrAwait(key); dispatcher.awaitSynchronous(); if (statsEnabled) { statistics.recordRemoveTime(ticker.read() - start); } if (value != null) { statistics.recordRemovals(1L); return true; } return false; }
@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); }
@Override public @Nullable V getIfPresentQuietly(Object key, long[/* 1 */] writeTime) { V value; Node<K, V> node = data.get(nodeFactory.newLookupKey(key)); if ((node == null) || ((value = node.getValue()) == null) || hasExpired(node, expirationTicker().read())) { return null; } writeTime[0] = node.getWriteTime(); return value; }
@Override public boolean putIfAbsent(K key, V value) { requireNotClosed(); requireNonNull(value); boolean statsEnabled = statistics.isEnabled(); long start = statsEnabled ? ticker.read() : 0L; boolean added = putIfAbsentNoAwait(key, value, true); dispatcher.awaitSynchronous(); if (statsEnabled) { if (added) { statistics.recordPuts(1L); statistics.recordMisses(1L); } else { statistics.recordHits(1L); } statistics.recordPutTime(ticker.read() - start); } return added; }
@Override public boolean replace(K key, V value) { requireNotClosed(); boolean statsEnabled = statistics.isEnabled(); long start = statsEnabled ? ticker.read() : 0L; V oldValue = replaceNoCopyOrAwait(key, value); dispatcher.awaitSynchronous(); if (oldValue == null) { statistics.recordMisses(1L); return false; } if (statsEnabled) { statistics.recordHits(1L); statistics.recordPuts(1L); statistics.recordPutTime(ticker.read() - start); } return true; }
/** Expires entries that have expired by access, write, or variable. */ @GuardedBy("evictionLock") void expireEntries() { long now = expirationTicker().read(); expireAfterAccessEntries(now); expireAfterWriteEntries(now); expireVariableEntries(now); }
@Override public @Nullable Expirable<V> load(K key) { try { boolean statsEnabled = statistics.isEnabled(); long start = statsEnabled ? ticker.read() : 0L; V value = delegate.load(key); if (value == null) { return null; } dispatcher.publishCreated(cache, key, value); if (statsEnabled) { // Subtracts the load time from the get time statistics.recordGetTime(start - ticker.read()); } return new Expirable<>(value, expireTimeMS()); } catch (CacheLoaderException e) { throw e; } catch (RuntimeException e) { throw new CacheLoaderException(e); } }
@Override public V getAndReplace(K key, V value) { requireNotClosed(); boolean statsEnabled = statistics.isEnabled(); long start = statsEnabled ? ticker.read() : 0L; V oldValue = replaceNoCopyOrAwait(key, value); dispatcher.awaitSynchronous(); V copy = copyOf(oldValue); if (statsEnabled) { if (oldValue == null) { statistics.recordMisses(1L); } else { statistics.recordHits(1L); statistics.recordPuts(1L); } long duration = ticker.read() - start; statistics.recordGetTime(duration); statistics.recordPutTime(duration); } return copy; }