Home | History | Annotate | Download | only in cache
      1 /*
      2  * Copyright (C) 2011 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
      5  * in compliance with the License. You may obtain a copy of the License at
      6  *
      7  * http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the License
     10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
     11  * or implied. See the License for the specific language governing permissions and limitations under
     12  * the License.
     13  */
     14 
     15 package com.google.common.cache;
     16 
     17 import static com.google.common.cache.TestingCacheLoaders.bulkLoader;
     18 import static com.google.common.cache.TestingCacheLoaders.constantLoader;
     19 import static com.google.common.cache.TestingCacheLoaders.errorLoader;
     20 import static com.google.common.cache.TestingCacheLoaders.exceptionLoader;
     21 import static com.google.common.cache.TestingCacheLoaders.identityLoader;
     22 import static com.google.common.cache.TestingRemovalListeners.countingRemovalListener;
     23 import static com.google.common.truth.Truth.assertThat;
     24 import static java.lang.Thread.currentThread;
     25 import static java.util.Arrays.asList;
     26 import static java.util.concurrent.TimeUnit.MILLISECONDS;
     27 
     28 import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
     29 import com.google.common.cache.TestingCacheLoaders.CountingLoader;
     30 import com.google.common.cache.TestingCacheLoaders.IdentityLoader;
     31 import com.google.common.cache.TestingRemovalListeners.CountingRemovalListener;
     32 import com.google.common.collect.ImmutableList;
     33 import com.google.common.collect.ImmutableMap;
     34 import com.google.common.collect.Lists;
     35 import com.google.common.collect.Maps;
     36 import com.google.common.testing.FakeTicker;
     37 import com.google.common.testing.TestLogHandler;
     38 import com.google.common.util.concurrent.Callables;
     39 import com.google.common.util.concurrent.ExecutionError;
     40 import com.google.common.util.concurrent.Futures;
     41 import com.google.common.util.concurrent.ListenableFuture;
     42 import com.google.common.util.concurrent.UncheckedExecutionException;
     43 
     44 import junit.framework.TestCase;
     45 
     46 import java.io.IOException;
     47 import java.lang.ref.WeakReference;
     48 import java.util.List;
     49 import java.util.Map;
     50 import java.util.concurrent.Callable;
     51 import java.util.concurrent.ConcurrentMap;
     52 import java.util.concurrent.CountDownLatch;
     53 import java.util.concurrent.ExecutionException;
     54 import java.util.concurrent.TimeUnit;
     55 import java.util.concurrent.atomic.AtomicInteger;
     56 import java.util.concurrent.atomic.AtomicReferenceArray;
     57 import java.util.logging.LogRecord;
     58 
     59 /**
     60  * Tests relating to cache loading: concurrent loading, exceptions during loading, etc.
     61  *
     62  * @author mike nonemacher
     63  */
     64 public class CacheLoadingTest extends TestCase {
     65   TestLogHandler logHandler;
     66 
     67   @Override
     68   public void setUp() throws Exception {
     69     super.setUp();
     70     logHandler = new TestLogHandler();
     71     LocalCache.logger.addHandler(logHandler);
     72   }
     73 
     74   @Override
     75   public void tearDown() throws Exception {
     76     super.tearDown();
     77     // TODO(cpovirk): run tests in other thread instead of messing with main thread interrupt status
     78     currentThread().interrupted();
     79     LocalCache.logger.removeHandler(logHandler);
     80   }
     81 
     82   private Throwable popLoggedThrowable() {
     83     List<LogRecord> logRecords = logHandler.getStoredLogRecords();
     84     assertEquals(1, logRecords.size());
     85     LogRecord logRecord = logRecords.get(0);
     86     logHandler.clear();
     87     return logRecord.getThrown();
     88   }
     89 
     90   private void checkNothingLogged() {
     91     assertTrue(logHandler.getStoredLogRecords().isEmpty());
     92   }
     93 
     94   private void checkLoggedCause(Throwable t) {
     95     assertSame(t, popLoggedThrowable().getCause());
     96   }
     97 
     98   private void checkLoggedInvalidLoad() {
     99     assertTrue(popLoggedThrowable() instanceof InvalidCacheLoadException);
    100   }
    101 
    102   public void testLoad() throws ExecutionException {
    103     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
    104         .recordStats()
    105         .build(identityLoader());
    106     CacheStats stats = cache.stats();
    107     assertEquals(0, stats.missCount());
    108     assertEquals(0, stats.loadSuccessCount());
    109     assertEquals(0, stats.loadExceptionCount());
    110     assertEquals(0, stats.hitCount());
    111 
    112     Object key = new Object();
    113     assertSame(key, cache.get(key));
    114     stats = cache.stats();
    115     assertEquals(1, stats.missCount());
    116     assertEquals(1, stats.loadSuccessCount());
    117     assertEquals(0, stats.loadExceptionCount());
    118     assertEquals(0, stats.hitCount());
    119 
    120     key = new Object();
    121     assertSame(key, cache.getUnchecked(key));
    122     stats = cache.stats();
    123     assertEquals(2, stats.missCount());
    124     assertEquals(2, stats.loadSuccessCount());
    125     assertEquals(0, stats.loadExceptionCount());
    126     assertEquals(0, stats.hitCount());
    127 
    128     key = new Object();
    129     cache.refresh(key);
    130     checkNothingLogged();
    131     stats = cache.stats();
    132     assertEquals(2, stats.missCount());
    133     assertEquals(3, stats.loadSuccessCount());
    134     assertEquals(0, stats.loadExceptionCount());
    135     assertEquals(0, stats.hitCount());
    136 
    137     assertSame(key, cache.get(key));
    138     stats = cache.stats();
    139     assertEquals(2, stats.missCount());
    140     assertEquals(3, stats.loadSuccessCount());
    141     assertEquals(0, stats.loadExceptionCount());
    142     assertEquals(1, stats.hitCount());
    143 
    144     Object value = new Object();
    145     // callable is not called
    146     assertSame(key, cache.get(key, throwing(new Exception())));
    147     stats = cache.stats();
    148     assertEquals(2, stats.missCount());
    149     assertEquals(3, stats.loadSuccessCount());
    150     assertEquals(0, stats.loadExceptionCount());
    151     assertEquals(2, stats.hitCount());
    152 
    153     key = new Object();
    154     assertSame(value, cache.get(key, Callables.returning(value)));
    155     stats = cache.stats();
    156     assertEquals(3, stats.missCount());
    157     assertEquals(4, stats.loadSuccessCount());
    158     assertEquals(0, stats.loadExceptionCount());
    159     assertEquals(2, stats.hitCount());
    160   }
    161 
    162   public void testReload() throws ExecutionException {
    163     final Object one = new Object();
    164     final Object two = new Object();
    165     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    166       @Override
    167       public Object load(Object key) {
    168         return one;
    169       }
    170 
    171       @Override
    172       public ListenableFuture<Object> reload(Object key, Object oldValue) {
    173         return Futures.immediateFuture(two);
    174       }
    175     };
    176 
    177     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
    178     Object key = new Object();
    179     CacheStats stats = cache.stats();
    180     assertEquals(0, stats.missCount());
    181     assertEquals(0, stats.loadSuccessCount());
    182     assertEquals(0, stats.loadExceptionCount());
    183     assertEquals(0, stats.hitCount());
    184 
    185     assertSame(one, cache.getUnchecked(key));
    186     stats = cache.stats();
    187     assertEquals(1, stats.missCount());
    188     assertEquals(1, stats.loadSuccessCount());
    189     assertEquals(0, stats.loadExceptionCount());
    190     assertEquals(0, stats.hitCount());
    191 
    192     cache.refresh(key);
    193     checkNothingLogged();
    194     stats = cache.stats();
    195     assertEquals(1, stats.missCount());
    196     assertEquals(2, stats.loadSuccessCount());
    197     assertEquals(0, stats.loadExceptionCount());
    198     assertEquals(0, stats.hitCount());
    199 
    200     assertSame(two, cache.getUnchecked(key));
    201     stats = cache.stats();
    202     assertEquals(1, stats.missCount());
    203     assertEquals(2, stats.loadSuccessCount());
    204     assertEquals(0, stats.loadExceptionCount());
    205     assertEquals(1, stats.hitCount());
    206   }
    207 
    208   public void testRefresh() {
    209     final Object one = new Object();
    210     final Object two = new Object();
    211     FakeTicker ticker = new FakeTicker();
    212     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    213       @Override
    214       public Object load(Object key) {
    215         return one;
    216       }
    217 
    218       @Override
    219       public ListenableFuture<Object> reload(Object key, Object oldValue) {
    220         return Futures.immediateFuture(two);
    221       }
    222     };
    223 
    224     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
    225         .recordStats()
    226         .ticker(ticker)
    227         .refreshAfterWrite(1, MILLISECONDS)
    228         .build(loader);
    229     Object key = new Object();
    230     CacheStats stats = cache.stats();
    231     assertEquals(0, stats.missCount());
    232     assertEquals(0, stats.loadSuccessCount());
    233     assertEquals(0, stats.loadExceptionCount());
    234     assertEquals(0, stats.hitCount());
    235 
    236     assertSame(one, cache.getUnchecked(key));
    237     stats = cache.stats();
    238     assertEquals(1, stats.missCount());
    239     assertEquals(1, stats.loadSuccessCount());
    240     assertEquals(0, stats.loadExceptionCount());
    241     assertEquals(0, stats.hitCount());
    242 
    243     ticker.advance(1, MILLISECONDS);
    244     assertSame(one, cache.getUnchecked(key));
    245     stats = cache.stats();
    246     assertEquals(1, stats.missCount());
    247     assertEquals(1, stats.loadSuccessCount());
    248     assertEquals(0, stats.loadExceptionCount());
    249     assertEquals(1, stats.hitCount());
    250 
    251     ticker.advance(1, MILLISECONDS);
    252     assertSame(two, cache.getUnchecked(key));
    253     stats = cache.stats();
    254     assertEquals(1, stats.missCount());
    255     assertEquals(2, stats.loadSuccessCount());
    256     assertEquals(0, stats.loadExceptionCount());
    257     assertEquals(2, stats.hitCount());
    258 
    259     ticker.advance(1, MILLISECONDS);
    260     assertSame(two, cache.getUnchecked(key));
    261     stats = cache.stats();
    262     assertEquals(1, stats.missCount());
    263     assertEquals(2, stats.loadSuccessCount());
    264     assertEquals(0, stats.loadExceptionCount());
    265     assertEquals(3, stats.hitCount());
    266   }
    267 
    268   public void testRefresh_getIfPresent() {
    269     final Object one = new Object();
    270     final Object two = new Object();
    271     FakeTicker ticker = new FakeTicker();
    272     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    273       @Override
    274       public Object load(Object key) {
    275         return one;
    276       }
    277 
    278       @Override
    279       public ListenableFuture<Object> reload(Object key, Object oldValue) {
    280         return Futures.immediateFuture(two);
    281       }
    282     };
    283 
    284     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
    285         .recordStats()
    286         .ticker(ticker)
    287         .refreshAfterWrite(1, MILLISECONDS)
    288         .build(loader);
    289     Object key = new Object();
    290     CacheStats stats = cache.stats();
    291     assertEquals(0, stats.missCount());
    292     assertEquals(0, stats.loadSuccessCount());
    293     assertEquals(0, stats.loadExceptionCount());
    294     assertEquals(0, stats.hitCount());
    295 
    296     assertSame(one, cache.getUnchecked(key));
    297     stats = cache.stats();
    298     assertEquals(1, stats.missCount());
    299     assertEquals(1, stats.loadSuccessCount());
    300     assertEquals(0, stats.loadExceptionCount());
    301     assertEquals(0, stats.hitCount());
    302 
    303     ticker.advance(1, MILLISECONDS);
    304     assertSame(one, cache.getIfPresent(key));
    305     stats = cache.stats();
    306     assertEquals(1, stats.missCount());
    307     assertEquals(1, stats.loadSuccessCount());
    308     assertEquals(0, stats.loadExceptionCount());
    309     assertEquals(1, stats.hitCount());
    310 
    311     ticker.advance(1, MILLISECONDS);
    312     assertSame(two, cache.getIfPresent(key));
    313     stats = cache.stats();
    314     assertEquals(1, stats.missCount());
    315     assertEquals(2, stats.loadSuccessCount());
    316     assertEquals(0, stats.loadExceptionCount());
    317     assertEquals(2, stats.hitCount());
    318 
    319     ticker.advance(1, MILLISECONDS);
    320     assertSame(two, cache.getIfPresent(key));
    321     stats = cache.stats();
    322     assertEquals(1, stats.missCount());
    323     assertEquals(2, stats.loadSuccessCount());
    324     assertEquals(0, stats.loadExceptionCount());
    325     assertEquals(3, stats.hitCount());
    326   }
    327 
    328   public void testBulkLoad_default() throws ExecutionException {
    329     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
    330         .recordStats()
    331         .build(TestingCacheLoaders.<Integer>identityLoader());
    332     CacheStats stats = cache.stats();
    333     assertEquals(0, stats.missCount());
    334     assertEquals(0, stats.loadSuccessCount());
    335     assertEquals(0, stats.loadExceptionCount());
    336     assertEquals(0, stats.hitCount());
    337 
    338     assertEquals(ImmutableMap.of(), cache.getAll(ImmutableList.<Integer>of()));
    339     assertEquals(0, stats.missCount());
    340     assertEquals(0, stats.loadSuccessCount());
    341     assertEquals(0, stats.loadExceptionCount());
    342     assertEquals(0, stats.hitCount());
    343 
    344     assertEquals(ImmutableMap.of(1, 1), cache.getAll(asList(1)));
    345     stats = cache.stats();
    346     assertEquals(1, stats.missCount());
    347     assertEquals(1, stats.loadSuccessCount());
    348     assertEquals(0, stats.loadExceptionCount());
    349     assertEquals(0, stats.hitCount());
    350 
    351     assertEquals(ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4), cache.getAll(asList(1, 2, 3, 4)));
    352     stats = cache.stats();
    353     assertEquals(4, stats.missCount());
    354     assertEquals(4, stats.loadSuccessCount());
    355     assertEquals(0, stats.loadExceptionCount());
    356     assertEquals(1, stats.hitCount());
    357 
    358     assertEquals(ImmutableMap.of(2, 2, 3, 3), cache.getAll(asList(2, 3)));
    359     stats = cache.stats();
    360     assertEquals(4, stats.missCount());
    361     assertEquals(4, stats.loadSuccessCount());
    362     assertEquals(0, stats.loadExceptionCount());
    363     assertEquals(3, stats.hitCount());
    364 
    365     // duplicate keys are ignored, and don't impact stats
    366     assertEquals(ImmutableMap.of(4, 4, 5, 5), cache.getAll(asList(4, 5)));
    367     stats = cache.stats();
    368     assertEquals(5, stats.missCount());
    369     assertEquals(5, stats.loadSuccessCount());
    370     assertEquals(0, stats.loadExceptionCount());
    371     assertEquals(4, stats.hitCount());
    372   }
    373 
    374   public void testBulkLoad_loadAll() throws ExecutionException {
    375     IdentityLoader<Integer> backingLoader = identityLoader();
    376     CacheLoader<Integer, Integer> loader = bulkLoader(backingLoader);
    377     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder().recordStats().build(loader);
    378     CacheStats stats = cache.stats();
    379     assertEquals(0, stats.missCount());
    380     assertEquals(0, stats.loadSuccessCount());
    381     assertEquals(0, stats.loadExceptionCount());
    382     assertEquals(0, stats.hitCount());
    383 
    384     assertEquals(ImmutableMap.of(), cache.getAll(ImmutableList.<Integer>of()));
    385     assertEquals(0, stats.missCount());
    386     assertEquals(0, stats.loadSuccessCount());
    387     assertEquals(0, stats.loadExceptionCount());
    388     assertEquals(0, stats.hitCount());
    389 
    390     assertEquals(ImmutableMap.of(1, 1), cache.getAll(asList(1)));
    391     stats = cache.stats();
    392     assertEquals(1, stats.missCount());
    393     assertEquals(1, stats.loadSuccessCount());
    394     assertEquals(0, stats.loadExceptionCount());
    395     assertEquals(0, stats.hitCount());
    396 
    397     assertEquals(ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4), cache.getAll(asList(1, 2, 3, 4)));
    398     stats = cache.stats();
    399     assertEquals(4, stats.missCount());
    400     assertEquals(2, stats.loadSuccessCount());
    401     assertEquals(0, stats.loadExceptionCount());
    402     assertEquals(1, stats.hitCount());
    403 
    404     assertEquals(ImmutableMap.of(2, 2, 3, 3), cache.getAll(asList(2, 3)));
    405     stats = cache.stats();
    406     assertEquals(4, stats.missCount());
    407     assertEquals(2, stats.loadSuccessCount());
    408     assertEquals(0, stats.loadExceptionCount());
    409     assertEquals(3, stats.hitCount());
    410 
    411     // duplicate keys are ignored, and don't impact stats
    412     assertEquals(ImmutableMap.of(4, 4, 5, 5), cache.getAll(asList(4, 5)));
    413     stats = cache.stats();
    414     assertEquals(5, stats.missCount());
    415     assertEquals(3, stats.loadSuccessCount());
    416     assertEquals(0, stats.loadExceptionCount());
    417     assertEquals(4, stats.hitCount());
    418   }
    419 
    420   public void testBulkLoad_extra() throws ExecutionException {
    421     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    422       @Override
    423       public Object load(Object key) throws Exception {
    424         return new Object();
    425       }
    426 
    427       @Override
    428       public Map<Object, Object> loadAll(Iterable<? extends Object> keys) throws Exception {
    429         Map<Object, Object> result = Maps.newHashMap();
    430         for (Object key : keys) {
    431           Object value = new Object();
    432           result.put(key, value);
    433           // add extra entries
    434           result.put(value, key);
    435         }
    436         return result;
    437       }
    438     };
    439     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
    440 
    441     Object[] lookupKeys = new Object[] { new Object(), new Object(), new Object() };
    442     Map<Object, Object> result = cache.getAll(asList(lookupKeys));
    443     assertThat(result.keySet()).has().exactlyAs(asList(lookupKeys));
    444     for (Map.Entry<Object, Object> entry : result.entrySet()) {
    445       Object key = entry.getKey();
    446       Object value = entry.getValue();
    447       assertSame(value, result.get(key));
    448       assertNull(result.get(value));
    449       assertSame(value, cache.asMap().get(key));
    450       assertSame(key, cache.asMap().get(value));
    451     }
    452   }
    453 
    454   public void testBulkLoad_clobber() throws ExecutionException {
    455     final Object extraKey = new Object();
    456     final Object extraValue = new Object();
    457     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    458       @Override
    459       public Object load(Object key) throws Exception {
    460         throw new AssertionError();
    461       }
    462 
    463       @Override
    464       public Map<Object, Object> loadAll(Iterable<? extends Object> keys) throws Exception {
    465         Map<Object, Object> result = Maps.newHashMap();
    466         for (Object key : keys) {
    467           Object value = new Object();
    468           result.put(key, value);
    469         }
    470         result.put(extraKey, extraValue);
    471         return result;
    472       }
    473     };
    474     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
    475     cache.asMap().put(extraKey, extraKey);
    476     assertSame(extraKey, cache.asMap().get(extraKey));
    477 
    478     Object[] lookupKeys = new Object[] { new Object(), new Object(), new Object() };
    479     Map<Object, Object> result = cache.getAll(asList(lookupKeys));
    480     assertThat(result.keySet()).has().exactlyAs(asList(lookupKeys));
    481     for (Map.Entry<Object, Object> entry : result.entrySet()) {
    482       Object key = entry.getKey();
    483       Object value = entry.getValue();
    484       assertSame(value, result.get(key));
    485       assertSame(value, cache.asMap().get(key));
    486     }
    487     assertNull(result.get(extraKey));
    488     assertSame(extraValue, cache.asMap().get(extraKey));
    489   }
    490 
    491   public void testBulkLoad_clobberNullValue() throws ExecutionException {
    492     final Object extraKey = new Object();
    493     final Object extraValue = new Object();
    494     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    495       @Override
    496       public Object load(Object key) throws Exception {
    497         throw new AssertionError();
    498       }
    499 
    500       @Override
    501       public Map<Object, Object> loadAll(Iterable<? extends Object> keys) throws Exception {
    502         Map<Object, Object> result = Maps.newHashMap();
    503         for (Object key : keys) {
    504           Object value = new Object();
    505           result.put(key, value);
    506         }
    507         result.put(extraKey, extraValue);
    508         result.put(extraValue, null);
    509         return result;
    510       }
    511     };
    512     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
    513     cache.asMap().put(extraKey, extraKey);
    514     assertSame(extraKey, cache.asMap().get(extraKey));
    515 
    516     Object[] lookupKeys = new Object[] { new Object(), new Object(), new Object() };
    517     try {
    518       cache.getAll(asList(lookupKeys));
    519       fail();
    520     } catch (InvalidCacheLoadException expected) {}
    521 
    522     for (Object key : lookupKeys) {
    523       assertTrue(cache.asMap().containsKey(key));
    524     }
    525     assertSame(extraValue, cache.asMap().get(extraKey));
    526     assertFalse(cache.asMap().containsKey(extraValue));
    527   }
    528 
    529   public void testBulkLoad_clobberNullKey() throws ExecutionException {
    530     final Object extraKey = new Object();
    531     final Object extraValue = new Object();
    532     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    533       @Override
    534       public Object load(Object key) throws Exception {
    535         throw new AssertionError();
    536       }
    537 
    538       @Override
    539       public Map<Object, Object> loadAll(Iterable<? extends Object> keys) throws Exception {
    540         Map<Object, Object> result = Maps.newHashMap();
    541         for (Object key : keys) {
    542           Object value = new Object();
    543           result.put(key, value);
    544         }
    545         result.put(extraKey, extraValue);
    546         result.put(null, extraKey);
    547         return result;
    548       }
    549     };
    550     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
    551     cache.asMap().put(extraKey, extraKey);
    552     assertSame(extraKey, cache.asMap().get(extraKey));
    553 
    554     Object[] lookupKeys = new Object[] { new Object(), new Object(), new Object() };
    555     try {
    556       cache.getAll(asList(lookupKeys));
    557       fail();
    558     } catch (InvalidCacheLoadException expected) {}
    559 
    560     for (Object key : lookupKeys) {
    561       assertTrue(cache.asMap().containsKey(key));
    562     }
    563     assertSame(extraValue, cache.asMap().get(extraKey));
    564     assertFalse(cache.asMap().containsValue(extraKey));
    565   }
    566 
    567   public void testBulkLoad_partial() throws ExecutionException {
    568     final Object extraKey = new Object();
    569     final Object extraValue = new Object();
    570     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    571       @Override
    572       public Object load(Object key) throws Exception {
    573         throw new AssertionError();
    574       }
    575 
    576       @Override
    577       public Map<Object, Object> loadAll(Iterable<? extends Object> keys) throws Exception {
    578         Map<Object, Object> result = Maps.newHashMap();
    579         // ignore request keys
    580         result.put(extraKey, extraValue);
    581         return result;
    582       }
    583     };
    584     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
    585 
    586     Object[] lookupKeys = new Object[] { new Object(), new Object(), new Object() };
    587     try {
    588       cache.getAll(asList(lookupKeys));
    589       fail();
    590     } catch (InvalidCacheLoadException expected) {}
    591     assertSame(extraValue, cache.asMap().get(extraKey));
    592   }
    593 
    594   public void testLoadNull() throws ExecutionException {
    595     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
    596         .recordStats()
    597         .build(constantLoader(null));
    598     CacheStats stats = cache.stats();
    599     assertEquals(0, stats.missCount());
    600     assertEquals(0, stats.loadSuccessCount());
    601     assertEquals(0, stats.loadExceptionCount());
    602     assertEquals(0, stats.hitCount());
    603 
    604     try {
    605       cache.get(new Object());
    606       fail();
    607     } catch (InvalidCacheLoadException expected) {}
    608     stats = cache.stats();
    609     assertEquals(1, stats.missCount());
    610     assertEquals(0, stats.loadSuccessCount());
    611     assertEquals(1, stats.loadExceptionCount());
    612     assertEquals(0, stats.hitCount());
    613 
    614     try {
    615       cache.getUnchecked(new Object());
    616       fail();
    617     } catch (InvalidCacheLoadException expected) {}
    618     stats = cache.stats();
    619     assertEquals(2, stats.missCount());
    620     assertEquals(0, stats.loadSuccessCount());
    621     assertEquals(2, stats.loadExceptionCount());
    622     assertEquals(0, stats.hitCount());
    623 
    624     cache.refresh(new Object());
    625     checkLoggedInvalidLoad();
    626     stats = cache.stats();
    627     assertEquals(2, stats.missCount());
    628     assertEquals(0, stats.loadSuccessCount());
    629     assertEquals(3, stats.loadExceptionCount());
    630     assertEquals(0, stats.hitCount());
    631 
    632     try {
    633       cache.get(new Object(), Callables.returning(null));
    634       fail();
    635     } catch (InvalidCacheLoadException expected) {}
    636     stats = cache.stats();
    637     assertEquals(3, stats.missCount());
    638     assertEquals(0, stats.loadSuccessCount());
    639     assertEquals(4, stats.loadExceptionCount());
    640     assertEquals(0, stats.hitCount());
    641 
    642     try {
    643       cache.getAll(asList(new Object()));
    644       fail();
    645     } catch (InvalidCacheLoadException expected) {}
    646     stats = cache.stats();
    647     assertEquals(4, stats.missCount());
    648     assertEquals(0, stats.loadSuccessCount());
    649     assertEquals(5, stats.loadExceptionCount());
    650     assertEquals(0, stats.hitCount());
    651   }
    652 
    653   public void testReloadNull() throws ExecutionException {
    654     final Object one = new Object();
    655     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    656       @Override
    657       public Object load(Object key) {
    658         return one;
    659       }
    660 
    661       @Override
    662       public ListenableFuture<Object> reload(Object key, Object oldValue) {
    663         return null;
    664       }
    665     };
    666 
    667     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
    668     Object key = new Object();
    669     CacheStats stats = cache.stats();
    670     assertEquals(0, stats.missCount());
    671     assertEquals(0, stats.loadSuccessCount());
    672     assertEquals(0, stats.loadExceptionCount());
    673     assertEquals(0, stats.hitCount());
    674 
    675     assertSame(one, cache.getUnchecked(key));
    676     stats = cache.stats();
    677     assertEquals(1, stats.missCount());
    678     assertEquals(1, stats.loadSuccessCount());
    679     assertEquals(0, stats.loadExceptionCount());
    680     assertEquals(0, stats.hitCount());
    681 
    682     cache.refresh(key);
    683     checkLoggedInvalidLoad();
    684     stats = cache.stats();
    685     assertEquals(1, stats.missCount());
    686     assertEquals(1, stats.loadSuccessCount());
    687     assertEquals(1, stats.loadExceptionCount());
    688     assertEquals(0, stats.hitCount());
    689 
    690     assertSame(one, cache.getUnchecked(key));
    691     stats = cache.stats();
    692     assertEquals(1, stats.missCount());
    693     assertEquals(1, stats.loadSuccessCount());
    694     assertEquals(1, stats.loadExceptionCount());
    695     assertEquals(1, stats.hitCount());
    696   }
    697 
    698   public void testReloadNullFuture() throws ExecutionException {
    699     final Object one = new Object();
    700     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    701       @Override
    702       public Object load(Object key) {
    703         return one;
    704       }
    705 
    706       @Override
    707       public ListenableFuture<Object> reload(Object key, Object oldValue) {
    708         return Futures.immediateFuture(null);
    709       }
    710     };
    711 
    712     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
    713     Object key = new Object();
    714     CacheStats stats = cache.stats();
    715     assertEquals(0, stats.missCount());
    716     assertEquals(0, stats.loadSuccessCount());
    717     assertEquals(0, stats.loadExceptionCount());
    718     assertEquals(0, stats.hitCount());
    719 
    720     assertSame(one, cache.getUnchecked(key));
    721     stats = cache.stats();
    722     assertEquals(1, stats.missCount());
    723     assertEquals(1, stats.loadSuccessCount());
    724     assertEquals(0, stats.loadExceptionCount());
    725     assertEquals(0, stats.hitCount());
    726 
    727     cache.refresh(key);
    728     checkLoggedInvalidLoad();
    729     stats = cache.stats();
    730     assertEquals(1, stats.missCount());
    731     assertEquals(1, stats.loadSuccessCount());
    732     assertEquals(1, stats.loadExceptionCount());
    733     assertEquals(0, stats.hitCount());
    734 
    735     assertSame(one, cache.getUnchecked(key));
    736     stats = cache.stats();
    737     assertEquals(1, stats.missCount());
    738     assertEquals(1, stats.loadSuccessCount());
    739     assertEquals(1, stats.loadExceptionCount());
    740     assertEquals(1, stats.hitCount());
    741   }
    742 
    743   public void testRefreshNull() {
    744     final Object one = new Object();
    745     FakeTicker ticker = new FakeTicker();
    746     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    747       @Override
    748       public Object load(Object key) {
    749         return one;
    750       }
    751 
    752       @Override
    753       public ListenableFuture<Object> reload(Object key, Object oldValue) {
    754         return Futures.immediateFuture(null);
    755       }
    756     };
    757 
    758     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
    759         .recordStats()
    760         .ticker(ticker)
    761         .refreshAfterWrite(1, MILLISECONDS)
    762         .build(loader);
    763     Object key = new Object();
    764     CacheStats stats = cache.stats();
    765     assertEquals(0, stats.missCount());
    766     assertEquals(0, stats.loadSuccessCount());
    767     assertEquals(0, stats.loadExceptionCount());
    768     assertEquals(0, stats.hitCount());
    769 
    770     assertSame(one, cache.getUnchecked(key));
    771     stats = cache.stats();
    772     assertEquals(1, stats.missCount());
    773     assertEquals(1, stats.loadSuccessCount());
    774     assertEquals(0, stats.loadExceptionCount());
    775     assertEquals(0, stats.hitCount());
    776 
    777     ticker.advance(1, MILLISECONDS);
    778     assertSame(one, cache.getUnchecked(key));
    779     stats = cache.stats();
    780     assertEquals(1, stats.missCount());
    781     assertEquals(1, stats.loadSuccessCount());
    782     assertEquals(0, stats.loadExceptionCount());
    783     assertEquals(1, stats.hitCount());
    784 
    785     ticker.advance(1, MILLISECONDS);
    786     assertSame(one, cache.getUnchecked(key));
    787     // refreshed
    788     stats = cache.stats();
    789     assertEquals(1, stats.missCount());
    790     assertEquals(1, stats.loadSuccessCount());
    791     assertEquals(1, stats.loadExceptionCount());
    792     assertEquals(2, stats.hitCount());
    793 
    794     ticker.advance(1, MILLISECONDS);
    795     assertSame(one, cache.getUnchecked(key));
    796     stats = cache.stats();
    797     assertEquals(1, stats.missCount());
    798     assertEquals(1, stats.loadSuccessCount());
    799     assertEquals(2, stats.loadExceptionCount());
    800     assertEquals(3, stats.hitCount());
    801   }
    802 
    803   public void testBulkLoadNull() throws ExecutionException {
    804     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
    805         .recordStats()
    806         .build(bulkLoader(constantLoader(null)));
    807     CacheStats stats = cache.stats();
    808     assertEquals(0, stats.missCount());
    809     assertEquals(0, stats.loadSuccessCount());
    810     assertEquals(0, stats.loadExceptionCount());
    811     assertEquals(0, stats.hitCount());
    812 
    813     try {
    814       cache.getAll(asList(new Object()));
    815       fail();
    816     } catch (InvalidCacheLoadException expected) {}
    817     stats = cache.stats();
    818     assertEquals(1, stats.missCount());
    819     assertEquals(0, stats.loadSuccessCount());
    820     assertEquals(1, stats.loadExceptionCount());
    821     assertEquals(0, stats.hitCount());
    822   }
    823 
    824   public void testBulkLoadNullMap() throws ExecutionException {
    825     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
    826         .recordStats()
    827         .build(new CacheLoader<Object, Object>() {
    828           @Override
    829           public Object load(Object key) {
    830             throw new AssertionError();
    831           }
    832 
    833           @Override
    834           public Map<Object, Object> loadAll(Iterable<? extends Object> keys) {
    835             return null;
    836           }
    837         });
    838 
    839     CacheStats stats = cache.stats();
    840     assertEquals(0, stats.missCount());
    841     assertEquals(0, stats.loadSuccessCount());
    842     assertEquals(0, stats.loadExceptionCount());
    843     assertEquals(0, stats.hitCount());
    844 
    845     try {
    846       cache.getAll(asList(new Object()));
    847       fail();
    848     } catch (InvalidCacheLoadException expected) {}
    849     stats = cache.stats();
    850     assertEquals(1, stats.missCount());
    851     assertEquals(0, stats.loadSuccessCount());
    852     assertEquals(1, stats.loadExceptionCount());
    853     assertEquals(0, stats.hitCount());
    854   }
    855 
    856   public void testLoadError() throws ExecutionException {
    857     Error e = new Error();
    858     CacheLoader<Object, Object> loader = errorLoader(e);
    859     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
    860     CacheStats stats = cache.stats();
    861     assertEquals(0, stats.missCount());
    862     assertEquals(0, stats.loadSuccessCount());
    863     assertEquals(0, stats.loadExceptionCount());
    864     assertEquals(0, stats.hitCount());
    865 
    866     try {
    867       cache.get(new Object());
    868       fail();
    869     } catch (ExecutionError expected) {
    870       assertSame(e, expected.getCause());
    871     }
    872     stats = cache.stats();
    873     assertEquals(1, stats.missCount());
    874     assertEquals(0, stats.loadSuccessCount());
    875     assertEquals(1, stats.loadExceptionCount());
    876     assertEquals(0, stats.hitCount());
    877 
    878     try {
    879       cache.getUnchecked(new Object());
    880       fail();
    881     } catch (ExecutionError expected) {
    882       assertSame(e, expected.getCause());
    883     }
    884     stats = cache.stats();
    885     assertEquals(2, stats.missCount());
    886     assertEquals(0, stats.loadSuccessCount());
    887     assertEquals(2, stats.loadExceptionCount());
    888     assertEquals(0, stats.hitCount());
    889 
    890     cache.refresh(new Object());
    891     checkLoggedCause(e);
    892     stats = cache.stats();
    893     assertEquals(2, stats.missCount());
    894     assertEquals(0, stats.loadSuccessCount());
    895     assertEquals(3, stats.loadExceptionCount());
    896     assertEquals(0, stats.hitCount());
    897 
    898     final Error callableError = new Error();
    899     try {
    900       cache.get(new Object(), new Callable<Object>() {
    901         @Override
    902         public Object call() {
    903           throw callableError;
    904         }
    905       });
    906       fail();
    907     } catch (ExecutionError expected) {
    908       assertSame(callableError, expected.getCause());
    909     }
    910     stats = cache.stats();
    911     assertEquals(3, stats.missCount());
    912     assertEquals(0, stats.loadSuccessCount());
    913     assertEquals(4, stats.loadExceptionCount());
    914     assertEquals(0, stats.hitCount());
    915 
    916     try {
    917       cache.getAll(asList(new Object()));
    918       fail();
    919     } catch (ExecutionError expected) {
    920       assertSame(e, expected.getCause());
    921     }
    922     stats = cache.stats();
    923     assertEquals(4, stats.missCount());
    924     assertEquals(0, stats.loadSuccessCount());
    925     assertEquals(5, stats.loadExceptionCount());
    926     assertEquals(0, stats.hitCount());
    927   }
    928 
    929   public void testReloadError() throws ExecutionException {
    930     final Object one = new Object();
    931     final Error e = new Error();
    932     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    933       @Override
    934       public Object load(Object key) {
    935         return one;
    936       }
    937 
    938       @Override
    939       public ListenableFuture<Object> reload(Object key, Object oldValue) {
    940         throw e;
    941       }
    942     };
    943 
    944     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
    945     Object key = new Object();
    946     CacheStats stats = cache.stats();
    947     assertEquals(0, stats.missCount());
    948     assertEquals(0, stats.loadSuccessCount());
    949     assertEquals(0, stats.loadExceptionCount());
    950     assertEquals(0, stats.hitCount());
    951 
    952     assertSame(one, cache.getUnchecked(key));
    953     stats = cache.stats();
    954     assertEquals(1, stats.missCount());
    955     assertEquals(1, stats.loadSuccessCount());
    956     assertEquals(0, stats.loadExceptionCount());
    957     assertEquals(0, stats.hitCount());
    958 
    959     cache.refresh(key);
    960     checkLoggedCause(e);
    961     stats = cache.stats();
    962     assertEquals(1, stats.missCount());
    963     assertEquals(1, stats.loadSuccessCount());
    964     assertEquals(1, stats.loadExceptionCount());
    965     assertEquals(0, stats.hitCount());
    966 
    967     assertSame(one, cache.getUnchecked(key));
    968     stats = cache.stats();
    969     assertEquals(1, stats.missCount());
    970     assertEquals(1, stats.loadSuccessCount());
    971     assertEquals(1, stats.loadExceptionCount());
    972     assertEquals(1, stats.hitCount());
    973   }
    974 
    975   public void testReloadFutureError() throws ExecutionException {
    976     final Object one = new Object();
    977     final Error e = new Error();
    978     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
    979       @Override
    980       public Object load(Object key) {
    981         return one;
    982       }
    983 
    984       @Override
    985       public ListenableFuture<Object> reload(Object key, Object oldValue) {
    986         return Futures.immediateFailedFuture(e);
    987       }
    988     };
    989 
    990     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
    991     Object key = new Object();
    992     CacheStats stats = cache.stats();
    993     assertEquals(0, stats.missCount());
    994     assertEquals(0, stats.loadSuccessCount());
    995     assertEquals(0, stats.loadExceptionCount());
    996     assertEquals(0, stats.hitCount());
    997 
    998     assertSame(one, cache.getUnchecked(key));
    999     stats = cache.stats();
   1000     assertEquals(1, stats.missCount());
   1001     assertEquals(1, stats.loadSuccessCount());
   1002     assertEquals(0, stats.loadExceptionCount());
   1003     assertEquals(0, stats.hitCount());
   1004 
   1005     cache.refresh(key);
   1006     checkLoggedCause(e);
   1007     stats = cache.stats();
   1008     assertEquals(1, stats.missCount());
   1009     assertEquals(1, stats.loadSuccessCount());
   1010     assertEquals(1, stats.loadExceptionCount());
   1011     assertEquals(0, stats.hitCount());
   1012 
   1013     assertSame(one, cache.getUnchecked(key));
   1014     stats = cache.stats();
   1015     assertEquals(1, stats.missCount());
   1016     assertEquals(1, stats.loadSuccessCount());
   1017     assertEquals(1, stats.loadExceptionCount());
   1018     assertEquals(1, stats.hitCount());
   1019   }
   1020 
   1021   public void testRefreshError() {
   1022     final Object one = new Object();
   1023     final Error e = new Error();
   1024     FakeTicker ticker = new FakeTicker();
   1025     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
   1026       @Override
   1027       public Object load(Object key) {
   1028         return one;
   1029       }
   1030 
   1031       @Override
   1032       public ListenableFuture<Object> reload(Object key, Object oldValue) {
   1033         return Futures.immediateFailedFuture(e);
   1034       }
   1035     };
   1036 
   1037     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
   1038         .recordStats()
   1039         .ticker(ticker)
   1040         .refreshAfterWrite(1, MILLISECONDS)
   1041         .build(loader);
   1042     Object key = new Object();
   1043     CacheStats stats = cache.stats();
   1044     assertEquals(0, stats.missCount());
   1045     assertEquals(0, stats.loadSuccessCount());
   1046     assertEquals(0, stats.loadExceptionCount());
   1047     assertEquals(0, stats.hitCount());
   1048 
   1049     assertSame(one, cache.getUnchecked(key));
   1050     stats = cache.stats();
   1051     assertEquals(1, stats.missCount());
   1052     assertEquals(1, stats.loadSuccessCount());
   1053     assertEquals(0, stats.loadExceptionCount());
   1054     assertEquals(0, stats.hitCount());
   1055 
   1056     ticker.advance(1, MILLISECONDS);
   1057     assertSame(one, cache.getUnchecked(key));
   1058     stats = cache.stats();
   1059     assertEquals(1, stats.missCount());
   1060     assertEquals(1, stats.loadSuccessCount());
   1061     assertEquals(0, stats.loadExceptionCount());
   1062     assertEquals(1, stats.hitCount());
   1063 
   1064     ticker.advance(1, MILLISECONDS);
   1065     assertSame(one, cache.getUnchecked(key));
   1066     // refreshed
   1067     stats = cache.stats();
   1068     assertEquals(1, stats.missCount());
   1069     assertEquals(1, stats.loadSuccessCount());
   1070     assertEquals(1, stats.loadExceptionCount());
   1071     assertEquals(2, stats.hitCount());
   1072 
   1073     ticker.advance(1, MILLISECONDS);
   1074     assertSame(one, cache.getUnchecked(key));
   1075     stats = cache.stats();
   1076     assertEquals(1, stats.missCount());
   1077     assertEquals(1, stats.loadSuccessCount());
   1078     assertEquals(2, stats.loadExceptionCount());
   1079     assertEquals(3, stats.hitCount());
   1080   }
   1081 
   1082   public void testBulkLoadError() throws ExecutionException {
   1083     Error e = new Error();
   1084     CacheLoader<Object, Object> loader = errorLoader(e);
   1085     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
   1086         .recordStats()
   1087         .build(bulkLoader(loader));
   1088     CacheStats stats = cache.stats();
   1089     assertEquals(0, stats.missCount());
   1090     assertEquals(0, stats.loadSuccessCount());
   1091     assertEquals(0, stats.loadExceptionCount());
   1092     assertEquals(0, stats.hitCount());
   1093 
   1094     try {
   1095       cache.getAll(asList(new Object()));
   1096       fail();
   1097     } catch (ExecutionError expected) {
   1098       assertSame(e, expected.getCause());
   1099     }
   1100     stats = cache.stats();
   1101     assertEquals(1, stats.missCount());
   1102     assertEquals(0, stats.loadSuccessCount());
   1103     assertEquals(1, stats.loadExceptionCount());
   1104     assertEquals(0, stats.hitCount());
   1105   }
   1106 
   1107   public void testLoadCheckedException() {
   1108     Exception e = new Exception();
   1109     CacheLoader<Object, Object> loader = exceptionLoader(e);
   1110     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
   1111     CacheStats stats = cache.stats();
   1112     assertEquals(0, stats.missCount());
   1113     assertEquals(0, stats.loadSuccessCount());
   1114     assertEquals(0, stats.loadExceptionCount());
   1115     assertEquals(0, stats.hitCount());
   1116 
   1117     try {
   1118       cache.get(new Object());
   1119       fail();
   1120     } catch (ExecutionException expected) {
   1121       assertSame(e, expected.getCause());
   1122     }
   1123     stats = cache.stats();
   1124     assertEquals(1, stats.missCount());
   1125     assertEquals(0, stats.loadSuccessCount());
   1126     assertEquals(1, stats.loadExceptionCount());
   1127     assertEquals(0, stats.hitCount());
   1128 
   1129     try {
   1130       cache.getUnchecked(new Object());
   1131       fail();
   1132     } catch (UncheckedExecutionException expected) {
   1133       assertSame(e, expected.getCause());
   1134     }
   1135     stats = cache.stats();
   1136     assertEquals(2, stats.missCount());
   1137     assertEquals(0, stats.loadSuccessCount());
   1138     assertEquals(2, stats.loadExceptionCount());
   1139     assertEquals(0, stats.hitCount());
   1140 
   1141     cache.refresh(new Object());
   1142     checkLoggedCause(e);
   1143     stats = cache.stats();
   1144     assertEquals(2, stats.missCount());
   1145     assertEquals(0, stats.loadSuccessCount());
   1146     assertEquals(3, stats.loadExceptionCount());
   1147     assertEquals(0, stats.hitCount());
   1148 
   1149     Exception callableException = new Exception();
   1150     try {
   1151       cache.get(new Object(), throwing(callableException));
   1152       fail();
   1153     } catch (ExecutionException expected) {
   1154       assertSame(callableException, expected.getCause());
   1155     }
   1156     stats = cache.stats();
   1157     assertEquals(3, stats.missCount());
   1158     assertEquals(0, stats.loadSuccessCount());
   1159     assertEquals(4, stats.loadExceptionCount());
   1160     assertEquals(0, stats.hitCount());
   1161 
   1162     try {
   1163       cache.getAll(asList(new Object()));
   1164       fail();
   1165     } catch (ExecutionException expected) {
   1166       assertSame(e, expected.getCause());
   1167     }
   1168     stats = cache.stats();
   1169     assertEquals(4, stats.missCount());
   1170     assertEquals(0, stats.loadSuccessCount());
   1171     assertEquals(5, stats.loadExceptionCount());
   1172     assertEquals(0, stats.hitCount());
   1173   }
   1174 
   1175   public void testLoadInterruptedException() {
   1176     Exception e = new InterruptedException();
   1177     CacheLoader<Object, Object> loader = exceptionLoader(e);
   1178     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
   1179     CacheStats stats = cache.stats();
   1180     assertEquals(0, stats.missCount());
   1181     assertEquals(0, stats.loadSuccessCount());
   1182     assertEquals(0, stats.loadExceptionCount());
   1183     assertEquals(0, stats.hitCount());
   1184 
   1185     // Sanity check:
   1186     assertFalse(currentThread().interrupted());
   1187 
   1188     try {
   1189       cache.get(new Object());
   1190       fail();
   1191     } catch (ExecutionException expected) {
   1192       assertSame(e, expected.getCause());
   1193     }
   1194     assertTrue(currentThread().interrupted());
   1195     stats = cache.stats();
   1196     assertEquals(1, stats.missCount());
   1197     assertEquals(0, stats.loadSuccessCount());
   1198     assertEquals(1, stats.loadExceptionCount());
   1199     assertEquals(0, stats.hitCount());
   1200 
   1201     try {
   1202       cache.getUnchecked(new Object());
   1203       fail();
   1204     } catch (UncheckedExecutionException expected) {
   1205       assertSame(e, expected.getCause());
   1206     }
   1207     assertTrue(currentThread().interrupted());
   1208     stats = cache.stats();
   1209     assertEquals(2, stats.missCount());
   1210     assertEquals(0, stats.loadSuccessCount());
   1211     assertEquals(2, stats.loadExceptionCount());
   1212     assertEquals(0, stats.hitCount());
   1213 
   1214     cache.refresh(new Object());
   1215     assertTrue(currentThread().interrupted());
   1216     checkLoggedCause(e);
   1217     stats = cache.stats();
   1218     assertEquals(2, stats.missCount());
   1219     assertEquals(0, stats.loadSuccessCount());
   1220     assertEquals(3, stats.loadExceptionCount());
   1221     assertEquals(0, stats.hitCount());
   1222 
   1223     Exception callableException = new InterruptedException();
   1224     try {
   1225       cache.get(new Object(), throwing(callableException));
   1226       fail();
   1227     } catch (ExecutionException expected) {
   1228       assertSame(callableException, expected.getCause());
   1229     }
   1230     assertTrue(currentThread().interrupted());
   1231     stats = cache.stats();
   1232     assertEquals(3, stats.missCount());
   1233     assertEquals(0, stats.loadSuccessCount());
   1234     assertEquals(4, stats.loadExceptionCount());
   1235     assertEquals(0, stats.hitCount());
   1236 
   1237     try {
   1238       cache.getAll(asList(new Object()));
   1239       fail();
   1240     } catch (ExecutionException expected) {
   1241       assertSame(e, expected.getCause());
   1242     }
   1243     assertTrue(currentThread().interrupted());
   1244     stats = cache.stats();
   1245     assertEquals(4, stats.missCount());
   1246     assertEquals(0, stats.loadSuccessCount());
   1247     assertEquals(5, stats.loadExceptionCount());
   1248     assertEquals(0, stats.hitCount());
   1249   }
   1250 
   1251   public void testReloadCheckedException() {
   1252     final Object one = new Object();
   1253     final Exception e = new Exception();
   1254     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
   1255       @Override
   1256       public Object load(Object key) {
   1257         return one;
   1258       }
   1259 
   1260       @Override
   1261       public ListenableFuture<Object> reload(Object key, Object oldValue) throws Exception {
   1262         throw e;
   1263       }
   1264     };
   1265 
   1266     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
   1267     Object key = new Object();
   1268     CacheStats stats = cache.stats();
   1269     assertEquals(0, stats.missCount());
   1270     assertEquals(0, stats.loadSuccessCount());
   1271     assertEquals(0, stats.loadExceptionCount());
   1272     assertEquals(0, stats.hitCount());
   1273 
   1274     assertSame(one, cache.getUnchecked(key));
   1275     stats = cache.stats();
   1276     assertEquals(1, stats.missCount());
   1277     assertEquals(1, stats.loadSuccessCount());
   1278     assertEquals(0, stats.loadExceptionCount());
   1279     assertEquals(0, stats.hitCount());
   1280 
   1281     cache.refresh(key);
   1282     checkLoggedCause(e);
   1283     stats = cache.stats();
   1284     assertEquals(1, stats.missCount());
   1285     assertEquals(1, stats.loadSuccessCount());
   1286     assertEquals(1, stats.loadExceptionCount());
   1287     assertEquals(0, stats.hitCount());
   1288 
   1289     assertSame(one, cache.getUnchecked(key));
   1290     stats = cache.stats();
   1291     assertEquals(1, stats.missCount());
   1292     assertEquals(1, stats.loadSuccessCount());
   1293     assertEquals(1, stats.loadExceptionCount());
   1294     assertEquals(1, stats.hitCount());
   1295   }
   1296 
   1297   public void testReloadFutureCheckedException() {
   1298     final Object one = new Object();
   1299     final Exception e = new Exception();
   1300     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
   1301       @Override
   1302       public Object load(Object key) {
   1303         return one;
   1304       }
   1305 
   1306       @Override
   1307       public ListenableFuture<Object> reload(Object key, Object oldValue) {
   1308         return Futures.immediateFailedFuture(e);
   1309       }
   1310     };
   1311 
   1312     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
   1313     Object key = new Object();
   1314     CacheStats stats = cache.stats();
   1315     assertEquals(0, stats.missCount());
   1316     assertEquals(0, stats.loadSuccessCount());
   1317     assertEquals(0, stats.loadExceptionCount());
   1318     assertEquals(0, stats.hitCount());
   1319 
   1320     assertSame(one, cache.getUnchecked(key));
   1321     stats = cache.stats();
   1322     assertEquals(1, stats.missCount());
   1323     assertEquals(1, stats.loadSuccessCount());
   1324     assertEquals(0, stats.loadExceptionCount());
   1325     assertEquals(0, stats.hitCount());
   1326 
   1327     cache.refresh(key);
   1328     checkLoggedCause(e);
   1329     stats = cache.stats();
   1330     assertEquals(1, stats.missCount());
   1331     assertEquals(1, stats.loadSuccessCount());
   1332     assertEquals(1, stats.loadExceptionCount());
   1333     assertEquals(0, stats.hitCount());
   1334 
   1335     assertSame(one, cache.getUnchecked(key));
   1336     stats = cache.stats();
   1337     assertEquals(1, stats.missCount());
   1338     assertEquals(1, stats.loadSuccessCount());
   1339     assertEquals(1, stats.loadExceptionCount());
   1340     assertEquals(1, stats.hitCount());
   1341   }
   1342 
   1343   public void testRefreshCheckedException() {
   1344     final Object one = new Object();
   1345     final Exception e = new Exception();
   1346     FakeTicker ticker = new FakeTicker();
   1347     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
   1348       @Override
   1349       public Object load(Object key) {
   1350         return one;
   1351       }
   1352 
   1353       @Override
   1354       public ListenableFuture<Object> reload(Object key, Object oldValue) {
   1355         return Futures.immediateFailedFuture(e);
   1356       }
   1357     };
   1358 
   1359     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
   1360         .recordStats()
   1361         .ticker(ticker)
   1362         .refreshAfterWrite(1, MILLISECONDS)
   1363         .build(loader);
   1364     Object key = new Object();
   1365     CacheStats stats = cache.stats();
   1366     assertEquals(0, stats.missCount());
   1367     assertEquals(0, stats.loadSuccessCount());
   1368     assertEquals(0, stats.loadExceptionCount());
   1369     assertEquals(0, stats.hitCount());
   1370 
   1371     assertSame(one, cache.getUnchecked(key));
   1372     stats = cache.stats();
   1373     assertEquals(1, stats.missCount());
   1374     assertEquals(1, stats.loadSuccessCount());
   1375     assertEquals(0, stats.loadExceptionCount());
   1376     assertEquals(0, stats.hitCount());
   1377 
   1378     ticker.advance(1, MILLISECONDS);
   1379     assertSame(one, cache.getUnchecked(key));
   1380     stats = cache.stats();
   1381     assertEquals(1, stats.missCount());
   1382     assertEquals(1, stats.loadSuccessCount());
   1383     assertEquals(0, stats.loadExceptionCount());
   1384     assertEquals(1, stats.hitCount());
   1385 
   1386     ticker.advance(1, MILLISECONDS);
   1387     assertSame(one, cache.getUnchecked(key));
   1388     // refreshed
   1389     stats = cache.stats();
   1390     assertEquals(1, stats.missCount());
   1391     assertEquals(1, stats.loadSuccessCount());
   1392     assertEquals(1, stats.loadExceptionCount());
   1393     assertEquals(2, stats.hitCount());
   1394 
   1395     ticker.advance(1, MILLISECONDS);
   1396     assertSame(one, cache.getUnchecked(key));
   1397     stats = cache.stats();
   1398     assertEquals(1, stats.missCount());
   1399     assertEquals(1, stats.loadSuccessCount());
   1400     assertEquals(2, stats.loadExceptionCount());
   1401     assertEquals(3, stats.hitCount());
   1402   }
   1403 
   1404   public void testBulkLoadCheckedException() {
   1405     Exception e = new Exception();
   1406     CacheLoader<Object, Object> loader = exceptionLoader(e);
   1407     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
   1408         .recordStats()
   1409         .build(bulkLoader(loader));
   1410     CacheStats stats = cache.stats();
   1411     assertEquals(0, stats.missCount());
   1412     assertEquals(0, stats.loadSuccessCount());
   1413     assertEquals(0, stats.loadExceptionCount());
   1414     assertEquals(0, stats.hitCount());
   1415 
   1416     try {
   1417       cache.getAll(asList(new Object()));
   1418       fail();
   1419     } catch (ExecutionException expected) {
   1420       assertSame(e, expected.getCause());
   1421     }
   1422     stats = cache.stats();
   1423     assertEquals(1, stats.missCount());
   1424     assertEquals(0, stats.loadSuccessCount());
   1425     assertEquals(1, stats.loadExceptionCount());
   1426     assertEquals(0, stats.hitCount());
   1427   }
   1428 
   1429   public void testBulkLoadInterruptedException() {
   1430     Exception e = new InterruptedException();
   1431     CacheLoader<Object, Object> loader = exceptionLoader(e);
   1432     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
   1433         .recordStats()
   1434         .build(bulkLoader(loader));
   1435     CacheStats stats = cache.stats();
   1436     assertEquals(0, stats.missCount());
   1437     assertEquals(0, stats.loadSuccessCount());
   1438     assertEquals(0, stats.loadExceptionCount());
   1439     assertEquals(0, stats.hitCount());
   1440 
   1441     try {
   1442       cache.getAll(asList(new Object()));
   1443       fail();
   1444     } catch (ExecutionException expected) {
   1445       assertSame(e, expected.getCause());
   1446     }
   1447     assertTrue(currentThread().interrupted());
   1448     stats = cache.stats();
   1449     assertEquals(1, stats.missCount());
   1450     assertEquals(0, stats.loadSuccessCount());
   1451     assertEquals(1, stats.loadExceptionCount());
   1452     assertEquals(0, stats.hitCount());
   1453   }
   1454 
   1455   public void testLoadUncheckedException() throws ExecutionException {
   1456     Exception e = new RuntimeException();
   1457     CacheLoader<Object, Object> loader = exceptionLoader(e);
   1458     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
   1459     CacheStats stats = cache.stats();
   1460     assertEquals(0, stats.missCount());
   1461     assertEquals(0, stats.loadSuccessCount());
   1462     assertEquals(0, stats.loadExceptionCount());
   1463     assertEquals(0, stats.hitCount());
   1464 
   1465     try {
   1466       cache.get(new Object());
   1467       fail();
   1468     } catch (UncheckedExecutionException expected) {
   1469       assertSame(e, expected.getCause());
   1470     }
   1471     stats = cache.stats();
   1472     assertEquals(1, stats.missCount());
   1473     assertEquals(0, stats.loadSuccessCount());
   1474     assertEquals(1, stats.loadExceptionCount());
   1475     assertEquals(0, stats.hitCount());
   1476 
   1477     try {
   1478       cache.getUnchecked(new Object());
   1479       fail();
   1480     } catch (UncheckedExecutionException expected) {
   1481       assertSame(e, expected.getCause());
   1482     }
   1483     stats = cache.stats();
   1484     assertEquals(2, stats.missCount());
   1485     assertEquals(0, stats.loadSuccessCount());
   1486     assertEquals(2, stats.loadExceptionCount());
   1487     assertEquals(0, stats.hitCount());
   1488 
   1489     cache.refresh(new Object());
   1490     checkLoggedCause(e);
   1491     stats = cache.stats();
   1492     assertEquals(2, stats.missCount());
   1493     assertEquals(0, stats.loadSuccessCount());
   1494     assertEquals(3, stats.loadExceptionCount());
   1495     assertEquals(0, stats.hitCount());
   1496 
   1497     Exception callableException = new RuntimeException();
   1498     try {
   1499       cache.get(new Object(), throwing(callableException));
   1500       fail();
   1501     } catch (UncheckedExecutionException expected) {
   1502       assertSame(callableException, expected.getCause());
   1503     }
   1504     stats = cache.stats();
   1505     assertEquals(3, stats.missCount());
   1506     assertEquals(0, stats.loadSuccessCount());
   1507     assertEquals(4, stats.loadExceptionCount());
   1508     assertEquals(0, stats.hitCount());
   1509 
   1510     try {
   1511       cache.getAll(asList(new Object()));
   1512       fail();
   1513     } catch (UncheckedExecutionException expected) {
   1514       assertSame(e, expected.getCause());
   1515     }
   1516     stats = cache.stats();
   1517     assertEquals(4, stats.missCount());
   1518     assertEquals(0, stats.loadSuccessCount());
   1519     assertEquals(5, stats.loadExceptionCount());
   1520     assertEquals(0, stats.hitCount());
   1521   }
   1522 
   1523   public void testReloadUncheckedException() throws ExecutionException {
   1524     final Object one = new Object();
   1525     final Exception e = new RuntimeException();
   1526     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
   1527       @Override
   1528       public Object load(Object key) {
   1529         return one;
   1530       }
   1531 
   1532       @Override
   1533       public ListenableFuture<Object> reload(Object key, Object oldValue) throws Exception {
   1534         throw e;
   1535       }
   1536     };
   1537 
   1538     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
   1539     Object key = new Object();
   1540     CacheStats stats = cache.stats();
   1541     assertEquals(0, stats.missCount());
   1542     assertEquals(0, stats.loadSuccessCount());
   1543     assertEquals(0, stats.loadExceptionCount());
   1544     assertEquals(0, stats.hitCount());
   1545 
   1546     assertSame(one, cache.getUnchecked(key));
   1547     stats = cache.stats();
   1548     assertEquals(1, stats.missCount());
   1549     assertEquals(1, stats.loadSuccessCount());
   1550     assertEquals(0, stats.loadExceptionCount());
   1551     assertEquals(0, stats.hitCount());
   1552 
   1553     cache.refresh(key);
   1554     checkLoggedCause(e);
   1555     stats = cache.stats();
   1556     assertEquals(1, stats.missCount());
   1557     assertEquals(1, stats.loadSuccessCount());
   1558     assertEquals(1, stats.loadExceptionCount());
   1559     assertEquals(0, stats.hitCount());
   1560 
   1561     assertSame(one, cache.getUnchecked(key));
   1562     stats = cache.stats();
   1563     assertEquals(1, stats.missCount());
   1564     assertEquals(1, stats.loadSuccessCount());
   1565     assertEquals(1, stats.loadExceptionCount());
   1566     assertEquals(1, stats.hitCount());
   1567   }
   1568 
   1569   public void testReloadFutureUncheckedException() throws ExecutionException {
   1570     final Object one = new Object();
   1571     final Exception e = new RuntimeException();
   1572     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
   1573       @Override
   1574       public Object load(Object key) {
   1575         return one;
   1576       }
   1577 
   1578       @Override
   1579       public ListenableFuture<Object> reload(Object key, Object oldValue) {
   1580         return Futures.immediateFailedFuture(e);
   1581       }
   1582     };
   1583 
   1584     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
   1585     Object key = new Object();
   1586     CacheStats stats = cache.stats();
   1587     assertEquals(0, stats.missCount());
   1588     assertEquals(0, stats.loadSuccessCount());
   1589     assertEquals(0, stats.loadExceptionCount());
   1590     assertEquals(0, stats.hitCount());
   1591 
   1592     assertSame(one, cache.getUnchecked(key));
   1593     stats = cache.stats();
   1594     assertEquals(1, stats.missCount());
   1595     assertEquals(1, stats.loadSuccessCount());
   1596     assertEquals(0, stats.loadExceptionCount());
   1597     assertEquals(0, stats.hitCount());
   1598 
   1599     cache.refresh(key);
   1600     checkLoggedCause(e);
   1601     stats = cache.stats();
   1602     assertEquals(1, stats.missCount());
   1603     assertEquals(1, stats.loadSuccessCount());
   1604     assertEquals(1, stats.loadExceptionCount());
   1605     assertEquals(0, stats.hitCount());
   1606 
   1607     assertSame(one, cache.getUnchecked(key));
   1608     stats = cache.stats();
   1609     assertEquals(1, stats.missCount());
   1610     assertEquals(1, stats.loadSuccessCount());
   1611     assertEquals(1, stats.loadExceptionCount());
   1612     assertEquals(1, stats.hitCount());
   1613   }
   1614 
   1615   public void testRefreshUncheckedException() {
   1616     final Object one = new Object();
   1617     final Exception e = new RuntimeException();
   1618     FakeTicker ticker = new FakeTicker();
   1619     CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
   1620       @Override
   1621       public Object load(Object key) {
   1622         return one;
   1623       }
   1624 
   1625       @Override
   1626       public ListenableFuture<Object> reload(Object key, Object oldValue) {
   1627         return Futures.immediateFailedFuture(e);
   1628       }
   1629     };
   1630 
   1631     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
   1632         .recordStats()
   1633         .ticker(ticker)
   1634         .refreshAfterWrite(1, MILLISECONDS)
   1635         .build(loader);
   1636     Object key = new Object();
   1637     CacheStats stats = cache.stats();
   1638     assertEquals(0, stats.missCount());
   1639     assertEquals(0, stats.loadSuccessCount());
   1640     assertEquals(0, stats.loadExceptionCount());
   1641     assertEquals(0, stats.hitCount());
   1642 
   1643     assertSame(one, cache.getUnchecked(key));
   1644     stats = cache.stats();
   1645     assertEquals(1, stats.missCount());
   1646     assertEquals(1, stats.loadSuccessCount());
   1647     assertEquals(0, stats.loadExceptionCount());
   1648     assertEquals(0, stats.hitCount());
   1649 
   1650     ticker.advance(1, MILLISECONDS);
   1651     assertSame(one, cache.getUnchecked(key));
   1652     stats = cache.stats();
   1653     assertEquals(1, stats.missCount());
   1654     assertEquals(1, stats.loadSuccessCount());
   1655     assertEquals(0, stats.loadExceptionCount());
   1656     assertEquals(1, stats.hitCount());
   1657 
   1658     ticker.advance(1, MILLISECONDS);
   1659     assertSame(one, cache.getUnchecked(key));
   1660     // refreshed
   1661     stats = cache.stats();
   1662     assertEquals(1, stats.missCount());
   1663     assertEquals(1, stats.loadSuccessCount());
   1664     assertEquals(1, stats.loadExceptionCount());
   1665     assertEquals(2, stats.hitCount());
   1666 
   1667     ticker.advance(1, MILLISECONDS);
   1668     assertSame(one, cache.getUnchecked(key));
   1669     stats = cache.stats();
   1670     assertEquals(1, stats.missCount());
   1671     assertEquals(1, stats.loadSuccessCount());
   1672     assertEquals(2, stats.loadExceptionCount());
   1673     assertEquals(3, stats.hitCount());
   1674   }
   1675 
   1676   public void testBulkLoadUncheckedException() throws ExecutionException {
   1677     Exception e = new RuntimeException();
   1678     CacheLoader<Object, Object> loader = exceptionLoader(e);
   1679     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
   1680         .recordStats()
   1681         .build(bulkLoader(loader));
   1682     CacheStats stats = cache.stats();
   1683     assertEquals(0, stats.missCount());
   1684     assertEquals(0, stats.loadSuccessCount());
   1685     assertEquals(0, stats.loadExceptionCount());
   1686     assertEquals(0, stats.hitCount());
   1687 
   1688     try {
   1689       cache.getAll(asList(new Object()));
   1690       fail();
   1691     } catch (UncheckedExecutionException expected) {
   1692       assertSame(e, expected.getCause());
   1693     }
   1694     stats = cache.stats();
   1695     assertEquals(1, stats.missCount());
   1696     assertEquals(0, stats.loadSuccessCount());
   1697     assertEquals(1, stats.loadExceptionCount());
   1698     assertEquals(0, stats.hitCount());
   1699   }
   1700 
   1701   public void testReloadAfterFailure() throws ExecutionException {
   1702     final AtomicInteger count = new AtomicInteger();
   1703     final Exception e = new IllegalStateException("exception to trigger failure on first load()");
   1704     CacheLoader<Integer, String> failOnceFunction = new CacheLoader<Integer, String>() {
   1705 
   1706       @Override
   1707       public String load(Integer key) throws Exception {
   1708         if (count.getAndIncrement() == 0) {
   1709           throw e;
   1710         }
   1711         return key.toString();
   1712       }
   1713     };
   1714     CountingRemovalListener<Integer, String> removalListener = countingRemovalListener();
   1715     LoadingCache<Integer, String> cache = CacheBuilder.newBuilder()
   1716         .removalListener(removalListener)
   1717         .build(failOnceFunction);
   1718 
   1719     try {
   1720       cache.getUnchecked(1);
   1721       fail();
   1722     } catch (UncheckedExecutionException ue) {
   1723       assertSame(e, ue.getCause());
   1724     }
   1725 
   1726     assertEquals("1", cache.getUnchecked(1));
   1727     assertEquals(0, removalListener.getCount());
   1728 
   1729     count.set(0);
   1730     cache.refresh(2);
   1731     checkLoggedCause(e);
   1732 
   1733     assertEquals("2", cache.getUnchecked(2));
   1734     assertEquals(0, removalListener.getCount());
   1735 
   1736   }
   1737 
   1738   public void testReloadAfterValueReclamation() throws InterruptedException, ExecutionException {
   1739     CountingLoader countingLoader = new CountingLoader();
   1740     LoadingCache<Object, Object> cache =
   1741         CacheBuilder.newBuilder().weakValues().build(countingLoader);
   1742     ConcurrentMap<Object, Object> map = cache.asMap();
   1743 
   1744     int iterations = 10;
   1745     WeakReference<Object> ref = new WeakReference<Object>(null);
   1746     int expectedComputations = 0;
   1747     for (int i = 0; i < iterations; i++) {
   1748       // The entry should get garbage collected and recomputed.
   1749       Object oldValue = ref.get();
   1750       if (oldValue == null) {
   1751         expectedComputations++;
   1752       }
   1753       ref = new WeakReference<Object>(cache.getUnchecked(1));
   1754       oldValue = null;
   1755       Thread.sleep(i);
   1756       System.gc();
   1757     }
   1758     assertEquals(expectedComputations, countingLoader.getCount());
   1759 
   1760     for (int i = 0; i < iterations; i++) {
   1761       // The entry should get garbage collected and recomputed.
   1762       Object oldValue = ref.get();
   1763       if (oldValue == null) {
   1764         expectedComputations++;
   1765       }
   1766       cache.refresh(1);
   1767       checkNothingLogged();
   1768       ref = new WeakReference<Object>(map.get(1));
   1769       oldValue = null;
   1770       Thread.sleep(i);
   1771       System.gc();
   1772     }
   1773     assertEquals(expectedComputations, countingLoader.getCount());
   1774   }
   1775 
   1776   public void testReloadAfterSimulatedValueReclamation() throws ExecutionException {
   1777     CountingLoader countingLoader = new CountingLoader();
   1778     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
   1779         .concurrencyLevel(1)
   1780         .weakValues()
   1781         .build(countingLoader);
   1782 
   1783     Object key = new Object();
   1784     assertNotNull(cache.getUnchecked(key));
   1785 
   1786     CacheTesting.simulateValueReclamation(cache, key);
   1787 
   1788     // this blocks if computation can't deal with partially-collected values
   1789     assertNotNull(cache.getUnchecked(key));
   1790     assertEquals(1, cache.size());
   1791     assertEquals(2, countingLoader.getCount());
   1792 
   1793     CacheTesting.simulateValueReclamation(cache, key);
   1794     cache.refresh(key);
   1795     checkNothingLogged();
   1796     assertEquals(1, cache.size());
   1797     assertEquals(3, countingLoader.getCount());
   1798   }
   1799 
   1800   public void testReloadAfterSimulatedKeyReclamation() throws ExecutionException {
   1801     CountingLoader countingLoader = new CountingLoader();
   1802     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
   1803         .concurrencyLevel(1)
   1804         .weakKeys()
   1805         .build(countingLoader);
   1806 
   1807     Object key = new Object();
   1808     assertNotNull(cache.getUnchecked(key));
   1809     assertEquals(1, cache.size());
   1810 
   1811     CacheTesting.simulateKeyReclamation(cache, key);
   1812 
   1813     // this blocks if computation can't deal with partially-collected values
   1814     assertNotNull(cache.getUnchecked(key));
   1815     assertEquals(2, countingLoader.getCount());
   1816 
   1817     CacheTesting.simulateKeyReclamation(cache, key);
   1818     cache.refresh(key);
   1819     checkNothingLogged();
   1820     assertEquals(3, countingLoader.getCount());
   1821   }
   1822 
   1823   /**
   1824    * Make sure LoadingCache correctly wraps ExecutionExceptions and UncheckedExecutionExceptions.
   1825    */
   1826   public void testLoadingExceptionWithCause() {
   1827     final Exception cause = new Exception();
   1828     final UncheckedExecutionException uee = new UncheckedExecutionException(cause);
   1829     final ExecutionException ee = new ExecutionException(cause);
   1830 
   1831     LoadingCache<Object, Object> cacheUnchecked =
   1832         CacheBuilder.newBuilder().build(exceptionLoader(uee));
   1833     LoadingCache<Object, Object> cacheChecked =
   1834         CacheBuilder.newBuilder().build(exceptionLoader(ee));
   1835 
   1836     try {
   1837       cacheUnchecked.get(new Object());
   1838       fail();
   1839     } catch (ExecutionException e) {
   1840       fail();
   1841     } catch (UncheckedExecutionException caughtEe) {
   1842       assertSame(uee, caughtEe.getCause());
   1843     }
   1844 
   1845     try {
   1846       cacheUnchecked.getUnchecked(new Object());
   1847       fail();
   1848     } catch (UncheckedExecutionException caughtUee) {
   1849       assertSame(uee, caughtUee.getCause());
   1850     }
   1851 
   1852     cacheUnchecked.refresh(new Object());
   1853     checkLoggedCause(uee);
   1854 
   1855     try {
   1856       cacheUnchecked.getAll(asList(new Object()));
   1857       fail();
   1858     } catch (ExecutionException e) {
   1859       fail();
   1860     } catch (UncheckedExecutionException caughtEe) {
   1861       assertSame(uee, caughtEe.getCause());
   1862     }
   1863 
   1864     try {
   1865       cacheChecked.get(new Object());
   1866       fail();
   1867     } catch (ExecutionException caughtEe) {
   1868       assertSame(ee, caughtEe.getCause());
   1869     }
   1870 
   1871     try {
   1872       cacheChecked.getUnchecked(new Object());
   1873       fail();
   1874     } catch (UncheckedExecutionException caughtUee) {
   1875       assertSame(ee, caughtUee.getCause());
   1876     }
   1877 
   1878     cacheChecked.refresh(new Object());
   1879     checkLoggedCause(ee);
   1880 
   1881     try {
   1882       cacheChecked.getAll(asList(new Object()));
   1883       fail();
   1884     } catch (ExecutionException caughtEe) {
   1885       assertSame(ee, caughtEe.getCause());
   1886     }
   1887   }
   1888 
   1889   public void testBulkLoadingExceptionWithCause() {
   1890     final Exception cause = new Exception();
   1891     final UncheckedExecutionException uee = new UncheckedExecutionException(cause);
   1892     final ExecutionException ee = new ExecutionException(cause);
   1893 
   1894     LoadingCache<Object, Object> cacheUnchecked =
   1895         CacheBuilder.newBuilder().build(bulkLoader(exceptionLoader(uee)));
   1896     LoadingCache<Object, Object> cacheChecked =
   1897         CacheBuilder.newBuilder().build(bulkLoader(exceptionLoader(ee)));
   1898 
   1899     try {
   1900       cacheUnchecked.getAll(asList(new Object()));
   1901       fail();
   1902     } catch (ExecutionException e) {
   1903       fail();
   1904     } catch (UncheckedExecutionException caughtEe) {
   1905       assertSame(uee, caughtEe.getCause());
   1906     }
   1907 
   1908     try {
   1909       cacheChecked.getAll(asList(new Object()));
   1910       fail();
   1911     } catch (ExecutionException caughtEe) {
   1912       assertSame(ee, caughtEe.getCause());
   1913     }
   1914   }
   1915 
   1916   public void testConcurrentLoading() throws InterruptedException {
   1917     testConcurrentLoading(CacheBuilder.newBuilder());
   1918   }
   1919 
   1920   public void testConcurrentExpirationLoading() throws InterruptedException {
   1921     testConcurrentLoading(CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS));
   1922   }
   1923 
   1924   private static void testConcurrentLoading(CacheBuilder<Object, Object> builder)
   1925       throws InterruptedException {
   1926     testConcurrentLoadingDefault(builder);
   1927     testConcurrentLoadingNull(builder);
   1928     testConcurrentLoadingUncheckedException(builder);
   1929     testConcurrentLoadingCheckedException(builder);
   1930   }
   1931 
   1932   /**
   1933    * On a successful concurrent computation, only one thread does the work, but all the threads get
   1934    * the same result.
   1935    */
   1936   private static void testConcurrentLoadingDefault(CacheBuilder<Object, Object> builder)
   1937       throws InterruptedException {
   1938 
   1939     int count = 10;
   1940     final AtomicInteger callCount = new AtomicInteger();
   1941     final CountDownLatch startSignal = new CountDownLatch(count + 1);
   1942     final Object result = new Object();
   1943 
   1944     LoadingCache<String, Object> cache = builder.build(
   1945         new CacheLoader<String, Object>() {
   1946           @Override public Object load(String key) throws InterruptedException {
   1947             callCount.incrementAndGet();
   1948             startSignal.await();
   1949             return result;
   1950           }
   1951         });
   1952 
   1953     List<Object> resultArray = doConcurrentGet(cache, "bar", count, startSignal);
   1954 
   1955     assertEquals(1, callCount.get());
   1956     for (int i = 0; i < count; i++) {
   1957       assertSame("result(" + i + ") didn't match expected", result, resultArray.get(i));
   1958     }
   1959   }
   1960 
   1961   /**
   1962    * On a concurrent computation that returns null, all threads should get an
   1963    * InvalidCacheLoadException, with the loader only called once. The result should not be cached
   1964    * (a later request should call the loader again).
   1965    */
   1966   private static void testConcurrentLoadingNull(CacheBuilder<Object, Object> builder)
   1967       throws InterruptedException {
   1968 
   1969     int count = 10;
   1970     final AtomicInteger callCount = new AtomicInteger();
   1971     final CountDownLatch startSignal = new CountDownLatch(count + 1);
   1972 
   1973     LoadingCache<String, String> cache = builder.build(
   1974         new CacheLoader<String, String>() {
   1975           @Override public String load(String key) throws InterruptedException {
   1976             callCount.incrementAndGet();
   1977             startSignal.await();
   1978             return null;
   1979           }
   1980         });
   1981 
   1982     List<Object> result = doConcurrentGet(cache, "bar", count, startSignal);
   1983 
   1984     assertEquals(1, callCount.get());
   1985     for (int i = 0; i < count; i++) {
   1986       assertTrue(result.get(i) instanceof InvalidCacheLoadException);
   1987     }
   1988 
   1989     // subsequent calls should call the loader again, not get the old exception
   1990     try {
   1991       cache.getUnchecked("bar");
   1992       fail();
   1993     } catch (InvalidCacheLoadException expected) {
   1994     }
   1995     assertEquals(2, callCount.get());
   1996   }
   1997 
   1998   /**
   1999    * On a concurrent computation that throws an unchecked exception, all threads should get the
   2000    * (wrapped) exception, with the loader called only once. The result should not be cached (a later
   2001    * request should call the loader again).
   2002    */
   2003   private static void testConcurrentLoadingUncheckedException(
   2004       CacheBuilder<Object, Object> builder) throws InterruptedException {
   2005 
   2006     int count = 10;
   2007     final AtomicInteger callCount = new AtomicInteger();
   2008     final CountDownLatch startSignal = new CountDownLatch(count + 1);
   2009     final RuntimeException e = new RuntimeException();
   2010 
   2011     LoadingCache<String, String> cache = builder.build(
   2012         new CacheLoader<String, String>() {
   2013           @Override public String load(String key) throws InterruptedException {
   2014             callCount.incrementAndGet();
   2015             startSignal.await();
   2016             throw e;
   2017           }
   2018         });
   2019 
   2020     List<Object> result = doConcurrentGet(cache, "bar", count, startSignal);
   2021 
   2022     assertEquals(1, callCount.get());
   2023     for (int i = 0; i < count; i++) {
   2024       // doConcurrentGet alternates between calling getUnchecked and calling get, but an unchecked
   2025       // exception thrown by the loader is always wrapped as an UncheckedExecutionException.
   2026       assertTrue(result.get(i) instanceof UncheckedExecutionException);
   2027       assertSame(e, ((UncheckedExecutionException) result.get(i)).getCause());
   2028     }
   2029 
   2030     // subsequent calls should call the loader again, not get the old exception
   2031     try {
   2032       cache.getUnchecked("bar");
   2033       fail();
   2034     } catch (UncheckedExecutionException expected) {
   2035     }
   2036     assertEquals(2, callCount.get());
   2037   }
   2038 
   2039   /**
   2040    * On a concurrent computation that throws a checked exception, all threads should get the
   2041    * (wrapped) exception, with the loader called only once. The result should not be cached (a later
   2042    * request should call the loader again).
   2043    */
   2044   private static void testConcurrentLoadingCheckedException(
   2045       CacheBuilder<Object, Object> builder) throws InterruptedException {
   2046 
   2047     int count = 10;
   2048     final AtomicInteger callCount = new AtomicInteger();
   2049     final CountDownLatch startSignal = new CountDownLatch(count + 1);
   2050     final IOException e = new IOException();
   2051 
   2052     LoadingCache<String, String> cache = builder.build(
   2053         new CacheLoader<String, String>() {
   2054           @Override public String load(String key) throws IOException, InterruptedException {
   2055             callCount.incrementAndGet();
   2056             startSignal.await();
   2057             throw e;
   2058           }
   2059         });
   2060 
   2061     List<Object> result = doConcurrentGet(cache, "bar", count, startSignal);
   2062 
   2063     assertEquals(1, callCount.get());
   2064     for (int i = 0; i < count; i++) {
   2065       // doConcurrentGet alternates between calling getUnchecked and calling get. If we call get(),
   2066       // we should get an ExecutionException; if we call getUnchecked(), we should get an
   2067       // UncheckedExecutionException.
   2068       int mod = i % 3;
   2069       if (mod == 0 || mod == 2) {
   2070         assertTrue(result.get(i) instanceof ExecutionException);
   2071         assertSame(e, ((ExecutionException) result.get(i)).getCause());
   2072       } else {
   2073         assertTrue(result.get(i) instanceof UncheckedExecutionException);
   2074         assertSame(e, ((UncheckedExecutionException) result.get(i)).getCause());
   2075       }
   2076     }
   2077 
   2078     // subsequent calls should call the loader again, not get the old exception
   2079     try {
   2080       cache.getUnchecked("bar");
   2081       fail();
   2082     } catch (UncheckedExecutionException expected) {
   2083     }
   2084     assertEquals(2, callCount.get());
   2085   }
   2086 
   2087   /**
   2088    * Test-helper method that performs {@code nThreads} concurrent calls to {@code cache.get(key)}
   2089    * or {@code cache.getUnchecked(key)}, and returns a List containing each of the results. The
   2090    * result for any given call to {@code cache.get} or {@code cache.getUnchecked} is the value
   2091    * returned, or the exception thrown.
   2092    *
   2093    * <p>As we iterate from {@code 0} to {@code nThreads}, threads with an even index will call
   2094    * {@code getUnchecked}, and threads with an odd index will call {@code get}. If the cache throws
   2095    * exceptions, this difference may be visible in the returned List.
   2096    */
   2097   private static <K> List<Object> doConcurrentGet(final LoadingCache<K, ?> cache, final K key,
   2098       int nThreads, final CountDownLatch gettersStartedSignal) throws InterruptedException {
   2099 
   2100     final AtomicReferenceArray<Object> result = new AtomicReferenceArray<Object>(nThreads);
   2101     final CountDownLatch gettersComplete = new CountDownLatch(nThreads);
   2102     for (int i = 0; i < nThreads; i++) {
   2103       final int index = i;
   2104       Thread thread = new Thread(new Runnable() {
   2105         @Override public void run() {
   2106           gettersStartedSignal.countDown();
   2107           Object value = null;
   2108           try {
   2109             int mod = index % 3;
   2110             if (mod == 0) {
   2111               value = cache.get(key);
   2112             } else if (mod == 1) {
   2113               value = cache.getUnchecked(key);
   2114             } else {
   2115               cache.refresh(key);
   2116               value = cache.get(key);
   2117             }
   2118             result.set(index, value);
   2119           } catch (Throwable t) {
   2120             result.set(index, t);
   2121           }
   2122           gettersComplete.countDown();
   2123         }
   2124       });
   2125       thread.start();
   2126       // we want to wait until each thread is WAITING - one thread waiting inside CacheLoader.load
   2127       // (in startSignal.await()), and the others waiting for that thread's result.
   2128       while (thread.isAlive() && thread.getState() != Thread.State.WAITING) {
   2129         Thread.yield();
   2130       }
   2131     }
   2132     gettersStartedSignal.countDown();
   2133     gettersComplete.await();
   2134 
   2135     List<Object> resultList = Lists.newArrayListWithExpectedSize(nThreads);
   2136     for (int i = 0; i < nThreads; i++) {
   2137       resultList.add(result.get(i));
   2138     }
   2139     return resultList;
   2140   }
   2141 
   2142   public void testAsMapDuringLoading() throws InterruptedException, ExecutionException {
   2143     final CountDownLatch getStartedSignal = new CountDownLatch(2);
   2144     final CountDownLatch letGetFinishSignal = new CountDownLatch(1);
   2145     final CountDownLatch getFinishedSignal = new CountDownLatch(2);
   2146     final String getKey = "get";
   2147     final String refreshKey = "refresh";
   2148     final String suffix = "Suffix";
   2149 
   2150     CacheLoader<String, String> computeFunction = new CacheLoader<String, String>() {
   2151       @Override
   2152       public String load(String key) throws InterruptedException {
   2153         getStartedSignal.countDown();
   2154         letGetFinishSignal.await();
   2155         return key + suffix;
   2156       }
   2157     };
   2158 
   2159     final LoadingCache<String, String> cache = CacheBuilder.newBuilder()
   2160         .build(computeFunction);
   2161     ConcurrentMap<String,String> map = cache.asMap();
   2162     map.put(refreshKey, refreshKey);
   2163     assertEquals(1, map.size());
   2164     assertFalse(map.containsKey(getKey));
   2165     assertSame(refreshKey, map.get(refreshKey));
   2166 
   2167     new Thread() {
   2168       @Override
   2169       public void run() {
   2170         cache.getUnchecked(getKey);
   2171         getFinishedSignal.countDown();
   2172       }
   2173     }.start();
   2174     new Thread() {
   2175       @Override
   2176       public void run() {
   2177         cache.refresh(refreshKey);
   2178         getFinishedSignal.countDown();
   2179       }
   2180     }.start();
   2181 
   2182     getStartedSignal.await();
   2183 
   2184     // computation is in progress; asMap shouldn't have changed
   2185     assertEquals(1, map.size());
   2186     assertFalse(map.containsKey(getKey));
   2187     assertSame(refreshKey, map.get(refreshKey));
   2188 
   2189     // let computation complete
   2190     letGetFinishSignal.countDown();
   2191     getFinishedSignal.await();
   2192     checkNothingLogged();
   2193 
   2194     // asMap view should have been updated
   2195     assertEquals(2, cache.size());
   2196     assertEquals(getKey + suffix, map.get(getKey));
   2197     assertEquals(refreshKey + suffix, map.get(refreshKey));
   2198   }
   2199 
   2200   public void testInvalidateDuringLoading() throws InterruptedException, ExecutionException {
   2201     // computation starts; invalidate() is called on the key being computed, computation finishes
   2202     final CountDownLatch computationStarted = new CountDownLatch(2);
   2203     final CountDownLatch letGetFinishSignal = new CountDownLatch(1);
   2204     final CountDownLatch getFinishedSignal = new CountDownLatch(2);
   2205     final String getKey = "get";
   2206     final String refreshKey = "refresh";
   2207     final String suffix = "Suffix";
   2208 
   2209     CacheLoader<String, String> computeFunction = new CacheLoader<String, String>() {
   2210       @Override
   2211       public String load(String key) throws InterruptedException {
   2212         computationStarted.countDown();
   2213         letGetFinishSignal.await();
   2214         return key + suffix;
   2215       }
   2216     };
   2217 
   2218     final LoadingCache<String, String> cache = CacheBuilder.newBuilder()
   2219         .build(computeFunction);
   2220     ConcurrentMap<String,String> map = cache.asMap();
   2221     map.put(refreshKey, refreshKey);
   2222 
   2223     new Thread() {
   2224       @Override
   2225       public void run() {
   2226         cache.getUnchecked(getKey);
   2227         getFinishedSignal.countDown();
   2228       }
   2229     }.start();
   2230     new Thread() {
   2231       @Override
   2232       public void run() {
   2233         cache.refresh(refreshKey);
   2234         getFinishedSignal.countDown();
   2235       }
   2236     }.start();
   2237 
   2238     computationStarted.await();
   2239     cache.invalidate(getKey);
   2240     cache.invalidate(refreshKey);
   2241     assertFalse(map.containsKey(getKey));
   2242     assertFalse(map.containsKey(refreshKey));
   2243 
   2244     // let computation complete
   2245     letGetFinishSignal.countDown();
   2246     getFinishedSignal.await();
   2247     checkNothingLogged();
   2248 
   2249     // results should be visible
   2250     assertEquals(2, cache.size());
   2251     assertEquals(getKey + suffix, map.get(getKey));
   2252     assertEquals(refreshKey + suffix, map.get(refreshKey));
   2253     assertEquals(2, cache.size());
   2254   }
   2255 
   2256   public void testInvalidateAndReloadDuringLoading()
   2257       throws InterruptedException, ExecutionException {
   2258     // computation starts; clear() is called, computation finishes
   2259     final CountDownLatch computationStarted = new CountDownLatch(2);
   2260     final CountDownLatch letGetFinishSignal = new CountDownLatch(1);
   2261     final CountDownLatch getFinishedSignal = new CountDownLatch(4);
   2262     final String getKey = "get";
   2263     final String refreshKey = "refresh";
   2264     final String suffix = "Suffix";
   2265 
   2266     CacheLoader<String, String> computeFunction = new CacheLoader<String, String>() {
   2267       @Override
   2268       public String load(String key) throws InterruptedException {
   2269         computationStarted.countDown();
   2270         letGetFinishSignal.await();
   2271         return key + suffix;
   2272       }
   2273     };
   2274 
   2275     final LoadingCache<String, String> cache = CacheBuilder.newBuilder()
   2276         .build(computeFunction);
   2277     ConcurrentMap<String,String> map = cache.asMap();
   2278     map.put(refreshKey, refreshKey);
   2279 
   2280     new Thread() {
   2281       @Override
   2282       public void run() {
   2283         cache.getUnchecked(getKey);
   2284         getFinishedSignal.countDown();
   2285       }
   2286     }.start();
   2287     new Thread() {
   2288       @Override
   2289       public void run() {
   2290         cache.refresh(refreshKey);
   2291         getFinishedSignal.countDown();
   2292       }
   2293     }.start();
   2294 
   2295     computationStarted.await();
   2296     cache.invalidate(getKey);
   2297     cache.invalidate(refreshKey);
   2298     assertFalse(map.containsKey(getKey));
   2299     assertFalse(map.containsKey(refreshKey));
   2300 
   2301     // start new computations
   2302     new Thread() {
   2303       @Override
   2304       public void run() {
   2305         cache.getUnchecked(getKey);
   2306         getFinishedSignal.countDown();
   2307       }
   2308     }.start();
   2309     new Thread() {
   2310       @Override
   2311       public void run() {
   2312         cache.refresh(refreshKey);
   2313         getFinishedSignal.countDown();
   2314       }
   2315     }.start();
   2316 
   2317     // let computation complete
   2318     letGetFinishSignal.countDown();
   2319     getFinishedSignal.await();
   2320     checkNothingLogged();
   2321 
   2322     // results should be visible
   2323     assertEquals(2, cache.size());
   2324     assertEquals(getKey + suffix, map.get(getKey));
   2325     assertEquals(refreshKey + suffix, map.get(refreshKey));
   2326   }
   2327 
   2328   public void testExpandDuringLoading() throws InterruptedException {
   2329     final int count = 3;
   2330     final AtomicInteger callCount = new AtomicInteger();
   2331     // tells the computing thread when to start computing
   2332     final CountDownLatch computeSignal = new CountDownLatch(1);
   2333     // tells the main thread when computation is pending
   2334     final CountDownLatch secondSignal = new CountDownLatch(1);
   2335     // tells the main thread when the second get has started
   2336     final CountDownLatch thirdSignal = new CountDownLatch(1);
   2337     // tells the main thread when the third get has started
   2338     final CountDownLatch fourthSignal = new CountDownLatch(1);
   2339     // tells the test when all gets have returned
   2340     final CountDownLatch doneSignal = new CountDownLatch(count);
   2341 
   2342     CacheLoader<String, String> computeFunction = new CacheLoader<String, String>() {
   2343       @Override
   2344       public String load(String key) throws InterruptedException {
   2345         callCount.incrementAndGet();
   2346         secondSignal.countDown();
   2347         computeSignal.await();
   2348         return key + "foo";
   2349       }
   2350     };
   2351 
   2352     final LoadingCache<String, String> cache = CacheBuilder.newBuilder()
   2353         .weakKeys()
   2354         .build(computeFunction);
   2355 
   2356     final AtomicReferenceArray<String> result = new AtomicReferenceArray<String>(count);
   2357 
   2358     final String key = "bar";
   2359 
   2360     // start computing thread
   2361     new Thread() {
   2362       @Override
   2363       public void run() {
   2364         result.set(0, cache.getUnchecked(key));
   2365         doneSignal.countDown();
   2366       }
   2367     }.start();
   2368 
   2369     // wait for computation to start
   2370     secondSignal.await();
   2371 
   2372     // start waiting thread
   2373     new Thread() {
   2374       @Override
   2375       public void run() {
   2376         thirdSignal.countDown();
   2377         result.set(1, cache.getUnchecked(key));
   2378         doneSignal.countDown();
   2379       }
   2380     }.start();
   2381 
   2382     // give the second get a chance to run; it is okay for this to be racy
   2383     // as the end result should be the same either way
   2384     thirdSignal.await();
   2385     Thread.yield();
   2386 
   2387     // Expand!
   2388     CacheTesting.forceExpandSegment(cache, key);
   2389 
   2390     // start another waiting thread
   2391     new Thread() {
   2392       @Override
   2393       public void run() {
   2394         fourthSignal.countDown();
   2395         result.set(2, cache.getUnchecked(key));
   2396         doneSignal.countDown();
   2397       }
   2398     }.start();
   2399 
   2400     // give the third get a chance to run; it is okay for this to be racy
   2401     // as the end result should be the same either way
   2402     fourthSignal.await();
   2403     Thread.yield();
   2404 
   2405     // let computation finish
   2406     computeSignal.countDown();
   2407     doneSignal.await();
   2408 
   2409     assertTrue(callCount.get() == 1);
   2410     assertEquals("barfoo", result.get(0));
   2411     assertEquals("barfoo", result.get(1));
   2412     assertEquals("barfoo", result.get(2));
   2413     assertEquals("barfoo", cache.getUnchecked(key));
   2414   }
   2415 
   2416   public void testExpandDuringRefresh() throws InterruptedException, ExecutionException {
   2417     final AtomicInteger callCount = new AtomicInteger();
   2418     // tells the computing thread when to start computing
   2419     final CountDownLatch computeSignal = new CountDownLatch(1);
   2420     // tells the main thread when computation is pending
   2421     final CountDownLatch secondSignal = new CountDownLatch(1);
   2422     // tells the main thread when the second get has started
   2423     final CountDownLatch thirdSignal = new CountDownLatch(1);
   2424     // tells the main thread when the third get has started
   2425     final CountDownLatch fourthSignal = new CountDownLatch(1);
   2426     // tells the test when all gets have returned
   2427     final CountDownLatch doneSignal = new CountDownLatch(3);
   2428     final String suffix = "Suffix";
   2429 
   2430     CacheLoader<String, String> computeFunction = new CacheLoader<String, String>() {
   2431       @Override
   2432       public String load(String key) throws InterruptedException {
   2433         callCount.incrementAndGet();
   2434         secondSignal.countDown();
   2435         computeSignal.await();
   2436         return key + suffix;
   2437       }
   2438     };
   2439 
   2440     final AtomicReferenceArray<String> result = new AtomicReferenceArray<String>(2);
   2441 
   2442     final LoadingCache<String, String> cache = CacheBuilder.newBuilder()
   2443         .build(computeFunction);
   2444     final String key = "bar";
   2445     cache.asMap().put(key, key);
   2446 
   2447     // start computing thread
   2448     new Thread() {
   2449       @Override
   2450       public void run() {
   2451         cache.refresh(key);
   2452         doneSignal.countDown();
   2453       }
   2454     }.start();
   2455 
   2456     // wait for computation to start
   2457     secondSignal.await();
   2458     checkNothingLogged();
   2459 
   2460     // start waiting thread
   2461     new Thread() {
   2462       @Override
   2463       public void run() {
   2464         thirdSignal.countDown();
   2465         result.set(0, cache.getUnchecked(key));
   2466         doneSignal.countDown();
   2467       }
   2468     }.start();
   2469 
   2470     // give the second get a chance to run; it is okay for this to be racy
   2471     // as the end result should be the same either way
   2472     thirdSignal.await();
   2473     Thread.yield();
   2474 
   2475     // Expand!
   2476     CacheTesting.forceExpandSegment(cache, key);
   2477 
   2478     // start another waiting thread
   2479     new Thread() {
   2480       @Override
   2481       public void run() {
   2482         fourthSignal.countDown();
   2483         result.set(1, cache.getUnchecked(key));
   2484         doneSignal.countDown();
   2485       }
   2486     }.start();
   2487 
   2488     // give the third get a chance to run; it is okay for this to be racy
   2489     // as the end result should be the same either way
   2490     fourthSignal.await();
   2491     Thread.yield();
   2492 
   2493     // let computation finish
   2494     computeSignal.countDown();
   2495     doneSignal.await();
   2496 
   2497     assertTrue(callCount.get() == 1);
   2498     assertEquals(key, result.get(0));
   2499     assertEquals(key, result.get(1));
   2500     assertEquals(key + suffix, cache.getUnchecked(key));
   2501   }
   2502 
   2503   static <T> Callable<T> throwing(final Exception exception) {
   2504     return new Callable<T>() {
   2505       @Override public T call() throws Exception {
   2506         throw exception;
   2507       }
   2508     };
   2509   }
   2510 }
   2511