• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ben-manes / caffeine / #5651

18 Jul 2026 03:26AM UTC coverage: 66.303% (-33.7%) from 100.0%
#5651

push

github

ben-manes
dependency updates

2809 of 4164 branches covered (67.46%)

5584 of 8422 relevant lines covered (66.3%)

0.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.68
/caffeine/src/main/java/com/github/benmanes/caffeine/cache/LocalAsyncCache.java
1
/*
2
 * Copyright 2018 Ben Manes. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package com.github.benmanes.caffeine.cache;
17

18
import static com.github.benmanes.caffeine.cache.Caffeine.calculateHashMapCapacity;
19
import static com.github.benmanes.caffeine.cache.Caffeine.requireState;
20
import static java.util.Locale.US;
21
import static java.util.Objects.requireNonNull;
22

23
import java.io.IOException;
24
import java.io.InvalidObjectException;
25
import java.io.ObjectInputStream;
26
import java.io.Serializable;
27
import java.lang.System.Logger;
28
import java.lang.System.Logger.Level;
29
import java.util.AbstractCollection;
30
import java.util.AbstractSet;
31
import java.util.Collection;
32
import java.util.Collections;
33
import java.util.Iterator;
34
import java.util.LinkedHashMap;
35
import java.util.Map;
36
import java.util.NoSuchElementException;
37
import java.util.Objects;
38
import java.util.Set;
39
import java.util.Spliterator;
40
import java.util.concurrent.CancellationException;
41
import java.util.concurrent.CompletableFuture;
42
import java.util.concurrent.CompletionException;
43
import java.util.concurrent.ConcurrentMap;
44
import java.util.concurrent.Executor;
45
import java.util.concurrent.TimeoutException;
46
import java.util.function.BiConsumer;
47
import java.util.function.BiFunction;
48
import java.util.function.Consumer;
49
import java.util.function.Function;
50
import java.util.function.Predicate;
51

52
import org.jspecify.annotations.Nullable;
53

54
import com.github.benmanes.caffeine.cache.LocalAsyncCache.AsyncBulkCompleter.NullMapCompletionException;
55
import com.github.benmanes.caffeine.cache.stats.CacheStats;
56
import com.google.errorprone.annotations.CanIgnoreReturnValue;
57
import com.google.errorprone.annotations.Var;
58

59
/**
60
 * This class provides a skeletal implementation of the {@link AsyncCache} interface to minimize the
61
 * effort required to implement a {@link LocalCache}.
62
 *
63
 * @author ben.manes@gmail.com (Ben Manes)
64
 */
65
interface LocalAsyncCache<K, V> extends AsyncCache<K, V> {
66
  Logger logger = System.getLogger(LocalAsyncCache.class.getName());
1✔
67

68
  /** Returns the backing {@link LocalCache} data store. */
69
  LocalCache<K, CompletableFuture<V>> cache();
70

71
  /** Returns the policy supported by this implementation and its configuration. */
72
  Policy<K, V> policy();
73

74
  @Override
75
  default @Nullable CompletableFuture<V> getIfPresent(K key) {
76
    return cache().getIfPresent(key, /* recordStats= */ true);
1✔
77
  }
78

79
  @Override
80
  default CompletableFuture<V> get(K key, Function<? super K, ? extends V> mappingFunction) {
81
    requireNonNull(mappingFunction);
1✔
82
    return get(key, (k, executor) -> CompletableFuture.supplyAsync(
1✔
83
        () -> mappingFunction.apply(k), executor));
1✔
84
  }
85

86
  @Override
87
  default CompletableFuture<V> get(K key, BiFunction<? super K, ? super Executor,
88
      ? extends CompletableFuture<? extends V>> mappingFunction) {
89
    return get(key, mappingFunction, /* recordStats= */ true);
1✔
90
  }
91

92
  default CompletableFuture<V> get(K key, BiFunction<? super K, ? super Executor,
93
      ? extends CompletableFuture<? extends V>> mappingFunction, boolean recordStats) {
94
    CompletableFuture<V> present = cache().getIfPresent(key, /* recordStats= */ false);
1✔
95
    if (present != null) {
1✔
96
      if (recordStats) {
1✔
97
        cache().statsCounter().recordHits(1);
1✔
98
      }
99
      return present;
1✔
100
    }
101

102
    long startTime = cache().statsTicker().read();
1✔
103
    @SuppressWarnings({"rawtypes", "unchecked"})
104
    @Nullable CompletableFuture<? extends V>[] result = new CompletableFuture[1];
1✔
105
    CompletableFuture<V> future = cache().computeIfAbsent(key, k -> {
1✔
106
      @SuppressWarnings("unchecked")
107
      var castedResult = (CompletableFuture<V>) mappingFunction.apply(key, cache().executor());
1✔
108
      result[0] = castedResult;
1✔
109
      return requireNonNull(castedResult);
1✔
110
    }, recordStats, /* recordLoad= */ false);
111
    if (result[0] != null) {
1✔
112
      handleCompletion(key, result[0], startTime);
1✔
113
    }
114
    return requireNonNull(future);
1✔
115
  }
116

117
  @Override
118
  default CompletableFuture<Map<K, V>> getAll(Iterable<? extends K> keys,
119
      Function<? super Set<? extends K>, ? extends Map<? extends K, ? extends V>> mappingFunction) {
120
    requireNonNull(mappingFunction);
1✔
121
    return getAll(keys, (keysToLoad, executor) ->
1✔
122
        CompletableFuture.supplyAsync(() -> mappingFunction.apply(keysToLoad), executor));
1✔
123
  }
124

125
  @Override
126
  @SuppressWarnings("FutureReturnValueIgnored")
127
  default CompletableFuture<Map<K, V>> getAll(Iterable<? extends K> keys,
128
      BiFunction<? super Set<? extends K>, ? super Executor,
129
          ? extends CompletableFuture<? extends Map<? extends K, ? extends V>>> mappingFunction) {
130
    requireNonNull(mappingFunction);
1✔
131
    requireNonNull(keys);
1✔
132

133
    int initialCapacity = calculateHashMapCapacity(keys);
1✔
134
    var futures = new LinkedHashMap<K, @Nullable CompletableFuture<@Nullable V>>(initialCapacity);
1✔
135
    for (K key : keys) {
1✔
136
      futures.put(requireNonNull(key), null);
1✔
137
    }
1✔
138

139
    var proxies = new LinkedHashMap<K, CompletableFuture<@Nullable V>>(initialCapacity);
1✔
140
    for (var entry : futures.entrySet()) {
1✔
141
      K key = entry.getKey();
1✔
142
      @Var CompletableFuture<V> future = cache().getIfPresent(key, /* recordStats= */ false);
1✔
143
      if (future == null) {
1✔
144
        var proxy = new CompletableFuture<V>();
1✔
145
        future = cache().putIfAbsent(key, proxy);
1✔
146
        if (future == null) {
1✔
147
          future = proxy;
1✔
148
          proxies.put(key, proxy);
1✔
149
        }
150
      }
151
      entry.setValue(future);
1✔
152
    }
1✔
153
    cache().statsCounter().recordMisses(proxies.size());
1✔
154
    cache().statsCounter().recordHits(futures.size() - proxies.size());
1✔
155

156
    @SuppressWarnings("NullAway")
157
    Map<K, CompletableFuture<@Nullable V>> results = futures;
1✔
158
    if (proxies.isEmpty()) {
1✔
159
      return composeResult(results);
1✔
160
    }
161

162
    var completer = new AsyncBulkCompleter<>(cache(), proxies);
1✔
163
    try {
164
      var loader = mappingFunction.apply(
1✔
165
          Collections.unmodifiableSet(proxies.keySet()), cache().executor());
1✔
166
      return loader.handle(completer).thenCompose(ignored -> composeResult(results));
1✔
167
    } catch (Throwable t) {
1✔
168
      throw completer.error(t);
×
169
    }
170
  }
171

172
  /**
173
   * Returns a future that waits for all of the dependent futures to complete and returns the
174
   * combined mapping if successful. If any future fails then it is automatically removed from
175
   * the cache if still present.
176
   */
177
  static <K, V> CompletableFuture<Map<K, V>> composeResult(
178
      Map<K, CompletableFuture<@Nullable V>> futures) {
179
    if (futures.isEmpty()) {
1✔
180
      @SuppressWarnings({"ImmutableMapOf", "RedundantUnmodifiable"})
181
      Map<K, V> emptyMap = Collections.unmodifiableMap(Collections.emptyMap());
1✔
182
      return CompletableFuture.completedFuture(emptyMap);
1✔
183
    }
184
    @SuppressWarnings("rawtypes")
185
    CompletableFuture<?>[] array = futures.values().toArray(new CompletableFuture[0]);
1✔
186
    return CompletableFuture.allOf(array).thenApply(ignored -> {
1✔
187
      var result = new LinkedHashMap<K, V>(calculateHashMapCapacity(futures.size()));
1✔
188
      futures.forEach((key, future) -> {
1✔
189
        @Nullable V value = future.getNow(null);
1✔
190
        if (value != null) {
1✔
191
          result.put(key, value);
1✔
192
        }
193
      });
1✔
194
      return Collections.unmodifiableMap(result);
1✔
195
    });
196
  }
197

198
  @Override
199
  @SuppressWarnings("FutureReturnValueIgnored")
200
  default void put(K key, CompletableFuture<? extends @Nullable V> valueFuture) {
201
    long startTime = cache().statsTicker().read();
1✔
202

203
    @SuppressWarnings("unchecked")
204
    var castedFuture = (CompletableFuture<V>) valueFuture;
1✔
205
    CompletableFuture<V> prior = cache().put(key, castedFuture);
1✔
206
    if (prior != castedFuture) {
1!
207
      handleCompletion(key, valueFuture, startTime);
1✔
208
    }
209
  }
1✔
210

211
  @SuppressWarnings({"FutureReturnValueIgnored", "ResultOfMethodCallIgnored"})
212
  default void handleCompletion(K key, CompletableFuture<? extends V> valueFuture,
213
      long startTime) {
214
    valueFuture.whenComplete((value, error) -> {
1✔
215
      long loadTime = cache().statsTicker().read() - startTime;
1✔
216
      if (value == null) {
1✔
217
        if ((error != null) && !(error instanceof CancellationException)
1✔
218
            && !(error instanceof TimeoutException)) {
219
          logger.log(Level.WARNING, "Exception thrown during asynchronous load", error);
1✔
220
        }
221
        cache().statsCounter().recordLoadFailure(loadTime);
1✔
222
        cache().remove(key, valueFuture);
1✔
223
      } else if (!Async.isReady(valueFuture)) {
1✔
224
        logger.log(Level.ERROR, String.format(US, "An invalid state was detected, occurring when "
1✔
225
            + "the future's dependent action has completed successfully with a value, but the "
226
            + "future remains either in-flight or in a failed completion state. This may occur "
227
            + "when using a custom future that does not abide by the CompletableFuture contract "
228
            + "(key: %s, key type: %s, value type: %s, future: %s, cache type: %s).",
229
            key, key.getClass().getName(), value.getClass().getName(), valueFuture,
1✔
230
            cache().getClass().getSimpleName()), new IllegalStateException());
1✔
231
        cache().statsCounter().recordLoadFailure(loadTime);
1✔
232
        cache().remove(key, valueFuture);
1✔
233
      } else {
234
        @SuppressWarnings("unchecked")
235
        var castedFuture = (CompletableFuture<V>) valueFuture;
1✔
236

237
        try {
238
          // update the weight and expiration timestamps
239
          cache().replace(key, castedFuture, castedFuture, /* shouldDiscardRefresh= */ false);
1✔
240
          cache().statsCounter().recordLoadSuccess(loadTime);
1✔
241
        } catch (Throwable t) {
1✔
242
          logger.log(Level.WARNING, "Exception thrown during asynchronous load", t);
1✔
243
          cache().statsCounter().recordLoadFailure(loadTime);
1✔
244
          cache().remove(key, valueFuture);
1✔
245
        }
1✔
246
      }
247
    });
1✔
248
  }
1✔
249

250
  /** A function executed asynchronously after a bulk load completes. */
251
  final class AsyncBulkCompleter<K, V> implements BiFunction<
252
      Map<? extends K, ? extends V>, Throwable, Map<? extends K, ? extends V>> {
253
    @SuppressWarnings("ImmutableMemberCollection")
254
    private final Map<K, CompletableFuture<@Nullable V>> proxies;
255
    private final LocalCache<K, CompletableFuture<V>> cache;
256
    private final long startTime;
257

258
    AsyncBulkCompleter(LocalCache<K, CompletableFuture<V>> cache,
259
        Map<K, CompletableFuture<@Nullable V>> proxies) {
1✔
260
      this.startTime = cache.statsTicker().read();
1✔
261
      this.proxies = proxies;
1✔
262
      this.cache = cache;
1✔
263
    }
1✔
264

265
    @Override
266
    @CanIgnoreReturnValue
267
    public @Nullable Map<? extends K, ? extends V> apply(
268
        @Nullable Map<? extends K, ? extends V> result, @Nullable Throwable error) {
269
      long loadTime = cache.statsTicker().read() - startTime;
1✔
270
      var failure = handleResponse(result, error);
1✔
271

272
      if (failure == null) {
1✔
273
        if (requireNonNull(result).isEmpty()) {
1✔
274
          cache.statsCounter().recordLoadFailure(loadTime);
1✔
275
        } else {
276
          cache.statsCounter().recordLoadSuccess(loadTime);
1✔
277
        }
278
        return result;
1✔
279
      }
280

281
      cache.statsCounter().recordLoadFailure(loadTime);
1✔
282
      if (failure instanceof RuntimeException) {
1!
283
        throw (RuntimeException) failure;
1✔
284
      } else if (failure instanceof Error) {
×
285
        throw (Error) failure;
×
286
      }
287
      throw new CompletionException(failure);
×
288
    }
289

290
    public CompletionException error(Throwable error) {
291
      long loadTime = cache.statsTicker().read() - startTime;
1✔
292
      var failure = handleResponse(/* result= */ null, error);
1✔
293
      cache.statsCounter().recordLoadFailure(loadTime);
1✔
294
      if (failure instanceof RuntimeException) {
1!
295
        throw (RuntimeException) failure;
1✔
296
      } else if (failure instanceof Error) {
×
297
        throw (Error) failure;
×
298
      }
299
      return new CompletionException(failure);
×
300
    }
301

302
    @SuppressWarnings("ImmutableMapCopyOf")
303
    private @Nullable Throwable handleResponse(
304
        @Nullable Map<? extends K, ? extends V> result, @Nullable Throwable error) {
305
      if (result == null) {
1✔
306
        var failure = (error == null) ? new NullMapCompletionException() : error;
1✔
307
        failProxies(failure);
1✔
308
        return failure;
1✔
309
      }
310

311
      Map<K, V> snapshot;
312
      try {
313
        snapshot = Map.copyOf(result);
1✔
314
      } catch (Throwable t) {
1✔
315
        failProxies(t);
1✔
316
        return t;
1✔
317
      }
1✔
318

319
      var failure = fillProxies(snapshot);
1✔
320
      return addNewEntries(snapshot, failure);
1✔
321
    }
322

323
    /** Removes and exceptionally completes every requested proxy. */
324
    private void failProxies(Throwable failure) {
325
      for (var entry : proxies.entrySet()) {
1✔
326
        cache.remove(entry.getKey(), entry.getValue());
1✔
327
        entry.getValue().obtrudeException(failure);
1✔
328
      }
1✔
329
      if (!(failure instanceof CancellationException) && !(failure instanceof TimeoutException)) {
1!
330
        logger.log(Level.WARNING, "Exception thrown during asynchronous load", failure);
1✔
331
      }
332
    }
1✔
333

334
    /** Populates the proxies with the computed result. */
335
    private @Nullable Throwable fillProxies(Map<? extends K, ? extends V> result) {
336
      @Var Throwable error = null;
1✔
337
      for (var entry : proxies.entrySet()) {
1✔
338
        var key = entry.getKey();
1✔
339
        var value = result.get(key);
1✔
340
        var future = entry.getValue();
1✔
341
        future.obtrudeValue(value);
1✔
342

343
        if (value == null) {
1✔
344
          cache.remove(key, future);
1✔
345
        } else {
346
          try {
347
            // update the weight and expiration timestamps
348
            cache.replace(key, future, future);
1✔
349
          } catch (Throwable t) {
1✔
350
            logger.log(Level.WARNING, "Exception thrown during asynchronous load", t);
1✔
351
            cache.remove(key, future);
1✔
352
            if (error == null) {
1✔
353
              error = t;
1✔
354
            } else if (error != t) {
1✔
355
              error.addSuppressed(t);
1✔
356
            }
357
          }
1✔
358
        }
359
      }
1✔
360
      return error;
1✔
361
    }
362

363
    /** Adds to the cache any extra entries computed that were not requested. */
364
    private @Nullable Throwable addNewEntries(
365
        Map<? extends K, ? extends V> result, @Nullable Throwable failure) {
366
      @Var Throwable error = failure;
1✔
367
      for (var entry : result.entrySet()) {
1✔
368
        var key = entry.getKey();
1✔
369
        if (!proxies.containsKey(key)) {
1✔
370
          V value = entry.getValue();
1✔
371
          try {
372
            cache.put(key, CompletableFuture.completedFuture(value));
1✔
373
          } catch (Throwable t) {
1✔
374
            logger.log(Level.WARNING, "Exception thrown during asynchronous load", t);
1✔
375
            if (error == null) {
1!
376
              error = t;
×
377
            } else if (error != t) {
1!
378
              error.addSuppressed(t);
×
379
            }
380
          }
1✔
381
        }
382
      }
1✔
383
      return error;
1✔
384
    }
385

386
    static final class NullMapCompletionException extends CompletionException {
1✔
387
      private static final long serialVersionUID = 1L;
388
    }
389
  }
390

391
  /* --------------- Asynchronous view --------------- */
392
  final class AsyncAsMapView<K, V> implements ConcurrentMap<K, CompletableFuture<V>> {
393
    final LocalAsyncCache<K, V> asyncCache;
394

395
    AsyncAsMapView(LocalAsyncCache<K, V> asyncCache) {
1✔
396
      this.asyncCache = requireNonNull(asyncCache);
1✔
397
    }
1✔
398
    @Override public boolean isEmpty() {
399
      return asyncCache.cache().isEmpty();
1✔
400
    }
401
    @Override public int size() {
402
      return asyncCache.cache().size();
1✔
403
    }
404
    @Override public void clear() {
405
      asyncCache.cache().clear();
1✔
406
    }
1✔
407
    @Override public boolean containsKey(Object key) {
408
      return asyncCache.cache().containsKey(key);
1✔
409
    }
410
    @SuppressWarnings("CollectionUndefinedEquality")
411
    @Override public boolean containsValue(Object value) {
412
      return asyncCache.cache().containsValue(value);
1✔
413
    }
414
    @Override public @Nullable CompletableFuture<V> get(Object key) {
415
      return asyncCache.cache().get(key);
1✔
416
    }
417
    @Override public @Nullable CompletableFuture<V> putIfAbsent(K key, CompletableFuture<V> value) {
418
      CompletableFuture<V> prior = asyncCache.cache().putIfAbsent(key, value);
1✔
419
      long startTime = asyncCache.cache().statsTicker().read();
1✔
420
      if (prior == null) {
1✔
421
        asyncCache.handleCompletion(key, value, startTime);
1✔
422
      }
423
      return prior;
1✔
424
    }
425
    @Override public @Nullable CompletableFuture<V> put(K key, CompletableFuture<V> value) {
426
      CompletableFuture<V> prior = asyncCache.cache().put(key, value);
1✔
427
      long startTime = asyncCache.cache().statsTicker().read();
1✔
428
      if (prior != value) {
1!
429
        asyncCache.handleCompletion(key, value, startTime);
1✔
430
      }
431
      return prior;
1✔
432
    }
433
    @SuppressWarnings({"FutureReturnValueIgnored", "ResultOfMethodCallIgnored"})
434
    @Override public void putAll(Map<? extends K, ? extends CompletableFuture<V>> map) {
435
      map.forEach(this::put);
1✔
436
    }
1✔
437
    @Override public @Nullable CompletableFuture<V> replace(K key, CompletableFuture<V> value) {
438
      CompletableFuture<V> prior = asyncCache.cache().replace(key, value);
1✔
439
      long startTime = asyncCache.cache().statsTicker().read();
1✔
440
      if ((prior != null) && (prior != value)) {
1!
441
        asyncCache.handleCompletion(key, value, startTime);
1✔
442
      }
443
      return prior;
1✔
444
    }
445
    @Override
446
    public boolean replace(K key, CompletableFuture<V> oldValue, CompletableFuture<V> newValue) {
447
      boolean replaced = asyncCache.cache().replace(key, oldValue, newValue);
1✔
448
      long startTime = asyncCache.cache().statsTicker().read();
1✔
449
      if (replaced && (newValue != oldValue)) {
1!
450
        asyncCache.handleCompletion(key, newValue, startTime);
1✔
451
      }
452
      return replaced;
1✔
453
    }
454
    @Override public CompletableFuture<V> remove(Object key) {
455
      return asyncCache.cache().remove(key);
1✔
456
    }
457
    @Override public boolean remove(Object key, Object value) {
458
      return asyncCache.cache().remove(key, value);
1✔
459
    }
460
    @Override public @Nullable CompletableFuture<V> computeIfAbsent(K key,
461
        Function<? super K, ? extends @Nullable CompletableFuture<V>> mappingFunction) {
462
      requireNonNull(mappingFunction);
1✔
463
      @SuppressWarnings({"rawtypes", "unchecked"})
464
      @Nullable CompletableFuture<V>[] result = new CompletableFuture[1];
1✔
465
      long startTime = asyncCache.cache().statsTicker().read();
1✔
466
      Function<K, @Nullable CompletableFuture<V>> function = k -> {
1✔
467
        result[0] = mappingFunction.apply(k);
1✔
468
        return result[0];
1✔
469
      };
470
      @Nullable CompletableFuture<V> future = asyncCache.cache().computeIfAbsent(
1✔
471
          key, function, /* recordStats= */ true, /* recordLoad= */ false);
472
      if (result[0] != null) {
1✔
473
        asyncCache.handleCompletion(key, result[0], startTime);
1✔
474
      }
475
      return future;
1✔
476
    }
477
    @SuppressWarnings("ResultOfMethodCallIgnored")
478
    @Override public @Nullable CompletableFuture<V> computeIfPresent(K key, BiFunction<? super K,
479
        ? super CompletableFuture<V>, ? extends CompletableFuture<V>> remappingFunction) {
480
      requireNonNull(remappingFunction);
1✔
481

482
      @SuppressWarnings({"rawtypes", "unchecked"})
483
      @Nullable CompletableFuture<V>[] result = new CompletableFuture[1];
1✔
484
      @SuppressWarnings({"rawtypes", "unchecked"})
485
      @Nullable CompletableFuture<V>[] prior = new CompletableFuture[1];
1✔
486
      long startTime = asyncCache.cache().statsTicker().read();
1✔
487
      asyncCache.cache().compute(key, (K k, @Nullable CompletableFuture<V> oldValue) -> {
1✔
488
        result[0] = (oldValue == null) ? null : remappingFunction.apply(k, oldValue);
1✔
489
        prior[0] = oldValue;
1✔
490
        return result[0];
1✔
491
      }, asyncCache.cache().expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false);
1✔
492

493
      if ((result[0] != null) && (result[0] != prior[0])) {
1✔
494
        asyncCache.handleCompletion(key, result[0], startTime);
1✔
495
      }
496
      return result[0];
1✔
497
    }
498
    @SuppressWarnings("ResultOfMethodCallIgnored")
499
    @Override public @Nullable CompletableFuture<V> compute(K key, BiFunction<? super K,
500
        ? super CompletableFuture<V>, ? extends CompletableFuture<V>> remappingFunction) {
501
      requireNonNull(remappingFunction);
1✔
502

503
      @SuppressWarnings({"rawtypes", "unchecked"})
504
      @Nullable CompletableFuture<V>[] result = new CompletableFuture[1];
1✔
505
      @SuppressWarnings({"rawtypes", "unchecked"})
506
      @Nullable CompletableFuture<V>[] prior = new CompletableFuture[1];
1✔
507
      long startTime = asyncCache.cache().statsTicker().read();
1✔
508
      asyncCache.cache().compute(key, (k, oldValue) -> {
1✔
509
        result[0] = remappingFunction.apply(k, oldValue);
1✔
510
        prior[0] = oldValue;
1✔
511
        return result[0];
1✔
512
      }, asyncCache.cache().expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false);
1✔
513

514
      if ((result[0] != null) && (result[0] != prior[0])) {
1✔
515
        asyncCache.handleCompletion(key, result[0], startTime);
1✔
516
      }
517
      return result[0];
1✔
518
    }
519
    @SuppressWarnings("ResultOfMethodCallIgnored")
520
    @Override public @Nullable CompletableFuture<V> merge(K key, CompletableFuture<V> value,
521
        BiFunction<? super CompletableFuture<V>, ? super CompletableFuture<V>,
522
            ? extends CompletableFuture<V>> remappingFunction) {
523
      requireNonNull(value);
1✔
524
      requireNonNull(remappingFunction);
1✔
525

526
      @SuppressWarnings({"rawtypes", "unchecked"})
527
      @Nullable  CompletableFuture<V>[] result = new CompletableFuture[1];
1✔
528
      @SuppressWarnings({"rawtypes", "unchecked"})
529
      @Nullable  CompletableFuture<V>[] prior = new CompletableFuture[1];
1✔
530
      long startTime = asyncCache.cache().statsTicker().read();
1✔
531
      asyncCache.cache().compute(key, (K k, @Nullable CompletableFuture<V> oldValue) -> {
1✔
532
        result[0] = (oldValue == null) ? value : remappingFunction.apply(oldValue, value);
1✔
533
        prior[0] = oldValue;
1✔
534
        return result[0];
1✔
535
      }, asyncCache.cache().expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false);
1✔
536

537
      if ((result[0] != null) && (result[0] != prior[0])) {
1!
538
        asyncCache.handleCompletion(key, result[0], startTime);
1✔
539
      }
540
      return result[0];
1✔
541
    }
542
    @Override public void forEach(BiConsumer<? super K, ? super CompletableFuture<V>> action) {
543
      asyncCache.cache().forEach(action);
1✔
544
    }
1✔
545
    @Override public Set<K> keySet() {
546
      return asyncCache.cache().keySet();
1✔
547
    }
548
    @Override public Collection<CompletableFuture<V>> values() {
549
      return asyncCache.cache().values();
1✔
550
    }
551
    @Override public Set<Entry<K, CompletableFuture<V>>> entrySet() {
552
      return new AsyncEntrySet();
1✔
553
    }
554
    @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
555
    @Override public boolean equals(@Nullable Object o) {
556
      return (o == this) || asyncCache.cache().equals(o);
1✔
557
    }
558
    @Override public int hashCode() {
559
      return asyncCache.cache().hashCode();
1✔
560
    }
561
    @Override public String toString() {
562
      return asyncCache.cache().toString();
1✔
563
    }
564

565
    private final class AsyncEntrySet extends AbstractSet<Entry<K, CompletableFuture<V>>> {
1✔
566
      @Override public int size() {
567
        return AsyncAsMapView.this.size();
1✔
568
      }
569
      @Override public boolean isEmpty() {
570
        return AsyncAsMapView.this.isEmpty();
1✔
571
      }
572
      @Override public void clear() {
573
        AsyncAsMapView.this.clear();
×
574
      }
×
575
      @Override public boolean contains(Object o) {
576
        return asyncCache.cache().entrySet().contains(o);
1✔
577
      }
578
      @Override public boolean remove(Object o) {
579
        return asyncCache.cache().entrySet().remove(o);
1✔
580
      }
581
      @Override public boolean removeAll(Collection<?> collection) {
582
        return asyncCache.cache().entrySet().removeAll(collection);
1✔
583
      }
584
      @Override public boolean retainAll(Collection<?> collection) {
585
        return asyncCache.cache().entrySet().retainAll(collection);
1✔
586
      }
587
      @Override public boolean removeIf(Predicate<? super Entry<K, CompletableFuture<V>>> filter) {
588
        return asyncCache.cache().entrySet().removeIf(filter);
×
589
      }
590
      @Override public Iterator<Entry<K, CompletableFuture<V>>> iterator() {
591
        return new AsyncEntryIterator();
1✔
592
      }
593
      @Override public Spliterator<Entry<K, CompletableFuture<V>>> spliterator() {
594
        return new AsyncEntrySpliterator();
1✔
595
      }
596
    }
597

598
    private final class AsyncEntryIterator implements Iterator<Entry<K, CompletableFuture<V>>> {
1✔
599
      final Iterator<Entry<K, CompletableFuture<V>>> iterator =
1✔
600
          asyncCache.cache().entrySet().iterator();
1✔
601

602
      @Override public boolean hasNext() {
603
        return iterator.hasNext();
1✔
604
      }
605
      @Override public Entry<K, CompletableFuture<V>> next() {
606
        var entry = iterator.next();
1✔
607
        return new WriteThroughEntry<>(AsyncAsMapView.this, entry.getKey(), entry.getValue());
1✔
608
      }
609
      @Override public void remove() {
610
        iterator.remove();
×
611
      }
×
612
    }
613

614
    private final class AsyncEntrySpliterator
615
        implements Spliterator<Entry<K, CompletableFuture<V>>> {
616
      final Spliterator<Entry<K, CompletableFuture<V>>> spliterator;
617

618
      AsyncEntrySpliterator() {
619
        this(asyncCache.cache().entrySet().spliterator());
1✔
620
      }
1✔
621
      AsyncEntrySpliterator(Spliterator<Entry<K, CompletableFuture<V>>> spliterator) {
1✔
622
        this.spliterator = requireNonNull(spliterator);
1✔
623
      }
1✔
624
      @Override public boolean tryAdvance(Consumer<? super Entry<K, CompletableFuture<V>>> action) {
625
        requireNonNull(action);
×
626
        return spliterator.tryAdvance(entry -> action.accept(
×
627
            new WriteThroughEntry<>(AsyncAsMapView.this, entry.getKey(), entry.getValue())));
×
628
      }
629
      @Override public void forEachRemaining(
630
          Consumer<? super Entry<K, CompletableFuture<V>>> action) {
631
        requireNonNull(action);
1✔
632
        spliterator.forEachRemaining(entry -> action.accept(
1✔
633
            new WriteThroughEntry<>(AsyncAsMapView.this, entry.getKey(), entry.getValue())));
1✔
634
      }
1✔
635
      @Override public @Nullable Spliterator<Entry<K, CompletableFuture<V>>> trySplit() {
636
        var split = spliterator.trySplit();
×
637
        return (split == null) ? null : new AsyncEntrySpliterator(split);
×
638
      }
639
      @Override public long estimateSize() {
640
        return spliterator.estimateSize();
×
641
      }
642
      @Override public int characteristics() {
643
        return spliterator.characteristics();
1✔
644
      }
645
    }
646
  }
647

648
  /* --------------- Synchronous view --------------- */
649
  final class CacheView<K, V> extends AbstractCacheView<K, V> {
650
    private static final long serialVersionUID = 1L;
651

652
    @SuppressWarnings("serial")
653
    final LocalAsyncCache<K, V> asyncCache;
654

655
    CacheView(LocalAsyncCache<K, V> asyncCache) {
1✔
656
      this.asyncCache = requireNonNull(asyncCache);
1✔
657
    }
1✔
658
    @Override LocalAsyncCache<K, V> asyncCache() {
659
      return asyncCache;
1✔
660
    }
661
  }
662

663
  abstract class AbstractCacheView<K, V> implements Cache<K, V>, Serializable {
1✔
664
    private static final long serialVersionUID = 1L;
665

666
    transient @Nullable ConcurrentMap<K, V> asMapView;
667

668
    abstract LocalAsyncCache<K, V> asyncCache();
669

670
    @Override
671
    public @Nullable V getIfPresent(K key) {
672
      var future = asyncCache().cache().getIfPresent(key, /* recordStats= */ false);
1✔
673
      var value = Async.getIfReady(future);
1✔
674
      if (value == null) {
1✔
675
        asyncCache().cache().statsCounter().recordMisses(1);
1✔
676
      } else {
677
        asyncCache().cache().statsCounter().recordHits(1);
1✔
678
      }
679
      return value;
1✔
680
    }
681

682
    @Override
683
    public Map<K, V> getAllPresent(Iterable<? extends K> keys) {
684
      var result = new LinkedHashMap<K, @Nullable V>(calculateHashMapCapacity(keys));
1✔
685
      for (K key : keys) {
1✔
686
        result.put(key, null);
1✔
687
      }
1✔
688

689
      int uniqueKeys = result.size();
1✔
690
      for (var iter = result.entrySet().iterator(); iter.hasNext();) {
1✔
691
        Map.Entry<K, @Nullable V> entry = iter.next();
1✔
692

693
        CompletableFuture<V> future = asyncCache().cache().get(entry.getKey());
1✔
694
        V value = Async.getIfReady(future);
1✔
695
        if (value == null) {
1✔
696
          iter.remove();
1✔
697
        } else {
698
          entry.setValue(value);
1✔
699
        }
700
      }
1✔
701
      asyncCache().cache().statsCounter().recordHits(result.size());
1✔
702
      asyncCache().cache().statsCounter().recordMisses(uniqueKeys - result.size());
1✔
703

704
      @SuppressWarnings("NullableProblems")
705
      Map<K, V> unmodifiable = Collections.unmodifiableMap(result);
1✔
706
      return unmodifiable;
1✔
707
    }
708

709
    @Override
710
    public V get(K key, Function<? super K, ? extends V> mappingFunction) {
711
      return resolve(asyncCache().get(key, mappingFunction));
1✔
712
    }
713

714
    @Override
715
    public Map<K, V> getAll(
716
        Iterable<? extends K> keys,
717
        Function<
718
            ? super Set<? extends K>,
719
            ? extends Map<? extends K, ? extends V>> mappingFunction) {
720
      return resolve(asyncCache().getAll(keys, mappingFunction));
1✔
721
    }
722

723
    @SuppressWarnings({"PMD.AvoidThrowingNullPointerException",
724
      "PMD.PreserveStackTrace", "UnusedException"})
725
    protected static <T> T resolve(CompletableFuture<T> future) {
726
      try {
727
        return future.join();
1✔
728
      } catch (NullMapCompletionException e) {
1✔
729
        throw new NullPointerException("null map");
1✔
730
      } catch (CompletionException e) {
1✔
731
        if (e.getCause() instanceof RuntimeException) {
1✔
732
          throw (RuntimeException) e.getCause();
1✔
733
        } else if (e.getCause() instanceof Error) {
1!
734
          throw (Error) e.getCause();
×
735
        }
736
        throw e;
1✔
737
      }
738
    }
739

740
    @Override
741
    public void put(K key, V value) {
742
      requireNonNull(value);
1✔
743
      asyncCache().cache().put(key, CompletableFuture.completedFuture(value));
1✔
744
    }
1✔
745

746
    @Override
747
    public void putAll(Map<? extends K, ? extends V> map) {
748
      map.forEach(this::put);
1✔
749
    }
1✔
750

751
    @Override
752
    public void invalidate(K key) {
753
      asyncCache().cache().remove(key);
1✔
754
    }
1✔
755

756
    @Override
757
    public void invalidateAll(Iterable<? extends K> keys) {
758
      asyncCache().cache().invalidateAll(keys);
1✔
759
    }
1✔
760

761
    @Override
762
    public void invalidateAll() {
763
      asyncCache().cache().clear();
1✔
764
    }
1✔
765

766
    @Override
767
    public long estimatedSize() {
768
      return asyncCache().cache().estimatedSize();
1✔
769
    }
770

771
    @Override
772
    public CacheStats stats() {
773
      return asyncCache().cache().statsCounter().snapshot();
1✔
774
    }
775

776
    @Override
777
    public void cleanUp() {
778
      asyncCache().cache().cleanUp();
1✔
779
    }
1✔
780

781
    @Override
782
    public Policy<K, V> policy() {
783
      return asyncCache().policy();
1✔
784
    }
785

786
    @Override
787
    public ConcurrentMap<K, V> asMap() {
788
      return (asMapView == null) ? (asMapView = new AsMapView<>(asyncCache().cache())) : asMapView;
1✔
789
    }
790

791
    private void readObject(ObjectInputStream stream) throws InvalidObjectException {
792
      throw new InvalidObjectException("Proxy required");
×
793
    }
794

795
    @SuppressWarnings("unused")
796
    private void readObjectNoData() throws InvalidObjectException {
797
      throw new InvalidObjectException("Proxy required");
1✔
798
    }
799

800
    Object writeReplace() {
801
      return new SyncViewProxy<>(asyncCache());
1✔
802
    }
803
  }
804

805
  final class AsMapView<K, V> implements ConcurrentMap<K, V> {
806
    final LocalCache<K, CompletableFuture<V>> delegate;
807

808
    @Nullable Set<K> keys;
809
    @Nullable Collection<V> values;
810
    @Nullable Set<Entry<K, V>> entries;
811

812
    AsMapView(LocalCache<K, CompletableFuture<V>> delegate) {
1✔
813
      this.delegate = delegate;
1✔
814
    }
1✔
815

816
    @Override
817
    public boolean isEmpty() {
818
      return delegate.isEmpty();
1✔
819
    }
820

821
    @Override
822
    public int size() {
823
      return delegate.size();
1✔
824
    }
825

826
    @Override
827
    public void clear() {
828
      delegate.clear();
1✔
829
    }
1✔
830

831
    @Override
832
    public boolean containsKey(Object key) {
833
      return Async.isReady(delegate.getIfPresentQuietly(key));
1✔
834
    }
835

836
    @Override
837
    public boolean containsValue(Object value) {
838
      requireNonNull(value);
1✔
839

840
      for (CompletableFuture<V> valueFuture : delegate.values()) {
1✔
841
        if (value.equals(Async.getIfReady(valueFuture))) {
1✔
842
          return true;
1✔
843
        }
844
      }
1✔
845
      return false;
1✔
846
    }
847

848
    @Override
849
    public @Nullable V get(Object key) {
850
      return Async.getIfReady(delegate.get(key));
1✔
851
    }
852

853
    @Override
854
    @SuppressWarnings("ResultOfMethodCallIgnored")
855
    public @Nullable V putIfAbsent(K key, V value) {
856
      requireNonNull(value);
1✔
857

858
      // Keep in sync with BoundedVarExpiration.putIfAbsentAsync(key, value, duration, unit)
859
      @Var CompletableFuture<V> priorFuture = null;
1✔
860
      for (;;) {
861
        priorFuture = (priorFuture == null)
1!
862
            ? delegate.get(key)
1✔
863
            : delegate.getIfPresentQuietly(key);
1✔
864
        if (priorFuture != null) {
1✔
865
          if (!priorFuture.isDone()) {
1!
866
            Async.getWhenSuccessful(priorFuture);
×
867
            continue;
×
868
          }
869

870
          V prior = Async.getWhenSuccessful(priorFuture);
1✔
871
          if (prior != null) {
1✔
872
            return prior;
1✔
873
          }
874
        }
875

876
        boolean[] added = { false };
1✔
877
        var hints = new LocalCache.RemapHints();
1✔
878
        CompletableFuture<V> computed = delegate.compute(
1✔
879
            key, (K k, @Nullable CompletableFuture<V> valueFuture) -> {
880
              added[0] = (valueFuture == null)
1✔
881
                  || (valueFuture.isDone() && (Async.getIfReady(valueFuture) == null));
1!
882
              if (added[0]) {
1✔
883
                return CompletableFuture.completedFuture(value);
1✔
884
              }
885
              hints.preserveTimestamps = true;
1✔
886
              hints.preserveRefresh = true;
1✔
887
              return valueFuture;
1✔
888
            }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false, hints);
1✔
889

890
        if (added[0]) {
1✔
891
          return null;
1✔
892
        } else {
893
          V prior = Async.getWhenSuccessful(computed);
1✔
894
          if (prior != null) {
1!
895
            return prior;
1✔
896
          }
897
        }
898
      }
×
899
    }
900

901
    @Override
902
    @SuppressWarnings("ResultOfMethodCallIgnored")
903
    public void putAll(Map<? extends K, ? extends V> map) {
904
      map.forEach(this::put);
1✔
905
    }
1✔
906

907
    @Override
908
    public @Nullable V put(K key, V value) {
909
      requireNonNull(value);
1✔
910
      CompletableFuture<V> oldValueFuture =
1✔
911
          delegate.put(key, CompletableFuture.completedFuture(value));
1✔
912
      return Async.getWhenSuccessful(oldValueFuture);
1✔
913
    }
914

915
    @Override
916
    public @Nullable V remove(Object key) {
917
      CompletableFuture<V> oldValueFuture = delegate.remove(key);
1✔
918
      return Async.getWhenSuccessful(oldValueFuture);
1✔
919
    }
920

921
    @Override
922
    @SuppressWarnings("ResultOfMethodCallIgnored")
923
    public boolean remove(Object key, @Nullable Object value) {
924
      requireNonNull(key);
1✔
925
      if (value == null) {
1!
926
        return false;
×
927
      }
928

929
      @SuppressWarnings("unchecked")
930
      var castedKey = (K) key;
1✔
931
      boolean[] done = { false };
1✔
932
      boolean[] removed = { false };
1✔
933
      for (;;) {
934
        CompletableFuture<V> future = delegate.getIfPresentQuietly(castedKey);
1✔
935
        if ((future == null) || future.isCompletedExceptionally()) {
1!
936
          return false;
1✔
937
        }
938

939
        Async.getWhenSuccessful(future);
1✔
940
        var hints = new LocalCache.RemapHints();
1✔
941
        delegate.compute(castedKey, (K k, @Nullable CompletableFuture<V> oldValueFuture) -> {
1✔
942
          if (oldValueFuture == null) {
1✔
943
            done[0] = true;
1✔
944
            return null;
1✔
945
          } else if (!oldValueFuture.isDone()) {
1!
946
            hints.preserveTimestamps = true;
×
947
            hints.preserveRefresh = true;
×
948
            return oldValueFuture;
×
949
          }
950

951
          done[0] = true;
1✔
952
          V oldValue = Async.getIfReady(oldValueFuture);
1✔
953
          removed[0] = Objects.equals(value, oldValue);
1✔
954
          if (!removed[0] && (oldValue != null)) {
1✔
955
            hints.preserveTimestamps = true;
1✔
956
            hints.preserveRefresh = true;
1✔
957
          }
958
          return (oldValue == null) || removed[0] ? null : oldValueFuture;
1✔
959
        }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ true, hints);
1✔
960

961
        if (done[0]) {
1!
962
          return removed[0];
1✔
963
        }
964
      }
×
965
    }
966

967
    @Override
968
    @SuppressWarnings("ResultOfMethodCallIgnored")
969
    public @Nullable V replace(K key, V value) {
970
      requireNonNull(value);
1✔
971

972
      @SuppressWarnings({"rawtypes", "unchecked", "Varifier"})
973
      @Nullable V[] oldValue = (V[]) new Object[1];
1✔
974
      boolean[] done = { false };
1✔
975
      for (;;) {
976
        CompletableFuture<V> future = delegate.getIfPresentQuietly(key);
1✔
977
        if ((future == null) || future.isCompletedExceptionally()) {
1!
978
          return null;
1✔
979
        }
980

981
        Async.getWhenSuccessful(future);
1✔
982
        var hints = new LocalCache.RemapHints();
1✔
983
        delegate.compute(key, (K k, @Nullable CompletableFuture<V> oldValueFuture) -> {
1✔
984
          if (oldValueFuture == null) {
1✔
985
            done[0] = true;
1✔
986
            return null;
1✔
987
          } else if (!oldValueFuture.isDone()) {
1!
988
            hints.preserveTimestamps = true;
×
989
            hints.preserveRefresh = true;
×
990
            return oldValueFuture;
×
991
          }
992

993
          done[0] = true;
1✔
994
          oldValue[0] = Async.getIfReady(oldValueFuture);
1✔
995
          return (oldValue[0] == null) ? null : CompletableFuture.completedFuture(value);
1✔
996
        }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false, hints);
1✔
997

998
        if (done[0]) {
1!
999
          return oldValue[0];
1✔
1000
        }
1001
      }
×
1002
    }
1003

1004
    @Override
1005
    @SuppressWarnings("ResultOfMethodCallIgnored")
1006
    public boolean replace(K key, V oldValue, V newValue) {
1007
      requireNonNull(oldValue);
1✔
1008
      requireNonNull(newValue);
1✔
1009

1010
      boolean[] done = { false };
1✔
1011
      boolean[] replaced = { false };
1✔
1012
      for (;;) {
1013
        CompletableFuture<V> future = delegate.getIfPresentQuietly(key);
1✔
1014
        if ((future == null) || future.isCompletedExceptionally()) {
1!
1015
          return false;
1✔
1016
        }
1017

1018
        Async.getWhenSuccessful(future);
1✔
1019
        var hints = new LocalCache.RemapHints();
1✔
1020
        delegate.compute(key, (K k, @Nullable CompletableFuture<V> oldValueFuture) -> {
1✔
1021
          if (oldValueFuture == null) {
1!
1022
            done[0] = true;
×
1023
            return null;
×
1024
          } else if (!oldValueFuture.isDone()) {
1!
1025
            hints.preserveTimestamps = true;
×
1026
            hints.preserveRefresh = true;
×
1027
            return oldValueFuture;
×
1028
          }
1029

1030
          done[0] = true;
1✔
1031
          replaced[0] = Objects.equals(oldValue, Async.getIfReady(oldValueFuture));
1✔
1032
          if (!replaced[0]) {
1✔
1033
            hints.preserveTimestamps = true;
1✔
1034
            hints.preserveRefresh = true;
1✔
1035
          }
1036
          return replaced[0] ? CompletableFuture.completedFuture(newValue) : oldValueFuture;
1✔
1037
        }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false, hints);
1✔
1038

1039
        if (done[0]) {
1!
1040
          return replaced[0];
1✔
1041
        }
1042
      }
×
1043
    }
1044

1045
    @Override
1046
    @SuppressWarnings("ResultOfMethodCallIgnored")
1047
    public @Nullable V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
1048
      requireNonNull(mappingFunction);
1✔
1049

1050
      @Var CompletableFuture<V> priorFuture = null;
1✔
1051
      for (;;) {
1052
        priorFuture = (priorFuture == null)
1✔
1053
            ? delegate.get(key)
1✔
1054
            : delegate.getIfPresentQuietly(key);
1✔
1055
        if (priorFuture != null) {
1✔
1056
          if (!priorFuture.isDone()) {
1✔
1057
            Async.getWhenSuccessful(priorFuture);
1✔
1058
            continue;
1✔
1059
          }
1060

1061
          V prior = Async.getWhenSuccessful(priorFuture);
1✔
1062
          if (prior != null) {
1✔
1063
            delegate.statsCounter().recordHits(1);
1✔
1064
            return prior;
1✔
1065
          }
1066
        }
1067

1068
        @SuppressWarnings({"rawtypes", "unchecked"})
1069
        CompletableFuture<V>[] future = new CompletableFuture[1];
1✔
1070
        var hints = new LocalCache.RemapHints();
1✔
1071
        CompletableFuture<V> computed = delegate.compute(
1✔
1072
            key, (K k, @Nullable CompletableFuture<V> valueFuture) -> {
1073
              if ((valueFuture != null)
1✔
1074
                  && (!valueFuture.isDone() || (Async.getIfReady(valueFuture) != null))) {
1✔
1075
                hints.preserveTimestamps = true;
1✔
1076
                hints.preserveRefresh = true;
1✔
1077
                return valueFuture;
1✔
1078
              }
1079

1080
              V newValue = delegate.statsAware(mappingFunction, /* recordLoad= */ true).apply(key);
1✔
1081
              if (newValue == null) {
1!
1082
                return null;
×
1083
              }
1084
              future[0] = CompletableFuture.completedFuture(newValue);
1✔
1085
              return future[0];
1✔
1086
            }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false, hints);
1✔
1087

1088
        V result = Async.getWhenSuccessful(computed);
1✔
1089
        if ((computed == future[0]) || (result != null)) {
1✔
1090
          return result;
1✔
1091
        }
1092
      }
1✔
1093
    }
1094

1095
    @Override
1096
    @SuppressWarnings("ResultOfMethodCallIgnored")
1097
    public @Nullable V computeIfPresent(K key,
1098
        BiFunction<? super K, ? super V, ? extends @Nullable V> remappingFunction) {
1099
      requireNonNull(remappingFunction);
1✔
1100

1101
      @SuppressWarnings({"rawtypes", "unchecked", "Varifier"})
1102
      @Nullable V[] newValue = (@Nullable V[]) new Object[1];
1✔
1103
      for (;;) {
1104
        Async.getWhenSuccessful(delegate.getIfPresentQuietly(key));
1✔
1105

1106
        var hints = new LocalCache.RemapHints();
1✔
1107
        CompletableFuture<V> valueFuture = delegate.compute(
1✔
1108
            key, (K k, @Nullable CompletableFuture<V> oldValueFuture) -> {
1109
              if (oldValueFuture == null) {
1✔
1110
                return null;
1✔
1111
              } else if (!oldValueFuture.isDone()) {
1!
1112
                hints.preserveTimestamps = true;
×
1113
                hints.preserveRefresh = true;
×
1114
                return oldValueFuture;
×
1115
              }
1116

1117
              V oldValue = Async.getIfReady(oldValueFuture);
1✔
1118
              if (oldValue == null) {
1✔
1119
                return null;
1✔
1120
              }
1121

1122
              BiFunction<? super K, ? super V, ? extends @Nullable V> function =
1✔
1123
                  delegate.statsAware(remappingFunction);
1✔
1124
              newValue[0] = function.apply(key, oldValue);
1✔
1125
              return (newValue[0] == null) ? null : CompletableFuture.completedFuture(newValue[0]);
1!
1126
            }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false, hints);
1✔
1127

1128
        if (newValue[0] != null) {
1✔
1129
          return newValue[0];
1✔
1130
        } else if (valueFuture == null) {
1!
1131
          return null;
1✔
1132
        }
1133
      }
×
1134
    }
1135

1136
    @Override
1137
    @SuppressWarnings("ResultOfMethodCallIgnored")
1138
    public @Nullable V compute(K key,
1139
        BiFunction<? super K, ? super @Nullable V, ? extends @Nullable V> remappingFunction) {
1140
      // Keep in sync with BoundedVarExpiration.computeAsync(key, remappingFunction, expiry)
1141
      requireNonNull(remappingFunction);
1✔
1142

1143
      @SuppressWarnings({"rawtypes", "unchecked", "Varifier"})
1144
      @Nullable V[] newValue = (@Nullable V[]) new Object[1];
1✔
1145
      for (;;) {
1146
        Async.getWhenSuccessful(delegate.getIfPresentQuietly(key));
1✔
1147

1148
        var hints = new LocalCache.RemapHints();
1✔
1149
        CompletableFuture<V> valueFuture = delegate.compute(
1✔
1150
            key, (K k, @Nullable CompletableFuture<V> oldValueFuture) -> {
1151
              if ((oldValueFuture != null) && !oldValueFuture.isDone()) {
1!
1152
                hints.preserveTimestamps = true;
×
1153
                hints.preserveRefresh = true;
×
1154
                return oldValueFuture;
×
1155
              }
1156

1157
              V oldValue = Async.getIfReady(oldValueFuture);
1✔
1158
              BiFunction<? super K, ? super V, ? extends @Nullable V> function =
1✔
1159
                  delegate.statsAware(remappingFunction,
1✔
1160
                      /* recordLoad= */ true, /* recordLoadFailure= */ true);
1161
              newValue[0] = function.apply(key, oldValue);
1✔
1162
              return (newValue[0] == null) ? null : CompletableFuture.completedFuture(newValue[0]);
1✔
1163
            }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false, hints);
1✔
1164

1165
        if (newValue[0] != null) {
1✔
1166
          return newValue[0];
1✔
1167
        } else if (valueFuture == null) {
1!
1168
          return null;
1✔
1169
        }
1170
      }
×
1171
    }
1172

1173
    @Override
1174
    @SuppressWarnings("ResultOfMethodCallIgnored")
1175
    public @Nullable V merge(K key, V value,
1176
        BiFunction<? super V, ? super V, ? extends @Nullable V> remappingFunction) {
1177
      requireNonNull(value);
1✔
1178
      requireNonNull(remappingFunction);
1✔
1179

1180
      CompletableFuture<V> newValueFuture = CompletableFuture.completedFuture(value);
1✔
1181
      boolean[] merged = { false };
1✔
1182
      for (;;) {
1183
        Async.getWhenSuccessful(delegate.getIfPresentQuietly(key));
1✔
1184

1185
        var hints = new LocalCache.RemapHints();
1✔
1186
        CompletableFuture<V> mergedValueFuture = delegate.compute(
1✔
1187
            key, (K k, @Nullable CompletableFuture<V> oldValueFuture) -> {
1188
              if (oldValueFuture == null) {
1✔
1189
                merged[0] = true;
1✔
1190
                return newValueFuture;
1✔
1191
              } else if (!oldValueFuture.isDone()) {
1!
1192
                hints.preserveTimestamps = true;
×
1193
                hints.preserveRefresh = true;
×
1194
                return oldValueFuture;
×
1195
              }
1196

1197
              merged[0] = true;
1✔
1198
              V oldValue = Async.getIfReady(oldValueFuture);
1✔
1199
              if (oldValue == null) {
1✔
1200
                return newValueFuture;
1✔
1201
              }
1202
              V mergedValue = delegate.statsAware(remappingFunction).apply(oldValue, value);
1✔
1203
              if (mergedValue == null) {
1!
1204
                return null;
×
1205
              } else if (mergedValue == oldValue) {
1✔
1206
                return oldValueFuture;
1✔
1207
              } else if (mergedValue == value) {
1✔
1208
                return newValueFuture;
1✔
1209
              }
1210
              return CompletableFuture.completedFuture(mergedValue);
1✔
1211
            }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false, hints);
1✔
1212

1213
        if (merged[0]) {
1!
1214
          return Async.getWhenSuccessful(mergedValueFuture);
1✔
1215
        }
1216
      }
×
1217
    }
1218

1219
    @Override
1220
    public Set<K> keySet() {
1221
      return (keys == null) ? (keys = new KeySet()) : keys;
1✔
1222
    }
1223

1224
    @Override
1225
    public Collection<V> values() {
1226
      return (values == null) ? (values = new Values()) : values;
1✔
1227
    }
1228

1229
    @Override
1230
    public Set<Entry<K, V>> entrySet() {
1231
      return (entries == null) ? (entries = new EntrySet()) : entries;
1✔
1232
    }
1233

1234
    /** See {@link BoundedLocalCache#equals(Object)} for semantics. */
1235
    @Override
1236
    public boolean equals(@Nullable Object o) {
1237
      if (o == this) {
1✔
1238
        return true;
1✔
1239
      } else if (!(o instanceof Map)) {
1!
1240
        return false;
×
1241
      }
1242

1243
      var map = (Map<?, ?>) o;
1✔
1244
      int expectedSize = size();
1✔
1245
      if (map.size() != expectedSize) {
1✔
1246
        return false;
1✔
1247
      }
1248

1249
      @Var int count = 0;
1✔
1250
      try {
1251
        for (var iterator = new EntryIterator(); iterator.hasNext();) {
1✔
1252
          var entry = iterator.next();
1✔
1253
          var value = map.get(entry.getKey());
1✔
1254
          if ((value == null) || ((value != entry.getValue()) && !value.equals(entry.getValue()))) {
1!
1255
            return false;
1✔
1256
          }
1257
          count++;
1✔
1258
        }
1✔
1259
      } catch (ClassCastException | NullPointerException ignored) {
×
1260
        return false;
×
1261
      }
1✔
1262
      return (count == expectedSize);
1✔
1263
    }
1264

1265
    @Override
1266
    public int hashCode() {
1267
      @Var int hash = 0;
1✔
1268
      for (var iterator = new EntryIterator(); iterator.hasNext();) {
1✔
1269
        var entry = iterator.next();
1✔
1270
        hash += entry.hashCode();
1✔
1271
      }
1✔
1272
      return hash;
1✔
1273
    }
1274

1275
    @Override
1276
    public String toString() {
1277
      var result = new StringBuilder(50).append('{');
1✔
1278
      for (var iterator = new EntryIterator(); iterator.hasNext();) {
1✔
1279
        var entry = iterator.next();
1✔
1280
        result.append((entry.getKey() == this) ? "(this Map)" : entry.getKey())
1!
1281
            .append('=')
1✔
1282
            .append((entry.getValue() == this) ? "(this Map)" : entry.getValue());
1!
1283

1284
        if (iterator.hasNext()) {
1✔
1285
          result.append(", ");
1✔
1286
        }
1287
      }
1✔
1288
      return result.append('}').toString();
1✔
1289
    }
1290

1291
    private final class KeySet extends AbstractSet<K> {
1✔
1292

1293
      @Override
1294
      public boolean isEmpty() {
1295
        return AsMapView.this.isEmpty();
1✔
1296
      }
1297

1298
      @Override
1299
      public int size() {
1300
        return AsMapView.this.size();
1✔
1301
      }
1302

1303
      @Override
1304
      public void clear() {
1305
        AsMapView.this.clear();
×
1306
      }
×
1307

1308
      @Override
1309
      public boolean contains(Object o) {
1310
        return AsMapView.this.containsKey(o);
1✔
1311
      }
1312

1313
      @Override
1314
      public boolean containsAll(Collection<?> collection) {
1315
        requireNonNull(collection);
1✔
1316
        if (collection != this) {
1!
1317
          for (Object o : collection) {
1!
1318
            if ((o == null) || !contains(o)) {
×
1319
              return false;
×
1320
            }
1321
          }
×
1322
        }
1323
        return true;
1✔
1324
      }
1325

1326
      @Override
1327
      public boolean removeAll(Collection<?> collection) {
1328
        return delegate.keySet().removeAll(collection);
1✔
1329
      }
1330

1331
      @Override
1332
      @SuppressWarnings("RedundantCollectionOperation")
1333
      public boolean remove(Object o) {
1334
        return delegate.keySet().remove(o);
1✔
1335
      }
1336

1337
      @Override
1338
      public boolean removeIf(Predicate<? super K> filter) {
1339
        return delegate.keySet().removeIf(filter);
1✔
1340
      }
1341

1342
      @Override
1343
      public boolean retainAll(Collection<?> collection) {
1344
        return delegate.keySet().retainAll(collection);
1✔
1345
      }
1346

1347
      @Override
1348
      public Iterator<K> iterator() {
1349
        return new KeyIterator<>(new EntryIterator());
1✔
1350
      }
1351

1352
      @Override
1353
      public Spliterator<K> spliterator() {
1354
        return new KeySpliterator<>(new EntrySpliterator());
1✔
1355
      }
1356
    }
1357

1358
    private static final class KeyIterator<K, V> implements Iterator<K> {
1359
      private final Iterator<Entry<K, V>> iterator;
1360

1361
      KeyIterator(Iterator<Entry<K, V>> iterator) {
1✔
1362
        this.iterator = requireNonNull(iterator);
1✔
1363
      }
1✔
1364

1365
      @Override
1366
      public boolean hasNext() {
1367
        return iterator.hasNext();
1✔
1368
      }
1369

1370
      @Override
1371
      public K next() {
1372
        return iterator.next().getKey();
1✔
1373
      }
1374

1375
      @Override
1376
      public void remove() {
1377
        iterator.remove();
×
1378
      }
×
1379
    }
1380

1381
    private static final class KeySpliterator<K, V> implements Spliterator<K> {
1382
      private final Spliterator<Entry<K, V>> spliterator;
1383

1384
      KeySpliterator(Spliterator<Entry<K, V>> iterator) {
1✔
1385
        this.spliterator = requireNonNull(iterator);
1✔
1386
      }
1✔
1387

1388
      @Override
1389
      public boolean tryAdvance(Consumer<? super K> action) {
1390
        requireNonNull(action);
1✔
1391
        return spliterator.tryAdvance(entry -> {
1✔
1392
          action.accept(entry.getKey());
1✔
1393
        });
1✔
1394
      }
1395

1396
      @Override
1397
      public @Nullable Spliterator<K> trySplit() {
1398
        Spliterator<Entry<K, V>> split = spliterator.trySplit();
×
1399
        return (split == null) ? null : new KeySpliterator<>(split);
×
1400
      }
1401

1402
      @Override
1403
      public long estimateSize() {
1404
        return spliterator.estimateSize();
1✔
1405
      }
1406

1407
      @Override
1408
      public int characteristics() {
1409
        return DISTINCT | NONNULL | CONCURRENT;
1✔
1410
      }
1411
    }
1412

1413
    private final class Values extends AbstractCollection<V> {
1✔
1414

1415
      @Override
1416
      public boolean isEmpty() {
1417
        return AsMapView.this.isEmpty();
1✔
1418
      }
1419

1420
      @Override
1421
      public int size() {
1422
        return AsMapView.this.size();
1✔
1423
      }
1424

1425
      @Override
1426
      public void clear() {
1427
        AsMapView.this.clear();
×
1428
      }
×
1429

1430
      @Override
1431
      public boolean contains(Object o) {
1432
        return AsMapView.this.containsValue(o);
1✔
1433
      }
1434

1435
      @Override
1436
      public boolean containsAll(Collection<?> collection) {
1437
        requireNonNull(collection);
1✔
1438
        if (collection != this) {
1!
1439
          for (Object o : collection) {
1!
1440
            if ((o == null) || !contains(o)) {
1!
1441
              return false;
1✔
1442
            }
1443
          }
1✔
1444
        }
1445
        return true;
×
1446
      }
1447

1448
      @Override
1449
      public boolean removeAll(Collection<?> collection) {
1450
        requireNonNull(collection);
1✔
1451
        @Var boolean modified = false;
1✔
1452
        for (var entry : delegate.entrySet()) {
1✔
1453
          V value = Async.getIfReady(entry.getValue());
1✔
1454
          if ((value != null) && collection.contains(value)
1!
1455
              && AsMapView.this.remove(entry.getKey(), value)) {
1!
1456
            modified = true;
1✔
1457
          }
1458
        }
1✔
1459
        return modified;
1✔
1460
      }
1461

1462
      @Override
1463
      public boolean remove(@Nullable Object o) {
1464
        if (o == null) {
1!
1465
          return false;
1✔
1466
        }
1467
        for (var entry : delegate.entrySet()) {
×
1468
          V value = Async.getIfReady(entry.getValue());
×
1469
          if (o.equals(value) && AsMapView.this.remove(entry.getKey(), value)) {
×
1470
            return true;
×
1471
          }
1472
        }
×
1473
        return false;
×
1474
      }
1475

1476
      @Override
1477
      public boolean removeIf(Predicate<? super V> filter) {
1478
        requireNonNull(filter);
1✔
1479
        return delegate.values().removeIf(future -> {
1✔
1480
          V value = Async.getIfReady(future);
1✔
1481
          return (value != null) && filter.test(value);
1!
1482
        });
1483
      }
1484

1485
      @Override
1486
      public boolean retainAll(Collection<?> collection) {
1487
        requireNonNull(collection);
1✔
1488
        @Var boolean modified = false;
1✔
1489
        for (var entry : delegate.entrySet()) {
1✔
1490
          V value = Async.getIfReady(entry.getValue());
1✔
1491
          if ((value != null) && !collection.contains(value)
1!
1492
              && AsMapView.this.remove(entry.getKey(), value)) {
1!
1493
            modified = true;
1✔
1494
          }
1495
        }
1✔
1496
        return modified;
1✔
1497
      }
1498

1499
      @Override
1500
      public void forEach(Consumer<? super V> action) {
1501
        requireNonNull(action);
×
1502
        delegate.values().forEach(future -> {
×
1503
          V value = Async.getIfReady(future);
×
1504
          if (value != null) {
×
1505
            action.accept(value);
×
1506
          }
1507
        });
×
1508
      }
×
1509

1510
      @Override
1511
      public Iterator<V> iterator() {
1512
        return new ValueIterator<>(new EntryIterator());
1✔
1513
      }
1514

1515
      @Override
1516
      public Spliterator<V> spliterator() {
1517
        return new ValueSpliterator<>(new EntrySpliterator());
1✔
1518
      }
1519
    }
1520

1521
    private static final class ValueIterator<K, V> implements Iterator<V> {
1522
      private final Iterator<Entry<K, V>> iterator;
1523

1524
      ValueIterator(Iterator<Entry<K, V>> iterator) {
1✔
1525
        this.iterator = requireNonNull(iterator);
1✔
1526
      }
1✔
1527

1528
      @Override
1529
      public boolean hasNext() {
1530
        return iterator.hasNext();
1✔
1531
      }
1532

1533
      @Override
1534
      public V next() {
1535
        return iterator.next().getValue();
1✔
1536
      }
1537

1538
      @Override
1539
      public void remove() {
1540
        iterator.remove();
×
1541
      }
×
1542
    }
1543

1544
    private static final class ValueSpliterator<K, V> implements Spliterator<V> {
1545
      private final Spliterator<Entry<K, V>> spliterator;
1546

1547
      ValueSpliterator(Spliterator<Entry<K, V>> iterator) {
1✔
1548
        this.spliterator = requireNonNull(iterator);
1✔
1549
      }
1✔
1550

1551
      @Override
1552
      public boolean tryAdvance(Consumer<? super V> action) {
1553
        requireNonNull(action);
1✔
1554
        return spliterator.tryAdvance(entry -> {
1✔
1555
          action.accept(entry.getValue());
1✔
1556
        });
1✔
1557
      }
1558

1559
      @Override
1560
      public @Nullable Spliterator<V> trySplit() {
1561
        Spliterator<Entry<K, V>> split = spliterator.trySplit();
1✔
1562
        return (split == null) ? null : new ValueSpliterator<>(split);
1✔
1563
      }
1564

1565
      @Override
1566
      public long estimateSize() {
1567
        return spliterator.estimateSize();
×
1568
      }
1569

1570
      @Override
1571
      public int characteristics() {
1572
        return NONNULL | CONCURRENT;
1✔
1573
      }
1574
    }
1575

1576
    private final class EntrySet extends AbstractSet<Entry<K, V>> {
1✔
1577

1578
      @Override
1579
      public boolean isEmpty() {
1580
        return AsMapView.this.isEmpty();
1✔
1581
      }
1582

1583
      @Override
1584
      public int size() {
1585
        return AsMapView.this.size();
1✔
1586
      }
1587

1588
      @Override
1589
      public void clear() {
1590
        AsMapView.this.clear();
1✔
1591
      }
1✔
1592

1593
      @Override
1594
      public boolean contains(Object o) {
1595
        if (!(o instanceof Entry<?, ?>)) {
1✔
1596
          return false;
1✔
1597
        }
1598
        var entry = (Entry<?, ?>) o;
1✔
1599
        var key = entry.getKey();
1✔
1600
        var value = entry.getValue();
1✔
1601
        if ((key == null) || (value == null)) {
1✔
1602
          return false;
1✔
1603
        }
1604
        V cachedValue = Async.getIfReady(delegate.getIfPresentQuietly(key));
1✔
1605
        return value.equals(cachedValue);
1✔
1606
      }
1607

1608
      @Override
1609
      public boolean removeAll(Collection<?> collection) {
1610
        requireNonNull(collection);
1✔
1611
        @Var boolean modified = false;
1✔
1612
        if (delegate.collectKeys()
1!
1613
            || ((collection instanceof Set<?>) && (collection.size() > size()))) {
1✔
1614
          for (var entry : this) {
1✔
1615
            if (collection.contains(entry)) {
1✔
1616
              modified |= remove(entry);
1✔
1617
            }
1618
          }
1✔
1619
        } else {
1620
          for (var o : collection) {
1✔
1621
            modified |= remove(o);
1✔
1622
          }
1✔
1623
        }
1624
        return modified;
1✔
1625
      }
1626

1627
      @Override
1628
      public boolean remove(Object obj) {
1629
        if (!(obj instanceof Entry<?, ?>)) {
1✔
1630
          return false;
1✔
1631
        }
1632
        var entry = (Entry<?, ?>) obj;
1✔
1633
        var key = entry.getKey();
1✔
1634
        return (key != null) && AsMapView.this.remove(key, entry.getValue());
1✔
1635
      }
1636

1637
      @Override
1638
      public boolean removeIf(Predicate<? super Entry<K, V>> filter) {
1639
        requireNonNull(filter);
1✔
1640
        return delegate.entrySet().removeIf(entry -> {
1✔
1641
          V value = Async.getIfReady(entry.getValue());
1✔
1642
          return (value != null) && filter.test(Map.entry(entry.getKey(), value));
1!
1643
        });
1644
      }
1645

1646
      @Override
1647
      public boolean retainAll(Collection<?> collection) {
1648
        requireNonNull(collection);
1✔
1649
        @Var boolean modified = false;
1✔
1650
        for (var entry : this) {
1✔
1651
          if (!collection.contains(entry) && remove(entry)) {
1!
1652
            modified = true;
×
1653
          }
1654
        }
1✔
1655
        return modified;
1✔
1656
      }
1657

1658
      @Override
1659
      public Iterator<Entry<K, V>> iterator() {
1660
        return new EntryIterator();
1✔
1661
      }
1662

1663
      @Override
1664
      public Spliterator<Entry<K, V>> spliterator() {
1665
        return new EntrySpliterator();
1✔
1666
      }
1667
    }
1668

1669
    private final class EntryIterator implements Iterator<Entry<K, V>> {
1670
      final Iterator<Entry<K, CompletableFuture<V>>> iterator;
1671
      @Nullable Entry<K, V> cursor;
1672
      @Nullable K removalKey;
1673

1674
      EntryIterator() {
1✔
1675
        this.iterator = delegate.entrySet().iterator();
1✔
1676
      }
1✔
1677

1678
      @Override
1679
      public boolean hasNext() {
1680
        while ((cursor == null) && iterator.hasNext()) {
1✔
1681
          Entry<K, CompletableFuture<V>> entry = iterator.next();
1✔
1682
          V value = Async.getIfReady(entry.getValue());
1✔
1683
          if (value != null) {
1✔
1684
            cursor = new WriteThroughEntry<>(AsMapView.this, entry.getKey(), value);
1✔
1685
          }
1686
        }
1✔
1687
        return (cursor != null);
1✔
1688
      }
1689

1690
      @Override
1691
      public Entry<K, V> next() {
1692
        if (!hasNext()) {
1✔
1693
          throw new NoSuchElementException();
1✔
1694
        }
1695
        K key = requireNonNull(cursor).getKey();
1✔
1696
        Entry<K, V> entry = cursor;
1✔
1697
        removalKey = key;
1✔
1698
        cursor = null;
1✔
1699
        return entry;
1✔
1700
      }
1701

1702
      @Override
1703
      public void remove() {
1704
        requireState(removalKey != null);
1✔
1705
        delegate.remove(removalKey);
1✔
1706
        removalKey = null;
1✔
1707
      }
1✔
1708
    }
1709

1710
    private final class EntrySpliterator implements Spliterator<Entry<K, V>> {
1711
      final Spliterator<Entry<K, CompletableFuture<V>>> spliterator;
1712

1713
      EntrySpliterator() {
1714
        this(delegate.entrySet().spliterator());
1✔
1715
      }
1✔
1716

1717
      EntrySpliterator(Spliterator<Entry<K, CompletableFuture<V>>> spliterator) {
1✔
1718
        this.spliterator = requireNonNull(spliterator);
1✔
1719
      }
1✔
1720

1721
      @Override
1722
      public void forEachRemaining(Consumer<? super Entry<K, V>> action) {
1723
        requireNonNull(action);
1✔
1724
        spliterator.forEachRemaining(entry -> {
1✔
1725
          V value = Async.getIfReady(entry.getValue());
1✔
1726
          if (value != null) {
1!
1727
            var e = new WriteThroughEntry<>(AsMapView.this, entry.getKey(), value);
1✔
1728
            action.accept(e);
1✔
1729
          }
1730
        });
1✔
1731
      }
1✔
1732

1733
      @Override
1734
      public boolean tryAdvance(Consumer<? super Entry<K, V>> action) {
1735
        requireNonNull(action);
1✔
1736
        boolean[] advanced = { false };
1✔
1737
        Consumer<? super Entry<K, CompletableFuture<V>>> consumer = entry -> {
1✔
1738
          V value = Async.getIfReady(entry.getValue());
1✔
1739
          if (value != null) {
1✔
1740
            var e = new WriteThroughEntry<>(AsMapView.this, entry.getKey(), value);
1✔
1741
            action.accept(e);
1✔
1742
            advanced[0] = true;
1✔
1743
          }
1744
        };
1✔
1745
        while (spliterator.tryAdvance(consumer)) {
1✔
1746
          if (advanced[0]) {
1✔
1747
            return true;
1✔
1748
          }
1749
        }
1750
        return false;
1✔
1751
      }
1752

1753
      @Override
1754
      public @Nullable Spliterator<Entry<K, V>> trySplit() {
1755
        Spliterator<Entry<K, CompletableFuture<V>>> split = spliterator.trySplit();
1✔
1756
        return (split == null) ? null : new EntrySpliterator(split);
1✔
1757
      }
1758

1759
      @Override
1760
      public long estimateSize() {
1761
        return spliterator.estimateSize();
1✔
1762
      }
1763

1764
      @Override
1765
      public int characteristics() {
1766
        return DISTINCT | CONCURRENT | NONNULL;
1✔
1767
      }
1768
    }
1769
  }
1770

1771
  @SuppressWarnings("serial")
1772
  final class SyncViewProxy<K, V> implements Serializable {
1773
    private static final long serialVersionUID = 1;
1774

1775
    final AsyncCache<K, V> asyncCache;
1776

1777
    SyncViewProxy(AsyncCache<K, V> asyncCache) {
1✔
1778
      this.asyncCache = requireNonNull(asyncCache);
1✔
1779
    }
1✔
1780

1781
    @SuppressWarnings("unused")
1782
    private void readObject(ObjectInputStream stream)
1783
        throws IOException, ClassNotFoundException {
1784
      stream.defaultReadObject();
1✔
1785
      if (asyncCache == null) {
1!
1786
        throw new InvalidObjectException("asyncCache required");
×
1787
      }
1788
    }
1✔
1789

1790
    Object readResolve() {
1791
      return asyncCache.synchronous();
1✔
1792
    }
1793
  }
1794
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc