@@ -424,68 +424,74 @@ public V computeIfAbsent(K key, CacheLoader<K, V> loader) throws ExecutionExcept
424
424
}
425
425
});
426
426
if (value == null ) {
427
- // we need to synchronize loading of a value for a given key; however, holding the segment lock while
428
- // invoking load can lead to deadlock against another thread due to dependent key loading; therefore, we
429
- // need a mechanism to ensure that load is invoked at most once, but we are not invoking load while holding
430
- // the segment lock; to do this, we atomically put a future in the map that can load the value, and then
431
- // get the value from this future on the thread that won the race to place the future into the segment map
432
- CacheSegment <K , V > segment = getCacheSegment (key );
433
- CompletableFuture <Entry <K , V >> future ;
434
- CompletableFuture <Entry <K , V >> completableFuture = new CompletableFuture <>();
427
+ value = compute (key , loader );
428
+ }
429
+ return value ;
430
+ }
435
431
436
- try (ReleasableLock ignored = segment .writeLock .acquire ()) {
437
- future = segment .map .putIfAbsent (key , completableFuture );
438
- }
432
+ private V compute (K key , CacheLoader <K , V > loader ) throws ExecutionException {
433
+ long now = now ();
434
+ // we need to synchronize loading of a value for a given key; however, holding the segment lock while
435
+ // invoking load can lead to deadlock against another thread due to dependent key loading; therefore, we
436
+ // need a mechanism to ensure that load is invoked at most once, but we are not invoking load while holding
437
+ // the segment lock; to do this, we atomically put a future in the map that can load the value, and then
438
+ // get the value from this future on the thread that won the race to place the future into the segment map
439
+ CacheSegment <K , V > segment = getCacheSegment (key );
440
+ CompletableFuture <Entry <K , V >> future ;
441
+ CompletableFuture <Entry <K , V >> completableFuture = new CompletableFuture <>();
439
442
440
- BiFunction <? super Entry <K , V >, Throwable , ? extends V > handler = (ok , ex ) -> {
441
- if (ok != null ) {
442
- try (ReleasableLock ignored = lruLock .acquire ()) {
443
- promote (ok , now );
444
- }
445
- return ok .value ;
446
- } else {
447
- try (ReleasableLock ignored = segment .writeLock .acquire ()) {
448
- CompletableFuture <Entry <K , V >> sanity = segment .map .get (key );
449
- if (sanity != null && sanity .isCompletedExceptionally ()) {
450
- segment .map .remove (key );
451
- }
452
- }
453
- return null ;
454
- }
455
- };
443
+ try (ReleasableLock ignored = segment .writeLock .acquire ()) {
444
+ future = segment .map .putIfAbsent (key , completableFuture );
445
+ }
456
446
457
- CompletableFuture <V > completableValue ;
458
- if (future == null ) {
459
- future = completableFuture ;
460
- completableValue = future .handle (handler );
461
- V loaded ;
462
- try {
463
- loaded = loader .load (key );
464
- } catch (Exception e ) {
465
- future .completeExceptionally (e );
466
- throw new ExecutionException (e );
467
- }
468
- if (loaded == null ) {
469
- NullPointerException npe = new NullPointerException ("loader returned a null value" );
470
- future .completeExceptionally (npe );
471
- throw new ExecutionException (npe );
472
- } else {
473
- future .complete (new Entry <>(key , loaded , now ));
447
+ BiFunction <? super Entry <K , V >, Throwable , ? extends V > handler = (ok , ex ) -> {
448
+ if (ok != null ) {
449
+ try (ReleasableLock ignored = lruLock .acquire ()) {
450
+ promote (ok , now );
474
451
}
452
+ return ok .value ;
475
453
} else {
476
- completableValue = future .handle (handler );
454
+ try (ReleasableLock ignored = segment .writeLock .acquire ()) {
455
+ CompletableFuture <Entry <K , V >> sanity = segment .map .get (key );
456
+ if (sanity != null && sanity .isCompletedExceptionally ()) {
457
+ segment .map .remove (key );
458
+ }
459
+ }
460
+ return null ;
477
461
}
462
+ };
478
463
464
+ CompletableFuture <V > completableValue ;
465
+ if (future == null ) {
466
+ future = completableFuture ;
467
+ completableValue = future .handle (handler );
468
+ V loaded ;
479
469
try {
480
- value = completableValue .get ();
481
- // check to ensure the future hasn't been completed with an exception
482
- if (future .isCompletedExceptionally ()) {
483
- future .get (); // call get to force the exception to be thrown for other concurrent callers
484
- throw new IllegalStateException ("the future was completed exceptionally but no exception was thrown" );
485
- }
486
- } catch (InterruptedException e ) {
487
- throw new IllegalStateException (e );
470
+ loaded = loader .load (key );
471
+ } catch (Exception e ) {
472
+ future .completeExceptionally (e );
473
+ throw new ExecutionException (e );
488
474
}
475
+ if (loaded == null ) {
476
+ NullPointerException npe = new NullPointerException ("loader returned a null value" );
477
+ future .completeExceptionally (npe );
478
+ throw new ExecutionException (npe );
479
+ } else {
480
+ future .complete (new Entry <>(key , loaded , now ));
481
+ }
482
+ } else {
483
+ completableValue = future .handle (handler );
484
+ }
485
+ V value ;
486
+ try {
487
+ value = completableValue .get ();
488
+ // check to ensure the future hasn't been completed with an exception
489
+ if (future .isCompletedExceptionally ()) {
490
+ future .get (); // call get to force the exception to be thrown for other concurrent callers
491
+ throw new IllegalStateException ("the future was completed exceptionally but no exception was thrown" );
492
+ }
493
+ } catch (InterruptedException e ) {
494
+ throw new IllegalStateException (e );
489
495
}
490
496
return value ;
491
497
}
0 commit comments