@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 @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 @Nullable V putIfAbsent(K key, V value) { requireNonNull(value); CompletableFuture<V> valueFuture = delegate.putIfAbsent(key, CompletableFuture.completedFuture(value)); return Async.getWhenSuccessful(valueFuture); }
@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 @Nullable V put(K key, V value) { requireNonNull(value); CompletableFuture<V> oldValueFuture = delegate.put(key, CompletableFuture.completedFuture(value)); return Async.getWhenSuccessful(oldValueFuture); }
@Override public @Nullable V remove(Object key) { CompletableFuture<V> oldValueFuture = delegate.remove(key); return Async.getWhenSuccessful(oldValueFuture); }
@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); } } }
for (;;) { CompletableFuture<V> future = delegate.get(key); V oldValue = Async.getWhenSuccessful(future); CompletableFuture<V> mergedValueFuture = delegate.merge( key, newValueFuture, (oldValueFuture, valueFuture) -> { }); if (merged[0] || (mergedValueFuture == newValueFuture)) { return Async.getWhenSuccessful(mergedValueFuture);
@Test(dataProvider = "successful") public void getWhenSuccessful_success(CompletableFuture<?> future) { assertThat(Async.getWhenSuccessful(future), is(1)); }
@Override public boolean remove(Object key, Object value) { requireNonNull(key); if (value == null) { return false; } @SuppressWarnings("unchecked") K castedKey = (K) key; boolean[] removed = { false }; boolean[] done = { false }; for (;;) { CompletableFuture<V> future = delegate.get(key); V oldValue = Async.getWhenSuccessful(future); if ((future != null) && !value.equals(oldValue)) { // Optimistically check if the current value is equal, but don't skip if it may be loading return false; } delegate.compute(castedKey, (k, oldValueFuture) -> { if (future != oldValueFuture) { return oldValueFuture; } done[0] = true; removed[0] = value.equals(oldValue); return removed[0] ? null : oldValueFuture; }, /* recordStats */ false, /* recordLoad */ false); if (done[0]) { return removed[0]; } } }
@Test(dataProvider = "unsuccessful") public void getWhenSuccessful_fails(CompletableFuture<?> future) { if ((future != null) && !future.isDone()) { AtomicInteger result = new AtomicInteger(); ConcurrentTestHarness.execute(() -> { result.set(1); Object value = Async.getWhenSuccessful(future); result.set((value == null) ? 2 : 3); }); Awaits.await().untilAtomic(result, is(1)); future.obtrudeException(new IllegalStateException()); Awaits.await().untilAtomic(result, is(not(1))); assertThat(result.get(), is(2)); } assertThat(Async.getWhenSuccessful(future), is(nullValue())); }
@Test public void getWhenSuccessful_success_async() { CompletableFuture<Integer> future = new CompletableFuture<Integer>(); AtomicInteger result = new AtomicInteger(); ConcurrentTestHarness.execute(() -> { result.set(1); result.set(Async.getWhenSuccessful(future)); }); Awaits.await().untilAtomic(result, is(1)); future.obtrudeValue(2); Awaits.await().untilAtomic(result, is(2)); }
@Override public @Nullable V compute(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); CompletableFuture<V> valueFuture = delegate.compute(key, (k, oldValueFuture) -> { if (future != oldValueFuture) { return oldValueFuture; } computed[0] = true; long startTime = delegate.statsTicker().read(); V newValue = remappingFunction.apply(key, oldValue); long loadTime = delegate.statsTicker().read() - startTime; if (newValue == null) { delegate.statsCounter().recordLoadFailure(loadTime); return null; } delegate.statsCounter().recordLoadSuccess(loadTime); return CompletableFuture.completedFuture(newValue); }, /* recordMiss */ false, /* recordLoad */ false); if (computed[0]) { return Async.getWhenSuccessful(valueFuture); } } }
@Override public @Nullable V remove(Object key) { CompletableFuture<V> oldValueFuture = delegate.remove(key); return Async.getWhenSuccessful(oldValueFuture); }
@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 @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 @Nullable V putIfAbsent(K key, V value) { requireNonNull(value); CompletableFuture<V> valueFuture = delegate.putIfAbsent(key, CompletableFuture.completedFuture(value)); return Async.getWhenSuccessful(valueFuture); }
@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 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 @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); } } }