@Override public boolean containsValue(Object value) { requireNonNull(value); for (CompletableFuture<V> valueFuture : delegate.values()) { if (value.equals(Async.getIfReady(valueFuture))) { return true; } } return false; }
@Override public boolean replace(K key, V oldValue, V newValue) { requireNonNull(oldValue); requireNonNull(newValue); CompletableFuture<V> oldValueFuture = delegate.get(key); if ((oldValueFuture != null) && !oldValue.equals(Async.getWhenSuccessful(oldValueFuture))) { // Optimistically check if the current value is equal, but don't skip if it may be loading return false; } @SuppressWarnings("unchecked") K castedKey = key; boolean[] replaced = { false }; delegate.compute(castedKey, (k, value) -> { replaced[0] = oldValue.equals(Async.getWhenSuccessful(value)); return replaced[0] ? CompletableFuture.completedFuture(newValue) : value; }, /* recordStats */ false, /* recordLoad */ false); return replaced[0]; }
@Override public long expireAfterCreate(K key, CompletableFuture<V> future, long currentTime) { if (isReady(future)) { long duration = delegate.expireAfterCreate(key, future.join(), currentTime); return Math.min(duration, MAXIMUM_EXPIRY); } return ASYNC_EXPIRY; }
@Override public @Nullable V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { requireNonNull(mappingFunction); CompletableFuture<V> valueFuture = delegate.computeIfAbsent(key, k -> { V newValue = mappingFunction.apply(key); return (newValue == null) ? null : CompletableFuture.completedFuture(newValue); }); return Async.getWhenSuccessful(valueFuture); }
@Override public long expireAfterRead(K key, CompletableFuture<V> future, long currentTime, long currentDuration) { if (isReady(future)) { long duration = delegate.expireAfterRead(key, future.join(), currentTime, currentDuration); return Math.min(duration, MAXIMUM_EXPIRY); } return ASYNC_EXPIRY; }
@Override public @Nullable V get(Object key) { return Async.getIfReady(delegate.get(key)); }
@Override public @Nullable V putIfAbsent(K key, V value) { requireNonNull(value); CompletableFuture<V> valueFuture = delegate.putIfAbsent(key, CompletableFuture.completedFuture(value)); return Async.getWhenSuccessful(valueFuture); }
/** Returns if the node's value is currently being computed, asynchronously. */ final boolean isComputingAsync(Node<?, ?> node) { return isAsync && !Async.isReady((CompletableFuture<?>) node.getValue()); }
@Test(dataProvider = "successful") public void getIfReady_success(CompletableFuture<?> future) { assertThat(Async.getIfReady(future), is(1)); }
@Override public @Nullable V replace(K key, V value) { requireNonNull(value); CompletableFuture<V> oldValueFuture = delegate.replace(key, CompletableFuture.completedFuture(value)); return Async.getWhenSuccessful(oldValueFuture); }
@Override public int weigh(K key, CompletableFuture<V> future) { return isReady(future) ? delegate.weigh(key, future.join()) : 0; }
@Test(dataProvider = "unsuccessful") public void getIfReady_fails(CompletableFuture<?> future) { assertThat(Async.getIfReady(future), is(nullValue())); }
@Override public @Nullable V put(K key, V value) { requireNonNull(value); CompletableFuture<V> oldValueFuture = delegate.put(key, CompletableFuture.completedFuture(value)); return Async.getWhenSuccessful(oldValueFuture); }
@Override public long expireAfterUpdate(K key, CompletableFuture<V> future, long currentTime, long currentDuration) { if (isReady(future)) { long duration = (currentDuration > MAXIMUM_EXPIRY) ? delegate.expireAfterCreate(key, future.join(), currentTime) : delegate.expireAfterUpdate(key, future.join(), currentTime, currentDuration); return Math.min(duration, MAXIMUM_EXPIRY); } return ASYNC_EXPIRY; }
@Override public @Nullable V getIfPresent(Object key) { CompletableFuture<V> future = asyncCache().cache().getIfPresent(key, /* recordStats */ true); return Async.getIfReady(future); }
@Override public @Nullable V remove(Object key) { CompletableFuture<V> oldValueFuture = delegate.remove(key); return Async.getWhenSuccessful(oldValueFuture); }
/** Returns the current value or null if either not done or failed. */ @SuppressWarnings("NullAway") static @Nullable <V> V getIfReady(@Nullable CompletableFuture<V> future) { return isReady(future) ? future.join() : null; }
@Override public Map<K, V> getAllPresent(Iterable<?> keys) { Set<Object> uniqueKeys = new LinkedHashSet<>(); for (Object key : keys) { uniqueKeys.add(key); } int misses = 0; Map<Object, Object> result = new LinkedHashMap<>(); for (Object key : uniqueKeys) { CompletableFuture<V> future = asyncCache().cache().get(key); Object value = Async.getIfReady(future); if (value == null) { misses++; } else { result.put(key, value); } } asyncCache().cache().statsCounter().recordMisses(misses); asyncCache().cache().statsCounter().recordHits(result.size()); @SuppressWarnings("unchecked") Map<K, V> castedResult = (Map<K, V>) result; return Collections.unmodifiableMap(castedResult); }
@Override public @Nullable V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { requireNonNull(remappingFunction); boolean[] computed = { false }; for (;;) { CompletableFuture<V> future = delegate.get(key); V oldValue = Async.getWhenSuccessful(future); if (oldValue == null) { return null; } CompletableFuture<V> valueFuture = delegate.computeIfPresent(key, (k, oldValueFuture) -> { if (future != oldValueFuture) { return oldValueFuture; } computed[0] = true; V newValue = remappingFunction.apply(key, oldValue); return (newValue == null) ? null : CompletableFuture.completedFuture(newValue); }); if (computed[0] || (valueFuture == null)) { return Async.getWhenSuccessful(valueFuture); } } }
@Test(dataProvider = "unsuccessful") public void isReady_fails(CompletableFuture<?> future) { assertThat(Async.isReady(future), is(false)); }