• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.cache;
18 
19 import static com.google.common.cache.CacheBuilder.NULL_TICKER;
20 import static com.google.common.cache.LocalCache.DISCARDING_QUEUE;
21 import static com.google.common.cache.LocalCache.DRAIN_THRESHOLD;
22 import static com.google.common.cache.LocalCache.nullEntry;
23 import static com.google.common.cache.LocalCache.unset;
24 import static com.google.common.cache.TestingCacheLoaders.identityLoader;
25 import static com.google.common.cache.TestingRemovalListeners.countingRemovalListener;
26 import static com.google.common.cache.TestingRemovalListeners.queuingRemovalListener;
27 import static com.google.common.cache.TestingWeighers.constantWeigher;
28 import static com.google.common.collect.Lists.newArrayList;
29 import static com.google.common.collect.Maps.immutableEntry;
30 import static java.util.concurrent.TimeUnit.MINUTES;
31 import static java.util.concurrent.TimeUnit.NANOSECONDS;
32 import static java.util.concurrent.TimeUnit.SECONDS;
33 import static org.easymock.EasyMock.createMock;
34 
35 import com.google.common.base.Equivalence;
36 import com.google.common.base.Ticker;
37 import com.google.common.cache.LocalCache.EntryFactory;
38 import com.google.common.cache.LocalCache.LoadingValueReference;
39 import com.google.common.cache.LocalCache.LocalLoadingCache;
40 import com.google.common.cache.LocalCache.LocalManualCache;
41 import com.google.common.cache.LocalCache.ReferenceEntry;
42 import com.google.common.cache.LocalCache.Segment;
43 import com.google.common.cache.LocalCache.Strength;
44 import com.google.common.cache.LocalCache.ValueReference;
45 import com.google.common.cache.TestingCacheLoaders.CountingLoader;
46 import com.google.common.cache.TestingRemovalListeners.CountingRemovalListener;
47 import com.google.common.cache.TestingRemovalListeners.QueuingRemovalListener;
48 import com.google.common.collect.ImmutableList;
49 import com.google.common.collect.ImmutableMap;
50 import com.google.common.collect.Lists;
51 import com.google.common.collect.Maps;
52 import com.google.common.testing.FakeTicker;
53 import com.google.common.testing.NullPointerTester;
54 import com.google.common.testing.SerializableTester;
55 import com.google.common.testing.TestLogHandler;
56 
57 import junit.framework.TestCase;
58 
59 import java.io.Serializable;
60 import java.lang.ref.Reference;
61 import java.lang.ref.ReferenceQueue;
62 import java.util.Iterator;
63 import java.util.LinkedHashMap;
64 import java.util.List;
65 import java.util.Map;
66 import java.util.Random;
67 import java.util.concurrent.CountDownLatch;
68 import java.util.concurrent.ExecutionException;
69 import java.util.concurrent.TimeUnit;
70 import java.util.concurrent.atomic.AtomicReferenceArray;
71 import java.util.logging.LogRecord;
72 
73 /**
74  * @author Charles Fry
75  */
76 public class LocalCacheTest extends TestCase {
77 
78   static final int SMALL_MAX_SIZE = DRAIN_THRESHOLD * 5;
79 
80   TestLogHandler logHandler;
81 
82   @Override
setUp()83   public void setUp() throws Exception {
84     super.setUp();
85     logHandler = new TestLogHandler();
86     LocalCache.logger.addHandler(logHandler);
87   }
88 
89   @Override
tearDown()90   public void tearDown() throws Exception {
91     super.tearDown();
92     LocalCache.logger.removeHandler(logHandler);
93   }
94 
popLoggedThrowable()95   private Throwable popLoggedThrowable() {
96     List<LogRecord> logRecords = logHandler.getStoredLogRecords();
97     assertSame(1, logRecords.size());
98     LogRecord logRecord = logRecords.get(0);
99     logHandler.clear();
100     return logRecord.getThrown();
101   }
102 
checkNothingLogged()103   private void checkNothingLogged() {
104     assertTrue(logHandler.getStoredLogRecords().isEmpty());
105   }
106 
checkLogged(Throwable t)107   private void checkLogged(Throwable t) {
108     assertSame(t, popLoggedThrowable());
109   }
110 
makeLocalCache(CacheBuilder<K, V> builder)111   private static <K, V> LocalCache<K, V> makeLocalCache(CacheBuilder<K, V> builder) {
112     return new LocalCache<K, V>(builder, null);
113   }
114 
createCacheBuilder()115   private static CacheBuilder<Object, Object> createCacheBuilder() {
116     return new CacheBuilder<Object, Object>();
117   }
118 
119   // constructor tests
120 
testDefaults()121   public void testDefaults() {
122     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder());
123 
124     assertSame(Strength.STRONG, map.keyStrength);
125     assertSame(Strength.STRONG, map.valueStrength);
126     assertSame(map.keyStrength.defaultEquivalence(), map.keyEquivalence);
127     assertSame(map.valueStrength.defaultEquivalence(), map.valueEquivalence);
128 
129     assertEquals(0, map.expireAfterAccessNanos);
130     assertEquals(0, map.expireAfterWriteNanos);
131     assertEquals(0, map.refreshNanos);
132     assertEquals(CacheBuilder.UNSET_INT, map.maxWeight);
133 
134     assertSame(EntryFactory.STRONG, map.entryFactory);
135     assertSame(CacheBuilder.NullListener.INSTANCE, map.removalListener);
136     assertSame(DISCARDING_QUEUE, map.removalNotificationQueue);
137     assertSame(NULL_TICKER, map.ticker);
138 
139     assertEquals(4, map.concurrencyLevel);
140 
141     // concurrency level
142     assertEquals(4, map.segments.length);
143     // initial capacity / concurrency level
144     assertEquals(16 / map.segments.length, map.segments[0].table.length());
145 
146     assertFalse(map.evictsBySize());
147     assertFalse(map.expires());
148     assertFalse(map.expiresAfterWrite());
149     assertFalse(map.expiresAfterAccess());
150     assertFalse(map.refreshes());
151   }
152 
testSetKeyEquivalence()153   public void testSetKeyEquivalence() {
154     Equivalence<Object> testEquivalence = new Equivalence<Object>() {
155       @Override
156       protected boolean doEquivalent(Object a, Object b) {
157         return false;
158       }
159 
160       @Override
161       protected int doHash(Object t) {
162         return 0;
163       }
164     };
165 
166     LocalCache<Object, Object> map =
167         makeLocalCache(createCacheBuilder().keyEquivalence(testEquivalence));
168     assertSame(testEquivalence, map.keyEquivalence);
169     assertSame(map.valueStrength.defaultEquivalence(), map.valueEquivalence);
170   }
171 
testSetValueEquivalence()172   public void testSetValueEquivalence() {
173     Equivalence<Object> testEquivalence = new Equivalence<Object>() {
174       @Override
175       protected boolean doEquivalent(Object a, Object b) {
176         return false;
177       }
178 
179       @Override
180       protected int doHash(Object t) {
181         return 0;
182       }
183     };
184 
185     LocalCache<Object, Object> map =
186         makeLocalCache(createCacheBuilder().valueEquivalence(testEquivalence));
187     assertSame(testEquivalence, map.valueEquivalence);
188     assertSame(map.keyStrength.defaultEquivalence(), map.keyEquivalence);
189   }
190 
testSetConcurrencyLevel()191   public void testSetConcurrencyLevel() {
192     // round up to nearest power of two
193 
194     checkConcurrencyLevel(1, 1);
195     checkConcurrencyLevel(2, 2);
196     checkConcurrencyLevel(3, 4);
197     checkConcurrencyLevel(4, 4);
198     checkConcurrencyLevel(5, 8);
199     checkConcurrencyLevel(6, 8);
200     checkConcurrencyLevel(7, 8);
201     checkConcurrencyLevel(8, 8);
202   }
203 
checkConcurrencyLevel(int concurrencyLevel, int segmentCount)204   private static void checkConcurrencyLevel(int concurrencyLevel, int segmentCount) {
205     LocalCache<Object, Object> map =
206         makeLocalCache(createCacheBuilder().concurrencyLevel(concurrencyLevel));
207     assertEquals(segmentCount, map.segments.length);
208   }
209 
testSetInitialCapacity()210   public void testSetInitialCapacity() {
211     // share capacity over each segment, then round up to nearest power of two
212 
213     checkInitialCapacity(1, 0, 1);
214     checkInitialCapacity(1, 1, 1);
215     checkInitialCapacity(1, 2, 2);
216     checkInitialCapacity(1, 3, 4);
217     checkInitialCapacity(1, 4, 4);
218     checkInitialCapacity(1, 5, 8);
219     checkInitialCapacity(1, 6, 8);
220     checkInitialCapacity(1, 7, 8);
221     checkInitialCapacity(1, 8, 8);
222 
223     checkInitialCapacity(2, 0, 1);
224     checkInitialCapacity(2, 1, 1);
225     checkInitialCapacity(2, 2, 1);
226     checkInitialCapacity(2, 3, 2);
227     checkInitialCapacity(2, 4, 2);
228     checkInitialCapacity(2, 5, 4);
229     checkInitialCapacity(2, 6, 4);
230     checkInitialCapacity(2, 7, 4);
231     checkInitialCapacity(2, 8, 4);
232 
233     checkInitialCapacity(4, 0, 1);
234     checkInitialCapacity(4, 1, 1);
235     checkInitialCapacity(4, 2, 1);
236     checkInitialCapacity(4, 3, 1);
237     checkInitialCapacity(4, 4, 1);
238     checkInitialCapacity(4, 5, 2);
239     checkInitialCapacity(4, 6, 2);
240     checkInitialCapacity(4, 7, 2);
241     checkInitialCapacity(4, 8, 2);
242   }
243 
checkInitialCapacity( int concurrencyLevel, int initialCapacity, int segmentSize)244   private static void checkInitialCapacity(
245       int concurrencyLevel, int initialCapacity, int segmentSize) {
246     LocalCache<Object, Object> map = makeLocalCache(
247         createCacheBuilder().concurrencyLevel(concurrencyLevel).initialCapacity(initialCapacity));
248     for (int i = 0; i < map.segments.length; i++) {
249       assertEquals(segmentSize, map.segments[i].table.length());
250     }
251   }
252 
testSetMaximumSize()253   public void testSetMaximumSize() {
254     // vary maximumSize wrt concurrencyLevel
255 
256     for (int maxSize = 1; maxSize < 8; maxSize++) {
257       checkMaximumSize(1, 8, maxSize);
258       checkMaximumSize(2, 8, maxSize);
259       checkMaximumSize(4, 8, maxSize);
260       checkMaximumSize(8, 8, maxSize);
261     }
262 
263     checkMaximumSize(1, 8, Long.MAX_VALUE);
264     checkMaximumSize(2, 8, Long.MAX_VALUE);
265     checkMaximumSize(4, 8, Long.MAX_VALUE);
266     checkMaximumSize(8, 8, Long.MAX_VALUE);
267 
268     // vary initial capacity wrt maximumSize
269 
270     for (int capacity = 0; capacity < 8; capacity++) {
271       checkMaximumSize(1, capacity, 4);
272       checkMaximumSize(2, capacity, 4);
273       checkMaximumSize(4, capacity, 4);
274       checkMaximumSize(8, capacity, 4);
275     }
276   }
277 
checkMaximumSize(int concurrencyLevel, int initialCapacity, long maxSize)278   private static void checkMaximumSize(int concurrencyLevel, int initialCapacity, long maxSize) {
279     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
280         .concurrencyLevel(concurrencyLevel)
281         .initialCapacity(initialCapacity)
282         .maximumSize(maxSize));
283     long totalCapacity = 0;
284     for (int i = 0; i < map.segments.length; i++) {
285       totalCapacity += map.segments[i].maxSegmentWeight;
286     }
287     assertTrue("totalCapacity=" + totalCapacity + ", maxSize=" + maxSize, totalCapacity == maxSize);
288 
289     map = makeLocalCache(createCacheBuilder()
290         .concurrencyLevel(concurrencyLevel)
291         .initialCapacity(initialCapacity)
292         .maximumWeight(maxSize)
293         .weigher(constantWeigher(1)));
294     totalCapacity = 0;
295     for (int i = 0; i < map.segments.length; i++) {
296       totalCapacity += map.segments[i].maxSegmentWeight;
297     }
298     assertTrue("totalCapacity=" + totalCapacity + ", maxSize=" + maxSize, totalCapacity == maxSize);
299   }
300 
testSetWeigher()301   public void testSetWeigher() {
302     Weigher<Object, Object> testWeigher = new Weigher<Object, Object>() {
303       @Override
304       public int weigh(Object key, Object value) {
305         return 42;
306       }
307     };
308     LocalCache<Object, Object> map =
309         makeLocalCache(createCacheBuilder().maximumWeight(1).weigher(testWeigher));
310     assertSame(testWeigher, map.weigher);
311   }
312 
testSetWeakKeys()313   public void testSetWeakKeys() {
314     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder().weakKeys());
315     checkStrength(map, Strength.WEAK, Strength.STRONG);
316     assertSame(EntryFactory.WEAK, map.entryFactory);
317   }
318 
testSetWeakValues()319   public void testSetWeakValues() {
320     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder().weakValues());
321     checkStrength(map, Strength.STRONG, Strength.WEAK);
322     assertSame(EntryFactory.STRONG, map.entryFactory);
323   }
324 
testSetSoftValues()325   public void testSetSoftValues() {
326     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder().softValues());
327     checkStrength(map, Strength.STRONG, Strength.SOFT);
328     assertSame(EntryFactory.STRONG, map.entryFactory);
329   }
330 
checkStrength( LocalCache<Object, Object> map, Strength keyStrength, Strength valueStrength)331   private static void checkStrength(
332       LocalCache<Object, Object> map, Strength keyStrength, Strength valueStrength) {
333     assertSame(keyStrength, map.keyStrength);
334     assertSame(valueStrength, map.valueStrength);
335     assertSame(keyStrength.defaultEquivalence(), map.keyEquivalence);
336     assertSame(valueStrength.defaultEquivalence(), map.valueEquivalence);
337   }
338 
testSetExpireAfterWrite()339   public void testSetExpireAfterWrite() {
340     long duration = 42;
341     TimeUnit unit = TimeUnit.SECONDS;
342     LocalCache<Object, Object> map =
343         makeLocalCache(createCacheBuilder().expireAfterWrite(duration, unit));
344     assertEquals(unit.toNanos(duration), map.expireAfterWriteNanos);
345   }
346 
testSetExpireAfterAccess()347   public void testSetExpireAfterAccess() {
348     long duration = 42;
349     TimeUnit unit = TimeUnit.SECONDS;
350     LocalCache<Object, Object> map =
351         makeLocalCache(createCacheBuilder().expireAfterAccess(duration, unit));
352     assertEquals(unit.toNanos(duration), map.expireAfterAccessNanos);
353   }
354 
testSetRefresh()355   public void testSetRefresh() {
356     long duration = 42;
357     TimeUnit unit = TimeUnit.SECONDS;
358     LocalCache<Object, Object> map =
359         makeLocalCache(createCacheBuilder().refreshAfterWrite(duration, unit));
360     assertEquals(unit.toNanos(duration), map.refreshNanos);
361   }
362 
testSetRemovalListener()363   public void testSetRemovalListener() {
364     RemovalListener<Object, Object> testListener = TestingRemovalListeners.nullRemovalListener();
365     LocalCache<Object, Object> map =
366         makeLocalCache(createCacheBuilder().removalListener(testListener));
367     assertSame(testListener, map.removalListener);
368   }
369 
testSetTicker()370   public void testSetTicker() {
371     Ticker testTicker = new Ticker() {
372       @Override
373       public long read() {
374         return 0;
375       }
376     };
377     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder().ticker(testTicker));
378     assertSame(testTicker, map.ticker);
379   }
380 
testEntryFactory()381   public void testEntryFactory() {
382     assertSame(EntryFactory.STRONG,
383         EntryFactory.getFactory(Strength.STRONG, false, false));
384     assertSame(EntryFactory.STRONG_ACCESS,
385         EntryFactory.getFactory(Strength.STRONG, true, false));
386     assertSame(EntryFactory.STRONG_WRITE,
387         EntryFactory.getFactory(Strength.STRONG, false, true));
388     assertSame(EntryFactory.STRONG_ACCESS_WRITE,
389         EntryFactory.getFactory(Strength.STRONG, true, true));
390     assertSame(EntryFactory.WEAK,
391         EntryFactory.getFactory(Strength.WEAK, false, false));
392     assertSame(EntryFactory.WEAK_ACCESS,
393         EntryFactory.getFactory(Strength.WEAK, true, false));
394     assertSame(EntryFactory.WEAK_WRITE,
395         EntryFactory.getFactory(Strength.WEAK, false, true));
396     assertSame(EntryFactory.WEAK_ACCESS_WRITE,
397         EntryFactory.getFactory(Strength.WEAK, true, true));
398   }
399 
400   // computation tests
401 
testCompute()402   public void testCompute() throws ExecutionException {
403     CountingLoader loader = new CountingLoader();
404     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder());
405     assertEquals(0, loader.getCount());
406 
407     Object key = new Object();
408     Object value = map.get(key, loader);
409     assertEquals(1, loader.getCount());
410     assertEquals(value, map.get(key, loader));
411     assertEquals(1, loader.getCount());
412   }
413 
testRecordReadOnCompute()414   public void testRecordReadOnCompute() throws ExecutionException {
415     CountingLoader loader = new CountingLoader();
416     for (CacheBuilder<Object, Object> builder : allEvictingMakers()) {
417       LocalCache<Object, Object> map =
418           makeLocalCache(builder.concurrencyLevel(1));
419       Segment<Object, Object> segment = map.segments[0];
420       List<ReferenceEntry<Object, Object>> writeOrder = Lists.newLinkedList();
421       List<ReferenceEntry<Object, Object>> readOrder = Lists.newLinkedList();
422       for (int i = 0; i < SMALL_MAX_SIZE; i++) {
423         Object key = new Object();
424         int hash = map.hash(key);
425 
426         map.get(key, loader);
427         ReferenceEntry<Object, Object> entry = segment.getEntry(key, hash);
428         writeOrder.add(entry);
429         readOrder.add(entry);
430       }
431 
432       checkEvictionQueues(map, segment, readOrder, writeOrder);
433       checkExpirationTimes(map);
434       assertTrue(segment.recencyQueue.isEmpty());
435 
436       // access some of the elements
437       Random random = new Random();
438       List<ReferenceEntry<Object, Object>> reads = Lists.newArrayList();
439       Iterator<ReferenceEntry<Object, Object>> i = readOrder.iterator();
440       while (i.hasNext()) {
441         ReferenceEntry<Object, Object> entry = i.next();
442         if (random.nextBoolean()) {
443           map.get(entry.getKey(), loader);
444           reads.add(entry);
445           i.remove();
446           assertTrue(segment.recencyQueue.size() <= DRAIN_THRESHOLD);
447         }
448       }
449       int undrainedIndex = reads.size() - segment.recencyQueue.size();
450       checkAndDrainRecencyQueue(map, segment, reads.subList(undrainedIndex, reads.size()));
451       readOrder.addAll(reads);
452 
453       checkEvictionQueues(map, segment, readOrder, writeOrder);
454       checkExpirationTimes(map);
455     }
456   }
457 
testComputeExistingEntry()458   public void testComputeExistingEntry() throws ExecutionException {
459     CountingLoader loader = new CountingLoader();
460     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder());
461     assertEquals(0, loader.getCount());
462 
463     Object key = new Object();
464     Object value = new Object();
465     map.put(key, value);
466 
467     assertEquals(value, map.get(key, loader));
468     assertEquals(0, loader.getCount());
469   }
470 
testComputePartiallyCollectedKey()471   public void testComputePartiallyCollectedKey() throws ExecutionException {
472     CacheBuilder<Object, Object> builder = createCacheBuilder().concurrencyLevel(1);
473     CountingLoader loader = new CountingLoader();
474     LocalCache<Object, Object> map = makeLocalCache(builder);
475     Segment<Object, Object> segment = map.segments[0];
476     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
477     assertEquals(0, loader.getCount());
478 
479     Object key = new Object();
480     int hash = map.hash(key);
481     Object value = new Object();
482     int index = hash & (table.length() - 1);
483 
484     DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null);
485     DummyValueReference<Object, Object> valueRef = DummyValueReference.create(value, entry);
486     entry.setValueReference(valueRef);
487     table.set(index, entry);
488     segment.count++;
489 
490     assertSame(value, map.get(key, loader));
491     assertEquals(0, loader.getCount());
492     assertEquals(1, segment.count);
493 
494     entry.clearKey();
495     assertNotSame(value, map.get(key, loader));
496     assertEquals(1, loader.getCount());
497     assertEquals(2, segment.count);
498   }
499 
testComputePartiallyCollectedValue()500   public void testComputePartiallyCollectedValue() throws ExecutionException {
501     CacheBuilder<Object, Object> builder = createCacheBuilder().concurrencyLevel(1);
502     CountingLoader loader = new CountingLoader();
503     LocalCache<Object, Object> map = makeLocalCache(builder);
504     Segment<Object, Object> segment = map.segments[0];
505     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
506     assertEquals(0, loader.getCount());
507 
508     Object key = new Object();
509     int hash = map.hash(key);
510     Object value = new Object();
511     int index = hash & (table.length() - 1);
512 
513     DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null);
514     DummyValueReference<Object, Object> valueRef = DummyValueReference.create(value, entry);
515     entry.setValueReference(valueRef);
516     table.set(index, entry);
517     segment.count++;
518 
519     assertSame(value, map.get(key, loader));
520     assertEquals(0, loader.getCount());
521     assertEquals(1, segment.count);
522 
523     valueRef.clear();
524     assertNotSame(value, map.get(key, loader));
525     assertEquals(1, loader.getCount());
526     assertEquals(1, segment.count);
527   }
528 
testComputeExpiredEntry()529   public void testComputeExpiredEntry() throws ExecutionException {
530     CacheBuilder<Object, Object> builder = createCacheBuilder()
531         .expireAfterWrite(1, TimeUnit.NANOSECONDS);
532     CountingLoader loader = new CountingLoader();
533     LocalCache<Object, Object> map = makeLocalCache(builder);
534     assertEquals(0, loader.getCount());
535 
536     Object key = new Object();
537     Object one = map.get(key, loader);
538     assertEquals(1, loader.getCount());
539 
540     Object two = map.get(key, loader);
541     assertNotSame(one, two);
542     assertEquals(2, loader.getCount());
543   }
544 
testCopyEntry_computing()545   public void testCopyEntry_computing() {
546     final CountDownLatch startSignal = new CountDownLatch(1);
547     final CountDownLatch computingSignal = new CountDownLatch(1);
548     final CountDownLatch doneSignal = new CountDownLatch(2);
549     final Object computedObject = new Object();
550 
551     final CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
552       @Override
553       public Object load(Object key) throws Exception {
554         computingSignal.countDown();
555         startSignal.await();
556         return computedObject;
557       }
558     };
559 
560     QueuingRemovalListener<Object, Object> listener = queuingRemovalListener();
561     CacheBuilder<Object, Object> builder = createCacheBuilder()
562         .concurrencyLevel(1)
563         .removalListener(listener);
564     final LocalCache<Object, Object> map = makeLocalCache(builder);
565     Segment<Object, Object> segment = map.segments[0];
566     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
567     assertTrue(listener.isEmpty());
568 
569     final Object one = new Object();
570     int hash = map.hash(one);
571     int index = hash & (table.length() - 1);
572 
573     new Thread() {
574       @Override
575       public void run() {
576         try {
577           map.get(one, loader);
578         } catch (ExecutionException e) {
579           throw new RuntimeException(e);
580         }
581         doneSignal.countDown();
582       }
583     }.start();
584 
585     try {
586       computingSignal.await();
587     } catch (InterruptedException e) {
588       throw new RuntimeException(e);
589     }
590 
591     new Thread() {
592       @Override
593       public void run() {
594         try {
595           map.get(one, loader);
596         } catch (ExecutionException e) {
597           throw new RuntimeException(e);
598         }
599         doneSignal.countDown();
600       }
601     }.start();
602 
603     ReferenceEntry<Object, Object> entry = segment.getEntry(one, hash);
604     ReferenceEntry<Object, Object> newEntry = segment.copyEntry(entry, null);
605     table.set(index, newEntry);
606 
607     @SuppressWarnings("unchecked")
608     LoadingValueReference<Object, Object> valueReference =
609         (LoadingValueReference) newEntry.getValueReference();
610     assertFalse(valueReference.futureValue.isDone());
611     startSignal.countDown();
612 
613     try {
614       doneSignal.await();
615     } catch (InterruptedException e) {
616       throw new RuntimeException(e);
617     }
618 
619     map.cleanUp(); // force notifications
620     assertTrue(listener.isEmpty());
621     assertTrue(map.containsKey(one));
622     assertEquals(1, map.size());
623     assertSame(computedObject, map.get(one));
624   }
625 
testRemovalListenerCheckedException()626   public void testRemovalListenerCheckedException() {
627     final RuntimeException e = new RuntimeException();
628     RemovalListener<Object, Object> listener = new RemovalListener<Object, Object>() {
629       @Override
630       public void onRemoval(RemovalNotification<Object, Object> notification) {
631         throw e;
632       }
633     };
634 
635     CacheBuilder<Object, Object> builder = createCacheBuilder().removalListener(listener);
636     final LocalCache<Object, Object> cache = makeLocalCache(builder);
637     Object key = new Object();
638     cache.put(key, new Object());
639     checkNothingLogged();
640 
641     cache.remove(key);
642     checkLogged(e);
643   }
644 
testRemovalListener_replaced_computing()645   public void testRemovalListener_replaced_computing() {
646     final CountDownLatch startSignal = new CountDownLatch(1);
647     final CountDownLatch computingSignal = new CountDownLatch(1);
648     final CountDownLatch doneSignal = new CountDownLatch(1);
649     final Object computedObject = new Object();
650 
651     final CacheLoader<Object, Object> loader = new CacheLoader<Object, Object>() {
652       @Override
653       public Object load(Object key) throws Exception {
654         computingSignal.countDown();
655         startSignal.await();
656         return computedObject;
657       }
658     };
659 
660     QueuingRemovalListener<Object, Object> listener = queuingRemovalListener();
661     CacheBuilder<Object, Object> builder = createCacheBuilder().removalListener(listener);
662     final LocalCache<Object, Object> map = makeLocalCache(builder);
663     assertTrue(listener.isEmpty());
664 
665     final Object one = new Object();
666     final Object two = new Object();
667 
668     new Thread() {
669       @Override
670       public void run() {
671         try {
672           map.get(one, loader);
673         } catch (ExecutionException e) {
674           throw new RuntimeException(e);
675         }
676         doneSignal.countDown();
677       }
678     }.start();
679 
680     try {
681       computingSignal.await();
682     } catch (InterruptedException e) {
683       throw new RuntimeException(e);
684     }
685 
686     map.put(one, two);
687     assertSame(two, map.get(one));
688     startSignal.countDown();
689 
690     try {
691       doneSignal.await();
692     } catch (InterruptedException e) {
693       throw new RuntimeException(e);
694     }
695 
696     map.cleanUp(); // force notifications
697     assertNotified(listener, one, computedObject, RemovalCause.REPLACED);
698     assertTrue(listener.isEmpty());
699   }
700 
testSegmentRefresh_duplicate()701   public void testSegmentRefresh_duplicate() throws ExecutionException {
702     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
703         .concurrencyLevel(1));
704     Segment<Object, Object> segment = map.segments[0];
705 
706     Object key = new Object();
707     int hash = map.hash(key);
708     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
709     int index = hash & (table.length() - 1);
710 
711     // already loading
712     DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null);
713     DummyValueReference<Object, Object> valueRef = DummyValueReference.create(null, entry);
714     valueRef.setLoading(true);
715     entry.setValueReference(valueRef);
716     table.set(index, entry);
717     assertNull(segment.refresh(key, hash, identityLoader()));
718   }
719 
720   // Removal listener tests
721 
testRemovalListener_explicit()722   public void testRemovalListener_explicit() {
723     QueuingRemovalListener<Object, Object> listener = queuingRemovalListener();
724     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
725         .removalListener(listener));
726     assertTrue(listener.isEmpty());
727 
728     Object one = new Object();
729     Object two = new Object();
730     Object three = new Object();
731     Object four = new Object();
732     Object five = new Object();
733     Object six = new Object();
734 
735     map.put(one, two);
736     map.remove(one);
737     assertNotified(listener, one, two, RemovalCause.EXPLICIT);
738 
739     map.put(two, three);
740     map.remove(two, three);
741     assertNotified(listener, two, three, RemovalCause.EXPLICIT);
742 
743     map.put(three, four);
744     Iterator<?> i = map.entrySet().iterator();
745     i.next();
746     i.remove();
747     assertNotified(listener, three, four, RemovalCause.EXPLICIT);
748 
749     map.put(four, five);
750     i = map.keySet().iterator();
751     i.next();
752     i.remove();
753     assertNotified(listener, four, five, RemovalCause.EXPLICIT);
754 
755     map.put(five, six);
756     i = map.values().iterator();
757     i.next();
758     i.remove();
759     assertNotified(listener, five, six, RemovalCause.EXPLICIT);
760 
761     assertTrue(listener.isEmpty());
762   }
763 
testRemovalListener_replaced()764   public void testRemovalListener_replaced() {
765     QueuingRemovalListener<Object, Object> listener = queuingRemovalListener();
766     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
767         .removalListener(listener));
768     assertTrue(listener.isEmpty());
769 
770     Object one = new Object();
771     Object two = new Object();
772     Object three = new Object();
773     Object four = new Object();
774     Object five = new Object();
775     Object six = new Object();
776 
777     map.put(one, two);
778     map.put(one, three);
779     assertNotified(listener, one, two, RemovalCause.REPLACED);
780 
781     Map<Object, Object> newMap = ImmutableMap.of(one, four);
782     map.putAll(newMap);
783     assertNotified(listener, one, three, RemovalCause.REPLACED);
784 
785     map.replace(one, five);
786     assertNotified(listener, one, four, RemovalCause.REPLACED);
787 
788     map.replace(one, five, six);
789     assertNotified(listener, one, five, RemovalCause.REPLACED);
790   }
791 
testRemovalListener_collected()792   public void testRemovalListener_collected() {
793     QueuingRemovalListener<Object, Object> listener = queuingRemovalListener();
794     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
795         .concurrencyLevel(1)
796         .softValues()
797         .removalListener(listener));
798     Segment<Object, Object> segment = map.segments[0];
799     assertTrue(listener.isEmpty());
800 
801     Object one = new Object();
802     Object two = new Object();
803     Object three = new Object();
804 
805     map.put(one, two);
806     map.put(two, three);
807     assertTrue(listener.isEmpty());
808 
809     int hash = map.hash(one);
810     ReferenceEntry<Object, Object> entry = segment.getEntry(one, hash);
811     map.reclaimValue(entry.getValueReference());
812     assertNotified(listener, one, two, RemovalCause.COLLECTED);
813 
814     assertTrue(listener.isEmpty());
815   }
816 
testRemovalListener_expired()817   public void testRemovalListener_expired() {
818     FakeTicker ticker = new FakeTicker();
819     QueuingRemovalListener<Object, Object> listener = queuingRemovalListener();
820     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
821         .concurrencyLevel(1)
822         .expireAfterWrite(2, TimeUnit.NANOSECONDS)
823         .ticker(ticker)
824         .removalListener(listener));
825     assertTrue(listener.isEmpty());
826 
827     Object one = new Object();
828     Object two = new Object();
829     Object three = new Object();
830     Object four = new Object();
831     Object five = new Object();
832 
833     map.put(one, two);
834     ticker.advance(1);
835     map.put(two, three);
836     ticker.advance(1);
837     map.put(three, four);
838     assertTrue(listener.isEmpty());
839     ticker.advance(1);
840     map.put(four, five);
841     assertNotified(listener, one, two, RemovalCause.EXPIRED);
842 
843     assertTrue(listener.isEmpty());
844   }
845 
testRemovalListener_size()846   public void testRemovalListener_size() {
847     QueuingRemovalListener<Object, Object> listener = queuingRemovalListener();
848     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
849         .concurrencyLevel(1)
850         .maximumSize(2)
851         .removalListener(listener));
852     assertTrue(listener.isEmpty());
853 
854     Object one = new Object();
855     Object two = new Object();
856     Object three = new Object();
857     Object four = new Object();
858 
859     map.put(one, two);
860     map.put(two, three);
861     assertTrue(listener.isEmpty());
862     map.put(three, four);
863     assertNotified(listener, one, two, RemovalCause.SIZE);
864 
865     assertTrue(listener.isEmpty());
866   }
867 
assertNotified( QueuingRemovalListener<K, V> listener, K key, V value, RemovalCause cause)868   static <K, V> void assertNotified(
869       QueuingRemovalListener<K, V> listener, K key, V value, RemovalCause cause) {
870     RemovalNotification<K, V> notification = listener.remove();
871     assertSame(key, notification.getKey());
872     assertSame(value, notification.getValue());
873     assertSame(cause, notification.getCause());
874   }
875 
876   // Segment core tests
877 
testNewEntry()878   public void testNewEntry() {
879     for (CacheBuilder<Object, Object> builder : allEntryTypeMakers()) {
880       LocalCache<Object, Object> map = makeLocalCache(builder);
881 
882       Object keyOne = new Object();
883       Object valueOne = new Object();
884       int hashOne = map.hash(keyOne);
885       ReferenceEntry<Object, Object> entryOne = map.newEntry(keyOne, hashOne, null);
886       ValueReference<Object, Object> valueRefOne = map.newValueReference(entryOne, valueOne, 1);
887       assertSame(valueOne, valueRefOne.get());
888       entryOne.setValueReference(valueRefOne);
889 
890       assertSame(keyOne, entryOne.getKey());
891       assertEquals(hashOne, entryOne.getHash());
892       assertNull(entryOne.getNext());
893       assertSame(valueRefOne, entryOne.getValueReference());
894 
895       Object keyTwo = new Object();
896       Object valueTwo = new Object();
897       int hashTwo = map.hash(keyTwo);
898       ReferenceEntry<Object, Object> entryTwo = map.newEntry(keyTwo, hashTwo, entryOne);
899       ValueReference<Object, Object> valueRefTwo = map.newValueReference(entryTwo, valueTwo, 1);
900       assertSame(valueTwo, valueRefTwo.get());
901       entryTwo.setValueReference(valueRefTwo);
902 
903       assertSame(keyTwo, entryTwo.getKey());
904       assertEquals(hashTwo, entryTwo.getHash());
905       assertSame(entryOne, entryTwo.getNext());
906       assertSame(valueRefTwo, entryTwo.getValueReference());
907     }
908   }
909 
testCopyEntry()910   public void testCopyEntry() {
911     for (CacheBuilder<Object, Object> builder : allEntryTypeMakers()) {
912       LocalCache<Object, Object> map = makeLocalCache(builder);
913 
914       Object keyOne = new Object();
915       Object valueOne = new Object();
916       int hashOne = map.hash(keyOne);
917       ReferenceEntry<Object, Object> entryOne = map.newEntry(keyOne, hashOne, null);
918       entryOne.setValueReference(map.newValueReference(entryOne, valueOne, 1));
919 
920       Object keyTwo = new Object();
921       Object valueTwo = new Object();
922       int hashTwo = map.hash(keyTwo);
923       ReferenceEntry<Object, Object> entryTwo = map.newEntry(keyTwo, hashTwo, entryOne);
924       entryTwo.setValueReference(map.newValueReference(entryTwo, valueTwo, 1));
925       if (map.usesAccessQueue()) {
926         LocalCache.connectAccessOrder(entryOne, entryTwo);
927       }
928       if (map.usesWriteQueue()) {
929         LocalCache.connectWriteOrder(entryOne, entryTwo);
930       }
931       assertConnected(map, entryOne, entryTwo);
932 
933       ReferenceEntry<Object, Object> copyOne = map.copyEntry(entryOne, null);
934       assertSame(keyOne, entryOne.getKey());
935       assertEquals(hashOne, entryOne.getHash());
936       assertNull(entryOne.getNext());
937       assertSame(valueOne, copyOne.getValueReference().get());
938       assertConnected(map, copyOne, entryTwo);
939 
940       ReferenceEntry<Object, Object> copyTwo = map.copyEntry(entryTwo, copyOne);
941       assertSame(keyTwo, copyTwo.getKey());
942       assertEquals(hashTwo, copyTwo.getHash());
943       assertSame(copyOne, copyTwo.getNext());
944       assertSame(valueTwo, copyTwo.getValueReference().get());
945       assertConnected(map, copyOne, copyTwo);
946     }
947   }
948 
assertConnected( LocalCache<K, V> map, ReferenceEntry<K, V> one, ReferenceEntry<K, V> two)949   private static <K, V> void assertConnected(
950       LocalCache<K, V> map, ReferenceEntry<K, V> one, ReferenceEntry<K, V> two) {
951     if (map.usesWriteQueue()) {
952       assertSame(two, one.getNextInWriteQueue());
953     }
954     if (map.usesAccessQueue()) {
955       assertSame(two, one.getNextInAccessQueue());
956     }
957   }
958 
testSegmentGetAndContains()959   public void testSegmentGetAndContains() {
960     FakeTicker ticker = new FakeTicker();
961     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
962         .concurrencyLevel(1)
963         .ticker(ticker)
964         .expireAfterAccess(1, TimeUnit.NANOSECONDS));
965     Segment<Object, Object> segment = map.segments[0];
966     // TODO(fry): check recency ordering
967 
968     Object key = new Object();
969     int hash = map.hash(key);
970     Object value = new Object();
971     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
972     int index = hash & (table.length() - 1);
973 
974     ReferenceEntry<Object, Object> entry = map.newEntry(key, hash, null);
975     ValueReference<Object, Object> valueRef = map.newValueReference(entry, value, 1);
976     entry.setValueReference(valueRef);
977 
978     assertNull(segment.get(key, hash));
979 
980     // count == 0
981     table.set(index, entry);
982     assertNull(segment.get(key, hash));
983     assertFalse(segment.containsKey(key, hash));
984     assertFalse(segment.containsValue(value));
985 
986     // count == 1
987     segment.count++;
988     assertSame(value, segment.get(key, hash));
989     assertTrue(segment.containsKey(key, hash));
990     assertTrue(segment.containsValue(value));
991     // don't see absent values now that count > 0
992     assertNull(segment.get(new Object(), hash));
993 
994     // null key
995     DummyEntry<Object, Object> nullEntry = DummyEntry.create(null, hash, entry);
996     Object nullValue = new Object();
997     ValueReference<Object, Object> nullValueRef = map.newValueReference(nullEntry, nullValue, 1);
998     nullEntry.setValueReference(nullValueRef);
999     table.set(index, nullEntry);
1000     // skip the null key
1001     assertSame(value, segment.get(key, hash));
1002     assertTrue(segment.containsKey(key, hash));
1003     assertTrue(segment.containsValue(value));
1004     assertFalse(segment.containsValue(nullValue));
1005 
1006     // hash collision
1007     DummyEntry<Object, Object> dummy = DummyEntry.create(new Object(), hash, entry);
1008     Object dummyValue = new Object();
1009     ValueReference<Object, Object> dummyValueRef = map.newValueReference(dummy, dummyValue, 1);
1010     dummy.setValueReference(dummyValueRef);
1011     table.set(index, dummy);
1012     assertSame(value, segment.get(key, hash));
1013     assertTrue(segment.containsKey(key, hash));
1014     assertTrue(segment.containsValue(value));
1015     assertTrue(segment.containsValue(dummyValue));
1016 
1017     // key collision
1018     dummy = DummyEntry.create(key, hash, entry);
1019     dummyValue = new Object();
1020     dummyValueRef = map.newValueReference(dummy, dummyValue, 1);
1021     dummy.setValueReference(dummyValueRef);
1022     table.set(index, dummy);
1023     // returns the most recent entry
1024     assertSame(dummyValue, segment.get(key, hash));
1025     assertTrue(segment.containsKey(key, hash));
1026     assertTrue(segment.containsValue(value));
1027     assertTrue(segment.containsValue(dummyValue));
1028 
1029     // expired
1030     dummy.setAccessTime(ticker.read() - 2);
1031     assertNull(segment.get(key, hash));
1032     assertFalse(segment.containsKey(key, hash));
1033     assertTrue(segment.containsValue(value));
1034     assertFalse(segment.containsValue(dummyValue));
1035   }
1036 
testSegmentReplaceValue()1037   public void testSegmentReplaceValue() {
1038     LocalCache<Object, Object> map =
1039         makeLocalCache(createCacheBuilder().concurrencyLevel(1).expireAfterAccess(99999, SECONDS));
1040     Segment<Object, Object> segment = map.segments[0];
1041     // TODO(fry): check recency ordering
1042 
1043     Object key = new Object();
1044     int hash = map.hash(key);
1045     Object oldValue = new Object();
1046     Object newValue = new Object();
1047     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
1048     int index = hash & (table.length() - 1);
1049 
1050     DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null);
1051     DummyValueReference<Object, Object> oldValueRef = DummyValueReference.create(oldValue, entry);
1052     entry.setValueReference(oldValueRef);
1053 
1054     // no entry
1055     assertFalse(segment.replace(key, hash, oldValue, newValue));
1056     assertEquals(0, segment.count);
1057 
1058     // same value
1059     table.set(index, entry);
1060     segment.count++;
1061     assertEquals(1, segment.count);
1062     assertSame(oldValue, segment.get(key, hash));
1063     assertTrue(segment.replace(key, hash, oldValue, newValue));
1064     assertEquals(1, segment.count);
1065     assertSame(newValue, segment.get(key, hash));
1066 
1067     // different value
1068     assertFalse(segment.replace(key, hash, oldValue, newValue));
1069     assertEquals(1, segment.count);
1070     assertSame(newValue, segment.get(key, hash));
1071 
1072     // cleared
1073     entry.setValueReference(oldValueRef);
1074     assertSame(oldValue, segment.get(key, hash));
1075     oldValueRef.clear();
1076     assertFalse(segment.replace(key, hash, oldValue, newValue));
1077     assertEquals(0, segment.count);
1078     assertNull(segment.get(key, hash));
1079   }
1080 
testSegmentReplace()1081   public void testSegmentReplace() {
1082     LocalCache<Object, Object> map =
1083         makeLocalCache(createCacheBuilder().concurrencyLevel(1).expireAfterAccess(99999, SECONDS));
1084     Segment<Object, Object> segment = map.segments[0];
1085     // TODO(fry): check recency ordering
1086 
1087     Object key = new Object();
1088     int hash = map.hash(key);
1089     Object oldValue = new Object();
1090     Object newValue = new Object();
1091     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
1092     int index = hash & (table.length() - 1);
1093 
1094     DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null);
1095     DummyValueReference<Object, Object> oldValueRef = DummyValueReference.create(oldValue, entry);
1096     entry.setValueReference(oldValueRef);
1097 
1098     // no entry
1099     assertNull(segment.replace(key, hash, newValue));
1100     assertEquals(0, segment.count);
1101 
1102     // same key
1103     table.set(index, entry);
1104     segment.count++;
1105     assertEquals(1, segment.count);
1106     assertSame(oldValue, segment.get(key, hash));
1107     assertSame(oldValue, segment.replace(key, hash, newValue));
1108     assertEquals(1, segment.count);
1109     assertSame(newValue, segment.get(key, hash));
1110 
1111     // cleared
1112     entry.setValueReference(oldValueRef);
1113     assertSame(oldValue, segment.get(key, hash));
1114     oldValueRef.clear();
1115     assertNull(segment.replace(key, hash, newValue));
1116     assertEquals(0, segment.count);
1117     assertNull(segment.get(key, hash));
1118   }
1119 
testSegmentPut()1120   public void testSegmentPut() {
1121     LocalCache<Object, Object> map =
1122         makeLocalCache(createCacheBuilder().concurrencyLevel(1).expireAfterAccess(99999, SECONDS));
1123     Segment<Object, Object> segment = map.segments[0];
1124     // TODO(fry): check recency ordering
1125 
1126     Object key = new Object();
1127     int hash = map.hash(key);
1128     Object oldValue = new Object();
1129     Object newValue = new Object();
1130 
1131     // no entry
1132     assertEquals(0, segment.count);
1133     assertNull(segment.put(key, hash, oldValue, false));
1134     assertEquals(1, segment.count);
1135 
1136     // same key
1137     assertSame(oldValue, segment.put(key, hash, newValue, false));
1138     assertEquals(1, segment.count);
1139     assertSame(newValue, segment.get(key, hash));
1140 
1141     // cleared
1142     ReferenceEntry<Object, Object> entry = segment.getEntry(key, hash);
1143     DummyValueReference<Object, Object> oldValueRef = DummyValueReference.create(oldValue, entry);
1144     entry.setValueReference(oldValueRef);
1145     assertSame(oldValue, segment.get(key, hash));
1146     oldValueRef.clear();
1147     assertNull(segment.put(key, hash, newValue, false));
1148     assertEquals(1, segment.count);
1149     assertSame(newValue, segment.get(key, hash));
1150   }
1151 
testSegmentPutIfAbsent()1152   public void testSegmentPutIfAbsent() {
1153     LocalCache<Object, Object> map =
1154         makeLocalCache(createCacheBuilder().concurrencyLevel(1).expireAfterAccess(99999, SECONDS));
1155     Segment<Object, Object> segment = map.segments[0];
1156     // TODO(fry): check recency ordering
1157 
1158     Object key = new Object();
1159     int hash = map.hash(key);
1160     Object oldValue = new Object();
1161     Object newValue = new Object();
1162 
1163     // no entry
1164     assertEquals(0, segment.count);
1165     assertNull(segment.put(key, hash, oldValue, true));
1166     assertEquals(1, segment.count);
1167 
1168     // same key
1169     assertSame(oldValue, segment.put(key, hash, newValue, true));
1170     assertEquals(1, segment.count);
1171     assertSame(oldValue, segment.get(key, hash));
1172 
1173     // cleared
1174     ReferenceEntry<Object, Object> entry = segment.getEntry(key, hash);
1175     DummyValueReference<Object, Object> oldValueRef = DummyValueReference.create(oldValue, entry);
1176     entry.setValueReference(oldValueRef);
1177     assertSame(oldValue, segment.get(key, hash));
1178     oldValueRef.clear();
1179     assertNull(segment.put(key, hash, newValue, true));
1180     assertEquals(1, segment.count);
1181     assertSame(newValue, segment.get(key, hash));
1182   }
1183 
testSegmentPut_expand()1184   public void testSegmentPut_expand() {
1185     LocalCache<Object, Object> map =
1186         makeLocalCache(createCacheBuilder().concurrencyLevel(1).initialCapacity(1));
1187     Segment<Object, Object> segment = map.segments[0];
1188     assertEquals(1, segment.table.length());
1189 
1190     int count = 1024;
1191     for (int i = 0; i < count; i++) {
1192       Object key = new Object();
1193       Object value = new Object();
1194       int hash = map.hash(key);
1195       assertNull(segment.put(key, hash, value, false));
1196       assertTrue(segment.table.length() > i);
1197     }
1198   }
1199 
testSegmentPut_evict()1200   public void testSegmentPut_evict() {
1201     int maxSize = 10;
1202     LocalCache<Object, Object> map =
1203         makeLocalCache(createCacheBuilder().concurrencyLevel(1).maximumSize(maxSize));
1204 
1205     // manually add elements to avoid eviction
1206     int originalCount = 1024;
1207     LinkedHashMap<Object, Object> originalMap = Maps.newLinkedHashMap();
1208     for (int i = 0; i < originalCount; i++) {
1209       Object key = new Object();
1210       Object value = new Object();
1211       map.put(key, value);
1212       originalMap.put(key, value);
1213       if (i >= maxSize) {
1214         Iterator<Object> it = originalMap.keySet().iterator();
1215         it.next();
1216         it.remove();
1217       }
1218       assertEquals(originalMap, map);
1219     }
1220   }
1221 
testSegmentStoreComputedValue()1222   public void testSegmentStoreComputedValue() {
1223     QueuingRemovalListener<Object, Object> listener = queuingRemovalListener();
1224     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
1225         .concurrencyLevel(1)
1226         .removalListener(listener));
1227     Segment<Object, Object> segment = map.segments[0];
1228 
1229     Object key = new Object();
1230     int hash = map.hash(key);
1231     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
1232     int index = hash & (table.length() - 1);
1233 
1234     DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null);
1235     LoadingValueReference<Object, Object> valueRef = new LoadingValueReference<Object, Object>();
1236     entry.setValueReference(valueRef);
1237 
1238     // absent
1239     Object value = new Object();
1240     assertTrue(listener.isEmpty());
1241     assertEquals(0, segment.count);
1242     assertNull(segment.get(key, hash));
1243     assertTrue(segment.storeLoadedValue(key, hash, valueRef, value));
1244     assertSame(value, segment.get(key, hash));
1245     assertEquals(1, segment.count);
1246     assertTrue(listener.isEmpty());
1247 
1248     // clobbered
1249     Object value2 = new Object();
1250     assertFalse(segment.storeLoadedValue(key, hash, valueRef, value2));
1251     assertEquals(1, segment.count);
1252     assertSame(value, segment.get(key, hash));
1253     RemovalNotification<Object, Object> notification = listener.remove();
1254     assertEquals(immutableEntry(key, value2), notification);
1255     assertEquals(RemovalCause.REPLACED, notification.getCause());
1256     assertTrue(listener.isEmpty());
1257 
1258     // inactive
1259     Object value3 = new Object();
1260     map.clear();
1261     listener.clear();
1262     assertEquals(0, segment.count);
1263     table.set(index, entry);
1264     assertTrue(segment.storeLoadedValue(key, hash, valueRef, value3));
1265     assertSame(value3, segment.get(key, hash));
1266     assertEquals(1, segment.count);
1267     assertTrue(listener.isEmpty());
1268 
1269     // replaced
1270     Object value4 = new Object();
1271     DummyValueReference<Object, Object> value3Ref = DummyValueReference.create(value3, entry);
1272     valueRef = new LoadingValueReference<Object, Object>(value3Ref);
1273     entry.setValueReference(valueRef);
1274     table.set(index, entry);
1275     assertSame(value3, segment.get(key, hash));
1276     assertEquals(1, segment.count);
1277     assertTrue(segment.storeLoadedValue(key, hash, valueRef, value4));
1278     assertSame(value4, segment.get(key, hash));
1279     assertEquals(1, segment.count);
1280     notification = listener.remove();
1281     assertEquals(immutableEntry(key, value3), notification);
1282     assertEquals(RemovalCause.REPLACED, notification.getCause());
1283     assertTrue(listener.isEmpty());
1284 
1285     // collected
1286     entry.setValueReference(valueRef);
1287     table.set(index, entry);
1288     assertSame(value3, segment.get(key, hash));
1289     assertEquals(1, segment.count);
1290     value3Ref.clear();
1291     assertTrue(segment.storeLoadedValue(key, hash, valueRef, value4));
1292     assertSame(value4, segment.get(key, hash));
1293     assertEquals(1, segment.count);
1294     notification = listener.remove();
1295     assertEquals(immutableEntry(key, null), notification);
1296     assertEquals(RemovalCause.COLLECTED, notification.getCause());
1297     assertTrue(listener.isEmpty());
1298   }
1299 
testSegmentRemove()1300   public void testSegmentRemove() {
1301     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder().concurrencyLevel(1));
1302     Segment<Object, Object> segment = map.segments[0];
1303 
1304     Object key = new Object();
1305     int hash = map.hash(key);
1306     Object oldValue = new Object();
1307     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
1308     int index = hash & (table.length() - 1);
1309 
1310     DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null);
1311     DummyValueReference<Object, Object> oldValueRef = DummyValueReference.create(oldValue, entry);
1312     entry.setValueReference(oldValueRef);
1313 
1314     // no entry
1315     assertEquals(0, segment.count);
1316     assertNull(segment.remove(key, hash));
1317     assertEquals(0, segment.count);
1318 
1319     // same key
1320     table.set(index, entry);
1321     segment.count++;
1322     assertEquals(1, segment.count);
1323     assertSame(oldValue, segment.get(key, hash));
1324     assertSame(oldValue, segment.remove(key, hash));
1325     assertEquals(0, segment.count);
1326     assertNull(segment.get(key, hash));
1327 
1328     // cleared
1329     table.set(index, entry);
1330     segment.count++;
1331     assertEquals(1, segment.count);
1332     assertSame(oldValue, segment.get(key, hash));
1333     oldValueRef.clear();
1334     assertNull(segment.remove(key, hash));
1335     assertEquals(0, segment.count);
1336     assertNull(segment.get(key, hash));
1337   }
1338 
testSegmentRemoveValue()1339   public void testSegmentRemoveValue() {
1340     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder().concurrencyLevel(1));
1341     Segment<Object, Object> segment = map.segments[0];
1342 
1343     Object key = new Object();
1344     int hash = map.hash(key);
1345     Object oldValue = new Object();
1346     Object newValue = new Object();
1347     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
1348     int index = hash & (table.length() - 1);
1349 
1350     DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null);
1351     DummyValueReference<Object, Object> oldValueRef = DummyValueReference.create(oldValue, entry);
1352     entry.setValueReference(oldValueRef);
1353 
1354     // no entry
1355     assertEquals(0, segment.count);
1356     assertNull(segment.remove(key, hash));
1357     assertEquals(0, segment.count);
1358 
1359     // same value
1360     table.set(index, entry);
1361     segment.count++;
1362     assertEquals(1, segment.count);
1363     assertSame(oldValue, segment.get(key, hash));
1364     assertTrue(segment.remove(key, hash, oldValue));
1365     assertEquals(0, segment.count);
1366     assertNull(segment.get(key, hash));
1367 
1368     // different value
1369     table.set(index, entry);
1370     segment.count++;
1371     assertEquals(1, segment.count);
1372     assertSame(oldValue, segment.get(key, hash));
1373     assertFalse(segment.remove(key, hash, newValue));
1374     assertEquals(1, segment.count);
1375     assertSame(oldValue, segment.get(key, hash));
1376 
1377     // cleared
1378     assertSame(oldValue, segment.get(key, hash));
1379     oldValueRef.clear();
1380     assertFalse(segment.remove(key, hash, oldValue));
1381     assertEquals(0, segment.count);
1382     assertNull(segment.get(key, hash));
1383   }
1384 
testExpand()1385   public void testExpand() {
1386     LocalCache<Object, Object> map =
1387         makeLocalCache(createCacheBuilder().concurrencyLevel(1).initialCapacity(1));
1388     Segment<Object, Object> segment = map.segments[0];
1389     assertEquals(1, segment.table.length());
1390 
1391     // manually add elements to avoid expansion
1392     int originalCount = 1024;
1393     ReferenceEntry<Object, Object> entry = null;
1394     for (int i = 0; i < originalCount; i++) {
1395       Object key = new Object();
1396       Object value = new Object();
1397       int hash = map.hash(key);
1398       // chain all entries together as we only have a single bucket
1399       entry = map.newEntry(key, hash, entry);
1400       ValueReference<Object, Object> valueRef = map.newValueReference(entry, value, 1);
1401       entry.setValueReference(valueRef);
1402     }
1403     segment.table.set(0, entry);
1404     segment.count = originalCount;
1405     ImmutableMap<Object, Object> originalMap = ImmutableMap.copyOf(map);
1406     assertEquals(originalCount, originalMap.size());
1407     assertEquals(originalMap, map);
1408 
1409     for (int i = 1; i <= originalCount * 2; i *= 2) {
1410       if (i > 1) {
1411         segment.expand();
1412       }
1413       assertEquals(i, segment.table.length());
1414       assertEquals(originalCount, countLiveEntries(map, 0));
1415       assertEquals(originalCount, segment.count);
1416       assertEquals(originalMap, map);
1417     }
1418   }
1419 
testReclaimKey()1420   public void testReclaimKey() {
1421     CountingRemovalListener<Object, Object> listener = countingRemovalListener();
1422     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
1423         .concurrencyLevel(1)
1424         .initialCapacity(1)
1425         .maximumSize(SMALL_MAX_SIZE)
1426         .expireAfterWrite(99999, SECONDS)
1427         .removalListener(listener));
1428     Segment<Object, Object> segment = map.segments[0];
1429     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
1430     assertEquals(1, table.length());
1431 
1432     // create 3 objects and chain them together
1433     Object keyOne = new Object();
1434     Object valueOne = new Object();
1435     int hashOne = map.hash(keyOne);
1436     DummyEntry<Object, Object> entryOne = createDummyEntry(keyOne, hashOne, valueOne, null);
1437     Object keyTwo = new Object();
1438     Object valueTwo = new Object();
1439     int hashTwo = map.hash(keyTwo);
1440     DummyEntry<Object, Object> entryTwo = createDummyEntry(keyTwo, hashTwo, valueTwo, entryOne);
1441     Object keyThree = new Object();
1442     Object valueThree = new Object();
1443     int hashThree = map.hash(keyThree);
1444     DummyEntry<Object, Object> entryThree =
1445       createDummyEntry(keyThree, hashThree, valueThree, entryTwo);
1446 
1447     // absent
1448     assertEquals(0, listener.getCount());
1449     assertFalse(segment.reclaimKey(entryOne, hashOne));
1450     assertEquals(0, listener.getCount());
1451     table.set(0, entryOne);
1452     assertFalse(segment.reclaimKey(entryTwo, hashTwo));
1453     assertEquals(0, listener.getCount());
1454     table.set(0, entryTwo);
1455     assertFalse(segment.reclaimKey(entryThree, hashThree));
1456     assertEquals(0, listener.getCount());
1457 
1458     // present
1459     table.set(0, entryOne);
1460     segment.count = 1;
1461     assertTrue(segment.reclaimKey(entryOne, hashOne));
1462     assertEquals(1, listener.getCount());
1463     assertSame(keyOne, listener.getLastEvictedKey());
1464     assertSame(valueOne, listener.getLastEvictedValue());
1465     assertTrue(map.removalNotificationQueue.isEmpty());
1466     assertFalse(segment.accessQueue.contains(entryOne));
1467     assertFalse(segment.writeQueue.contains(entryOne));
1468     assertEquals(0, segment.count);
1469     assertNull(table.get(0));
1470   }
1471 
testRemoveEntryFromChain()1472   public void testRemoveEntryFromChain() {
1473     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder().concurrencyLevel(1));
1474     Segment<Object, Object> segment = map.segments[0];
1475 
1476     // create 3 objects and chain them together
1477     Object keyOne = new Object();
1478     Object valueOne = new Object();
1479     int hashOne = map.hash(keyOne);
1480     DummyEntry<Object, Object> entryOne = createDummyEntry(keyOne, hashOne, valueOne, null);
1481     Object keyTwo = new Object();
1482     Object valueTwo = new Object();
1483     int hashTwo = map.hash(keyTwo);
1484     DummyEntry<Object, Object> entryTwo = createDummyEntry(keyTwo, hashTwo, valueTwo, entryOne);
1485     Object keyThree = new Object();
1486     Object valueThree = new Object();
1487     int hashThree = map.hash(keyThree);
1488     DummyEntry<Object, Object> entryThree =
1489       createDummyEntry(keyThree, hashThree, valueThree, entryTwo);
1490 
1491     // alone
1492     assertNull(segment.removeEntryFromChain(entryOne, entryOne));
1493 
1494     // head
1495     assertSame(entryOne, segment.removeEntryFromChain(entryTwo, entryTwo));
1496 
1497     // middle
1498     ReferenceEntry<Object, Object> newFirst = segment.removeEntryFromChain(entryThree, entryTwo);
1499     assertSame(keyThree, newFirst.getKey());
1500     assertSame(valueThree, newFirst.getValueReference().get());
1501     assertEquals(hashThree, newFirst.getHash());
1502     assertSame(entryOne, newFirst.getNext());
1503 
1504     // tail (remaining entries are copied in reverse order)
1505     newFirst = segment.removeEntryFromChain(entryThree, entryOne);
1506     assertSame(keyTwo, newFirst.getKey());
1507     assertSame(valueTwo, newFirst.getValueReference().get());
1508     assertEquals(hashTwo, newFirst.getHash());
1509     newFirst = newFirst.getNext();
1510     assertSame(keyThree, newFirst.getKey());
1511     assertSame(valueThree, newFirst.getValueReference().get());
1512     assertEquals(hashThree, newFirst.getHash());
1513     assertNull(newFirst.getNext());
1514   }
1515 
testExpand_cleanup()1516   public void testExpand_cleanup() {
1517     LocalCache<Object, Object> map =
1518         makeLocalCache(createCacheBuilder().concurrencyLevel(1).initialCapacity(1));
1519     Segment<Object, Object> segment = map.segments[0];
1520     assertEquals(1, segment.table.length());
1521 
1522     // manually add elements to avoid expansion
1523     // 1/3 null keys, 1/3 null values
1524     int originalCount = 1024;
1525     ReferenceEntry<Object, Object> entry = null;
1526     for (int i = 0; i < originalCount; i++) {
1527       Object key = new Object();
1528       Object value = (i % 3 == 0) ? null : new Object();
1529       int hash = map.hash(key);
1530       if (i % 3 == 1) {
1531         key = null;
1532       }
1533       // chain all entries together as we only have a single bucket
1534       entry = DummyEntry.create(key, hash, entry);
1535       ValueReference<Object, Object> valueRef = DummyValueReference.create(value, entry);
1536       entry.setValueReference(valueRef);
1537     }
1538     segment.table.set(0, entry);
1539     segment.count = originalCount;
1540     int liveCount = originalCount / 3;
1541     assertEquals(1, segment.table.length());
1542     assertEquals(liveCount, countLiveEntries(map, 0));
1543     ImmutableMap<Object, Object> originalMap = ImmutableMap.copyOf(map);
1544     assertEquals(liveCount, originalMap.size());
1545     // can't compare map contents until cleanup occurs
1546 
1547     for (int i = 1; i <= originalCount * 2; i *= 2) {
1548       if (i > 1) {
1549         segment.expand();
1550       }
1551       assertEquals(i, segment.table.length());
1552       assertEquals(liveCount, countLiveEntries(map, 0));
1553       // expansion cleanup is sloppy, with a goal of avoiding unnecessary copies
1554       assertTrue(segment.count >= liveCount);
1555       assertTrue(segment.count <= originalCount);
1556       assertEquals(originalMap, ImmutableMap.copyOf(map));
1557     }
1558   }
1559 
countLiveEntries(LocalCache<K, V> map, long now)1560   private static <K, V> int countLiveEntries(LocalCache<K, V> map, long now) {
1561     int result = 0;
1562     for (Segment<K, V> segment : map.segments) {
1563       AtomicReferenceArray<ReferenceEntry<K, V>> table = segment.table;
1564       for (int i = 0; i < table.length(); i++) {
1565         for (ReferenceEntry<K, V> e = table.get(i); e != null; e = e.getNext()) {
1566           if (map.isLive(e, now)) {
1567             result++;
1568           }
1569         }
1570       }
1571     }
1572     return result;
1573   }
1574 
testClear()1575   public void testClear() {
1576     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
1577         .concurrencyLevel(1)
1578         .initialCapacity(1)
1579         .maximumSize(SMALL_MAX_SIZE)
1580         .expireAfterWrite(99999, SECONDS));
1581     Segment<Object, Object> segment = map.segments[0];
1582     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
1583     assertEquals(1, table.length());
1584 
1585     Object key = new Object();
1586     Object value = new Object();
1587     int hash = map.hash(key);
1588     DummyEntry<Object, Object> entry = createDummyEntry(key, hash, value, null);
1589     segment.recordWrite(entry, 1, map.ticker.read());
1590     segment.table.set(0, entry);
1591     segment.readCount.incrementAndGet();
1592     segment.count = 1;
1593     segment.totalWeight = 1;
1594 
1595     assertSame(entry, table.get(0));
1596     assertSame(entry, segment.accessQueue.peek());
1597     assertSame(entry, segment.writeQueue.peek());
1598 
1599     segment.clear();
1600     assertNull(table.get(0));
1601     assertTrue(segment.accessQueue.isEmpty());
1602     assertTrue(segment.writeQueue.isEmpty());
1603     assertEquals(0, segment.readCount.get());
1604     assertEquals(0, segment.count);
1605     assertEquals(0, segment.totalWeight);
1606   }
1607 
testClear_notification()1608   public void testClear_notification() {
1609     QueuingRemovalListener<Object, Object> listener = queuingRemovalListener();
1610     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
1611         .concurrencyLevel(1)
1612         .initialCapacity(1)
1613         .maximumSize(SMALL_MAX_SIZE)
1614         .expireAfterWrite(99999, SECONDS)
1615         .removalListener(listener));
1616     Segment<Object, Object> segment = map.segments[0];
1617     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
1618     assertEquals(1, table.length());
1619 
1620     Object key = new Object();
1621     Object value = new Object();
1622     int hash = map.hash(key);
1623     DummyEntry<Object, Object> entry = createDummyEntry(key, hash, value, null);
1624     segment.recordWrite(entry, 1, map.ticker.read());
1625     segment.table.set(0, entry);
1626     segment.readCount.incrementAndGet();
1627     segment.count = 1;
1628     segment.totalWeight = 1;
1629 
1630     assertSame(entry, table.get(0));
1631     assertSame(entry, segment.accessQueue.peek());
1632     assertSame(entry, segment.writeQueue.peek());
1633 
1634     segment.clear();
1635     assertNull(table.get(0));
1636     assertTrue(segment.accessQueue.isEmpty());
1637     assertTrue(segment.writeQueue.isEmpty());
1638     assertEquals(0, segment.readCount.get());
1639     assertEquals(0, segment.count);
1640     assertEquals(0, segment.totalWeight);
1641     assertNotified(listener, key, value, RemovalCause.EXPLICIT);
1642   }
1643 
testRemoveEntry()1644   public void testRemoveEntry() {
1645     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
1646         .concurrencyLevel(1)
1647         .initialCapacity(1)
1648         .maximumSize(SMALL_MAX_SIZE)
1649         .expireAfterWrite(99999, SECONDS)
1650         .removalListener(countingRemovalListener()));
1651     Segment<Object, Object> segment = map.segments[0];
1652     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
1653     assertEquals(1, table.length());
1654 
1655     Object key = new Object();
1656     Object value = new Object();
1657     int hash = map.hash(key);
1658     DummyEntry<Object, Object> entry = createDummyEntry(key, hash, value, null);
1659 
1660     // remove absent
1661     assertFalse(segment.removeEntry(entry, hash, RemovalCause.COLLECTED));
1662 
1663     // remove live
1664     segment.recordWrite(entry, 1, map.ticker.read());
1665     table.set(0, entry);
1666     segment.count = 1;
1667     assertTrue(segment.removeEntry(entry, hash, RemovalCause.COLLECTED));
1668     assertNotificationEnqueued(map, key, value, hash);
1669     assertTrue(map.removalNotificationQueue.isEmpty());
1670     assertFalse(segment.accessQueue.contains(entry));
1671     assertFalse(segment.writeQueue.contains(entry));
1672     assertEquals(0, segment.count);
1673     assertNull(table.get(0));
1674   }
1675 
testReclaimValue()1676   public void testReclaimValue() {
1677     CountingRemovalListener<Object, Object> listener =
1678         countingRemovalListener();
1679     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
1680         .concurrencyLevel(1)
1681         .initialCapacity(1)
1682         .maximumSize(SMALL_MAX_SIZE)
1683         .expireAfterWrite(99999, SECONDS)
1684         .removalListener(listener));
1685     Segment<Object, Object> segment = map.segments[0];
1686     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
1687     assertEquals(1, table.length());
1688 
1689     Object key = new Object();
1690     Object value = new Object();
1691     int hash = map.hash(key);
1692     DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null);
1693     DummyValueReference<Object, Object> valueRef = DummyValueReference.create(value, entry);
1694     entry.setValueReference(valueRef);
1695 
1696     // reclaim absent
1697     assertFalse(segment.reclaimValue(key, hash, valueRef));
1698 
1699     // reclaim live
1700     segment.recordWrite(entry, 1, map.ticker.read());
1701     table.set(0, entry);
1702     segment.count = 1;
1703     assertTrue(segment.reclaimValue(key, hash, valueRef));
1704     assertEquals(1, listener.getCount());
1705     assertSame(key, listener.getLastEvictedKey());
1706     assertSame(value, listener.getLastEvictedValue());
1707     assertTrue(map.removalNotificationQueue.isEmpty());
1708     assertFalse(segment.accessQueue.contains(entry));
1709     assertFalse(segment.writeQueue.contains(entry));
1710     assertEquals(0, segment.count);
1711     assertNull(table.get(0));
1712 
1713     // reclaim wrong value reference
1714     table.set(0, entry);
1715     DummyValueReference<Object, Object> otherValueRef = DummyValueReference.create(value, entry);
1716     entry.setValueReference(otherValueRef);
1717     assertFalse(segment.reclaimValue(key, hash, valueRef));
1718     assertEquals(1, listener.getCount());
1719     assertTrue(segment.reclaimValue(key, hash, otherValueRef));
1720     assertEquals(2, listener.getCount());
1721     assertSame(key, listener.getLastEvictedKey());
1722     assertSame(value, listener.getLastEvictedValue());
1723   }
1724 
testRemoveComputingValue()1725   public void testRemoveComputingValue() {
1726     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
1727         .concurrencyLevel(1)
1728         .initialCapacity(1)
1729         .maximumSize(SMALL_MAX_SIZE)
1730         .expireAfterWrite(99999, SECONDS)
1731         .removalListener(countingRemovalListener()));
1732     Segment<Object, Object> segment = map.segments[0];
1733     AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
1734     assertEquals(1, table.length());
1735 
1736     Object key = new Object();
1737     int hash = map.hash(key);
1738     DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null);
1739     LoadingValueReference<Object, Object> valueRef = new LoadingValueReference<Object, Object>();
1740     entry.setValueReference(valueRef);
1741 
1742     // absent
1743     assertFalse(segment.removeLoadingValue(key, hash, valueRef));
1744 
1745     // live
1746     table.set(0, entry);
1747     // don't increment count; this is used during computation
1748     assertTrue(segment.removeLoadingValue(key, hash, valueRef));
1749     // no notification sent with removeLoadingValue
1750     assertTrue(map.removalNotificationQueue.isEmpty());
1751     assertEquals(0, segment.count);
1752     assertNull(table.get(0));
1753 
1754     // active
1755     Object value = new Object();
1756     DummyValueReference<Object, Object> previousRef = DummyValueReference.create(value, entry);
1757     valueRef = new LoadingValueReference<Object, Object>(previousRef);
1758     entry.setValueReference(valueRef);
1759     table.set(0, entry);
1760     segment.count = 1;
1761     assertTrue(segment.removeLoadingValue(key, hash, valueRef));
1762     assertSame(entry, table.get(0));
1763     assertSame(value, segment.get(key, hash));
1764 
1765     // wrong value reference
1766     table.set(0, entry);
1767     DummyValueReference<Object, Object> otherValueRef = DummyValueReference.create(value, entry);
1768     entry.setValueReference(otherValueRef);
1769     assertFalse(segment.removeLoadingValue(key, hash, valueRef));
1770     entry.setValueReference(valueRef);
1771     assertTrue(segment.removeLoadingValue(key, hash, valueRef));
1772   }
1773 
assertNotificationEnqueued( LocalCache<K, V> map, K key, V value, int hash)1774   private static <K, V> void assertNotificationEnqueued(
1775       LocalCache<K, V> map, K key, V value, int hash) {
1776     RemovalNotification<K, V> notification = map.removalNotificationQueue.poll();
1777     assertSame(key, notification.getKey());
1778     assertSame(value, notification.getValue());
1779   }
1780 
1781   // Segment eviction tests
1782 
testDrainRecencyQueueOnWrite()1783   public void testDrainRecencyQueueOnWrite() {
1784     for (CacheBuilder<Object, Object> builder : allEvictingMakers()) {
1785       LocalCache<Object, Object> map = makeLocalCache(builder.concurrencyLevel(1));
1786       Segment<Object, Object> segment = map.segments[0];
1787 
1788       if (segment.recencyQueue != DISCARDING_QUEUE) {
1789         Object keyOne = new Object();
1790         Object valueOne = new Object();
1791         Object keyTwo = new Object();
1792         Object valueTwo = new Object();
1793 
1794         map.put(keyOne, valueOne);
1795         assertTrue(segment.recencyQueue.isEmpty());
1796 
1797         for (int i = 0; i < DRAIN_THRESHOLD / 2; i++) {
1798           map.get(keyOne);
1799         }
1800         assertFalse(segment.recencyQueue.isEmpty());
1801 
1802         map.put(keyTwo, valueTwo);
1803         assertTrue(segment.recencyQueue.isEmpty());
1804       }
1805     }
1806   }
1807 
testDrainRecencyQueueOnRead()1808   public void testDrainRecencyQueueOnRead() {
1809     for (CacheBuilder<Object, Object> builder : allEvictingMakers()) {
1810       LocalCache<Object, Object> map = makeLocalCache(builder.concurrencyLevel(1));
1811       Segment<Object, Object> segment = map.segments[0];
1812 
1813       if (segment.recencyQueue != DISCARDING_QUEUE) {
1814         Object keyOne = new Object();
1815         Object valueOne = new Object();
1816 
1817         // repeated get of the same key
1818 
1819         map.put(keyOne, valueOne);
1820         assertTrue(segment.recencyQueue.isEmpty());
1821 
1822         for (int i = 0; i < DRAIN_THRESHOLD / 2; i++) {
1823           map.get(keyOne);
1824         }
1825         assertFalse(segment.recencyQueue.isEmpty());
1826 
1827         for (int i = 0; i < DRAIN_THRESHOLD * 2; i++) {
1828           map.get(keyOne);
1829           assertTrue(segment.recencyQueue.size() <= DRAIN_THRESHOLD);
1830         }
1831 
1832         // get over many different keys
1833 
1834         for (int i = 0; i < DRAIN_THRESHOLD * 2; i++) {
1835           map.put(new Object(), new Object());
1836         }
1837         assertTrue(segment.recencyQueue.isEmpty());
1838 
1839         for (int i = 0; i < DRAIN_THRESHOLD / 2; i++) {
1840           map.get(keyOne);
1841         }
1842         assertFalse(segment.recencyQueue.isEmpty());
1843 
1844         for (Object key : map.keySet()) {
1845           map.get(key);
1846           assertTrue(segment.recencyQueue.size() <= DRAIN_THRESHOLD);
1847         }
1848       }
1849     }
1850   }
1851 
testRecordRead()1852   public void testRecordRead() {
1853     for (CacheBuilder<Object, Object> builder : allEvictingMakers()) {
1854       LocalCache<Object, Object> map = makeLocalCache(builder.concurrencyLevel(1));
1855       Segment<Object, Object> segment = map.segments[0];
1856       List<ReferenceEntry<Object, Object>> writeOrder = Lists.newLinkedList();
1857       List<ReferenceEntry<Object, Object>> readOrder = Lists.newLinkedList();
1858       for (int i = 0; i < DRAIN_THRESHOLD * 2; i++) {
1859         Object key = new Object();
1860         int hash = map.hash(key);
1861         Object value = new Object();
1862 
1863         ReferenceEntry<Object, Object> entry = createDummyEntry(key, hash, value, null);
1864         // must recordRead for drainRecencyQueue to believe this entry is live
1865         segment.recordWrite(entry, 1, map.ticker.read());
1866         writeOrder.add(entry);
1867         readOrder.add(entry);
1868       }
1869 
1870       checkEvictionQueues(map, segment, readOrder, writeOrder);
1871       checkExpirationTimes(map);
1872 
1873       // access some of the elements
1874       Random random = new Random();
1875       List<ReferenceEntry<Object, Object>> reads = Lists.newArrayList();
1876       Iterator<ReferenceEntry<Object, Object>> i = readOrder.iterator();
1877       while (i.hasNext()) {
1878         ReferenceEntry<Object, Object> entry = i.next();
1879         if (random.nextBoolean()) {
1880           segment.recordRead(entry, map.ticker.read());
1881           reads.add(entry);
1882           i.remove();
1883         }
1884       }
1885       checkAndDrainRecencyQueue(map, segment, reads);
1886       readOrder.addAll(reads);
1887 
1888       checkEvictionQueues(map, segment, readOrder, writeOrder);
1889       checkExpirationTimes(map);
1890     }
1891   }
1892 
testRecordReadOnGet()1893   public void testRecordReadOnGet() {
1894     for (CacheBuilder<Object, Object> builder : allEvictingMakers()) {
1895       LocalCache<Object, Object> map = makeLocalCache(builder.concurrencyLevel(1));
1896       Segment<Object, Object> segment = map.segments[0];
1897       List<ReferenceEntry<Object, Object>> writeOrder = Lists.newLinkedList();
1898       List<ReferenceEntry<Object, Object>> readOrder = Lists.newLinkedList();
1899       for (int i = 0; i < DRAIN_THRESHOLD * 2; i++) {
1900         Object key = new Object();
1901         int hash = map.hash(key);
1902         Object value = new Object();
1903 
1904         map.put(key, value);
1905         ReferenceEntry<Object, Object> entry = segment.getEntry(key, hash);
1906         writeOrder.add(entry);
1907         readOrder.add(entry);
1908       }
1909 
1910       checkEvictionQueues(map, segment, readOrder, writeOrder);
1911       checkExpirationTimes(map);
1912       assertTrue(segment.recencyQueue.isEmpty());
1913 
1914       // access some of the elements
1915       Random random = new Random();
1916       List<ReferenceEntry<Object, Object>> reads = Lists.newArrayList();
1917       Iterator<ReferenceEntry<Object, Object>> i = readOrder.iterator();
1918       while (i.hasNext()) {
1919         ReferenceEntry<Object, Object> entry = i.next();
1920         if (random.nextBoolean()) {
1921           map.get(entry.getKey());
1922           reads.add(entry);
1923           i.remove();
1924           assertTrue(segment.recencyQueue.size() <= DRAIN_THRESHOLD);
1925         }
1926       }
1927       int undrainedIndex = reads.size() - segment.recencyQueue.size();
1928       checkAndDrainRecencyQueue(map, segment, reads.subList(undrainedIndex, reads.size()));
1929       readOrder.addAll(reads);
1930 
1931       checkEvictionQueues(map, segment, readOrder, writeOrder);
1932       checkExpirationTimes(map);
1933     }
1934   }
1935 
testRecordWrite()1936   public void testRecordWrite() {
1937     for (CacheBuilder<Object, Object> builder : allEvictingMakers()) {
1938       LocalCache<Object, Object> map = makeLocalCache(builder.concurrencyLevel(1));
1939       Segment<Object, Object> segment = map.segments[0];
1940       List<ReferenceEntry<Object, Object>> writeOrder = Lists.newLinkedList();
1941       for (int i = 0; i < DRAIN_THRESHOLD * 2; i++) {
1942         Object key = new Object();
1943         int hash = map.hash(key);
1944         Object value = new Object();
1945 
1946         ReferenceEntry<Object, Object> entry = createDummyEntry(key, hash, value, null);
1947         // must recordRead for drainRecencyQueue to believe this entry is live
1948         segment.recordWrite(entry, 1, map.ticker.read());
1949         writeOrder.add(entry);
1950       }
1951 
1952       checkEvictionQueues(map, segment, writeOrder, writeOrder);
1953       checkExpirationTimes(map);
1954 
1955       // access some of the elements
1956       Random random = new Random();
1957       List<ReferenceEntry<Object, Object>> writes = Lists.newArrayList();
1958       Iterator<ReferenceEntry<Object, Object>> i = writeOrder.iterator();
1959       while (i.hasNext()) {
1960         ReferenceEntry<Object, Object> entry = i.next();
1961         if (random.nextBoolean()) {
1962           segment.recordWrite(entry, 1, map.ticker.read());
1963           writes.add(entry);
1964           i.remove();
1965         }
1966       }
1967       writeOrder.addAll(writes);
1968 
1969       checkEvictionQueues(map, segment, writeOrder, writeOrder);
1970       checkExpirationTimes(map);
1971     }
1972   }
1973 
checkAndDrainRecencyQueue(LocalCache<K, V> map, Segment<K, V> segment, List<ReferenceEntry<K, V>> reads)1974   static <K, V> void checkAndDrainRecencyQueue(LocalCache<K, V> map,
1975       Segment<K, V> segment, List<ReferenceEntry<K, V>> reads) {
1976     if (map.evictsBySize() || map.expiresAfterAccess()) {
1977       assertSameEntries(reads, ImmutableList.copyOf(segment.recencyQueue));
1978     }
1979     segment.drainRecencyQueue();
1980   }
1981 
checkEvictionQueues(LocalCache<K, V> map, Segment<K, V> segment, List<ReferenceEntry<K, V>> readOrder, List<ReferenceEntry<K, V>> writeOrder)1982   static <K, V> void checkEvictionQueues(LocalCache<K, V> map,
1983       Segment<K, V> segment, List<ReferenceEntry<K, V>> readOrder,
1984       List<ReferenceEntry<K, V>> writeOrder) {
1985     if (map.evictsBySize() || map.expiresAfterAccess()) {
1986       assertSameEntries(readOrder, ImmutableList.copyOf(segment.accessQueue));
1987     }
1988     if (map.expiresAfterWrite()) {
1989       assertSameEntries(writeOrder, ImmutableList.copyOf(segment.writeQueue));
1990     }
1991   }
1992 
assertSameEntries(List<ReferenceEntry<K, V>> expectedEntries, List<ReferenceEntry<K, V>> actualEntries)1993   private static <K, V> void assertSameEntries(List<ReferenceEntry<K, V>> expectedEntries,
1994       List<ReferenceEntry<K, V>> actualEntries) {
1995     int size = expectedEntries.size();
1996     assertEquals(size, actualEntries.size());
1997     for (int i = 0; i < size; i++) {
1998       ReferenceEntry<K, V> expectedEntry = expectedEntries.get(0);
1999       ReferenceEntry<K, V> actualEntry = actualEntries.get(0);
2000       assertSame(expectedEntry.getKey(), actualEntry.getKey());
2001       assertSame(expectedEntry.getValueReference().get(), actualEntry.getValueReference().get());
2002     }
2003   }
2004 
checkExpirationTimes(LocalCache<K, V> map)2005   static <K, V> void checkExpirationTimes(LocalCache<K, V> map) {
2006     if (!map.expires()) {
2007       return;
2008     }
2009 
2010     for (Segment<K, V> segment : map.segments) {
2011       long lastAccessTime = 0;
2012       long lastWriteTime = 0;
2013       for (ReferenceEntry<K, V> e : segment.recencyQueue) {
2014         long accessTime = e.getAccessTime();
2015         assertTrue(accessTime >= lastAccessTime);
2016         lastAccessTime = accessTime;
2017         long writeTime = e.getWriteTime();
2018         assertTrue(writeTime >= lastWriteTime);
2019         lastWriteTime = writeTime;
2020       }
2021 
2022       lastAccessTime = 0;
2023       lastWriteTime = 0;
2024       for (ReferenceEntry<K, V> e : segment.accessQueue) {
2025         long accessTime = e.getAccessTime();
2026         assertTrue(accessTime >= lastAccessTime);
2027         lastAccessTime = accessTime;
2028       }
2029       for (ReferenceEntry<K, V> e : segment.writeQueue) {
2030         long writeTime = e.getWriteTime();
2031         assertTrue(writeTime >= lastWriteTime);
2032         lastWriteTime = writeTime;
2033       }
2034     }
2035   }
2036 
testExpireAfterWrite()2037   public void testExpireAfterWrite() {
2038     FakeTicker ticker = new FakeTicker();
2039     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
2040         .concurrencyLevel(1)
2041         .ticker(ticker)
2042         .expireAfterWrite(1, TimeUnit.NANOSECONDS));
2043     Segment<Object, Object> segment = map.segments[0];
2044 
2045     Object key = new Object();
2046     Object value = new Object();
2047     map.put(key, value);
2048     ReferenceEntry<Object, Object> entry = map.getEntry(key);
2049     assertTrue(map.isLive(entry, ticker.read()));
2050 
2051     segment.writeQueue.add(entry);
2052     assertSame(value, map.get(key));
2053     assertSame(entry, segment.writeQueue.peek());
2054     assertEquals(1, segment.writeQueue.size());
2055 
2056     segment.recordRead(entry, ticker.read());
2057     segment.expireEntries(ticker.read());
2058     assertSame(value, map.get(key));
2059     assertSame(entry, segment.writeQueue.peek());
2060     assertEquals(1, segment.writeQueue.size());
2061 
2062     ticker.advance(1);
2063     segment.recordRead(entry, ticker.read());
2064     segment.expireEntries(ticker.read());
2065     assertSame(value, map.get(key));
2066     assertSame(entry, segment.writeQueue.peek());
2067     assertEquals(1, segment.writeQueue.size());
2068 
2069     ticker.advance(1);
2070     assertNull(map.get(key));
2071     segment.expireEntries(ticker.read());
2072     assertNull(map.get(key));
2073     assertTrue(segment.writeQueue.isEmpty());
2074   }
2075 
testExpireAfterAccess()2076   public void testExpireAfterAccess() {
2077     FakeTicker ticker = new FakeTicker();
2078     LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder()
2079         .concurrencyLevel(1)
2080         .ticker(ticker)
2081         .expireAfterAccess(1, TimeUnit.NANOSECONDS));
2082     Segment<Object, Object> segment = map.segments[0];
2083 
2084     Object key = new Object();
2085     Object value = new Object();
2086     map.put(key, value);
2087     ReferenceEntry<Object, Object> entry = map.getEntry(key);
2088     assertTrue(map.isLive(entry, ticker.read()));
2089 
2090     segment.accessQueue.add(entry);
2091     assertSame(value, map.get(key));
2092     assertSame(entry, segment.accessQueue.peek());
2093     assertEquals(1, segment.accessQueue.size());
2094 
2095     segment.recordRead(entry, ticker.read());
2096     segment.expireEntries(ticker.read());
2097     assertTrue(map.containsKey(key));
2098     assertSame(entry, segment.accessQueue.peek());
2099     assertEquals(1, segment.accessQueue.size());
2100 
2101     ticker.advance(1);
2102     segment.recordRead(entry, ticker.read());
2103     segment.expireEntries(ticker.read());
2104     assertTrue(map.containsKey(key));
2105     assertSame(entry, segment.accessQueue.peek());
2106     assertEquals(1, segment.accessQueue.size());
2107 
2108     ticker.advance(1);
2109     segment.recordRead(entry, ticker.read());
2110     segment.expireEntries(ticker.read());
2111     assertTrue(map.containsKey(key));
2112     assertSame(entry, segment.accessQueue.peek());
2113     assertEquals(1, segment.accessQueue.size());
2114 
2115     ticker.advance(1);
2116     segment.expireEntries(ticker.read());
2117     assertTrue(map.containsKey(key));
2118     assertSame(entry, segment.accessQueue.peek());
2119     assertEquals(1, segment.accessQueue.size());
2120 
2121     ticker.advance(1);
2122     assertFalse(map.containsKey(key));
2123     assertNull(map.get(key));
2124     segment.expireEntries(ticker.read());
2125     assertFalse(map.containsKey(key));
2126     assertNull(map.get(key));
2127     assertTrue(segment.accessQueue.isEmpty());
2128   }
2129 
testEvictEntries()2130   public void testEvictEntries() {
2131     int maxSize = 10;
2132     LocalCache<Object, Object> map =
2133         makeLocalCache(createCacheBuilder().concurrencyLevel(1).maximumSize(maxSize));
2134     Segment<Object, Object> segment = map.segments[0];
2135 
2136     // manually add elements to avoid eviction
2137     int originalCount = 1024;
2138     ReferenceEntry<Object, Object> entry = null;
2139     LinkedHashMap<Object, Object> originalMap = Maps.newLinkedHashMap();
2140     for (int i = 0; i < originalCount; i++) {
2141       Object key = new Object();
2142       Object value = new Object();
2143       AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table;
2144       int hash = map.hash(key);
2145       int index = hash & (table.length() - 1);
2146       ReferenceEntry<Object, Object> first = table.get(index);
2147       entry = map.newEntry(key, hash, first);
2148       ValueReference<Object, Object> valueRef = map.newValueReference(entry, value, 1);
2149       entry.setValueReference(valueRef);
2150       segment.recordWrite(entry, 1, map.ticker.read());
2151       table.set(index, entry);
2152       originalMap.put(key, value);
2153     }
2154     segment.count = originalCount;
2155     segment.totalWeight = originalCount;
2156     assertEquals(originalCount, map.size());
2157     assertEquals(originalMap, map);
2158 
2159     Iterator<Object> it = originalMap.keySet().iterator();
2160     for (int i = 0; i < originalCount - maxSize; i++) {
2161       it.next();
2162       it.remove();
2163     }
2164     segment.evictEntries();
2165     assertEquals(maxSize, map.size());
2166     assertEquals(originalMap, map);
2167   }
2168 
2169   // reference queues
2170 
testDrainKeyReferenceQueueOnWrite()2171   public void testDrainKeyReferenceQueueOnWrite() {
2172     for (CacheBuilder<Object, Object> builder : allKeyValueStrengthMakers()) {
2173       LocalCache<Object, Object> map =
2174           makeLocalCache(builder.concurrencyLevel(1));
2175       if (map.usesKeyReferences()) {
2176         Segment<Object, Object> segment = map.segments[0];
2177 
2178         Object keyOne = new Object();
2179         int hashOne = map.hash(keyOne);
2180         Object valueOne = new Object();
2181         Object keyTwo = new Object();
2182         Object valueTwo = new Object();
2183 
2184         map.put(keyOne, valueOne);
2185         ReferenceEntry<Object, Object> entry = segment.getEntry(keyOne, hashOne);
2186 
2187         @SuppressWarnings("unchecked")
2188         Reference<Object> reference = (Reference) entry;
2189         reference.enqueue();
2190 
2191         map.put(keyTwo, valueTwo);
2192         assertFalse(map.containsKey(keyOne));
2193         assertFalse(map.containsValue(valueOne));
2194         assertNull(map.get(keyOne));
2195         assertEquals(1, map.size());
2196         assertNull(segment.keyReferenceQueue.poll());
2197       }
2198     }
2199   }
2200 
testDrainValueReferenceQueueOnWrite()2201   public void testDrainValueReferenceQueueOnWrite() {
2202     for (CacheBuilder<Object, Object> builder : allKeyValueStrengthMakers()) {
2203       LocalCache<Object, Object> map =
2204           makeLocalCache(builder.concurrencyLevel(1));
2205       if (map.usesValueReferences()) {
2206         Segment<Object, Object> segment = map.segments[0];
2207 
2208         Object keyOne = new Object();
2209         int hashOne = map.hash(keyOne);
2210         Object valueOne = new Object();
2211         Object keyTwo = new Object();
2212         Object valueTwo = new Object();
2213 
2214         map.put(keyOne, valueOne);
2215         ReferenceEntry<Object, Object> entry = segment.getEntry(keyOne, hashOne);
2216         ValueReference<Object, Object> valueReference = entry.getValueReference();
2217 
2218         @SuppressWarnings("unchecked")
2219         Reference<Object> reference = (Reference) valueReference;
2220         reference.enqueue();
2221 
2222         map.put(keyTwo, valueTwo);
2223         assertFalse(map.containsKey(keyOne));
2224         assertFalse(map.containsValue(valueOne));
2225         assertNull(map.get(keyOne));
2226         assertEquals(1, map.size());
2227         assertNull(segment.valueReferenceQueue.poll());
2228       }
2229     }
2230   }
2231 
testDrainKeyReferenceQueueOnRead()2232   public void testDrainKeyReferenceQueueOnRead() {
2233     for (CacheBuilder<Object, Object> builder : allKeyValueStrengthMakers()) {
2234       LocalCache<Object, Object> map =
2235           makeLocalCache(builder.concurrencyLevel(1));
2236       if (map.usesKeyReferences()) {
2237         Segment<Object, Object> segment = map.segments[0];
2238 
2239         Object keyOne = new Object();
2240         int hashOne = map.hash(keyOne);
2241         Object valueOne = new Object();
2242         Object keyTwo = new Object();
2243 
2244         map.put(keyOne, valueOne);
2245         ReferenceEntry<Object, Object> entry = segment.getEntry(keyOne, hashOne);
2246 
2247         @SuppressWarnings("unchecked")
2248         Reference<Object> reference = (Reference) entry;
2249         reference.enqueue();
2250 
2251         for (int i = 0; i < SMALL_MAX_SIZE; i++) {
2252           map.get(keyTwo);
2253         }
2254         assertFalse(map.containsKey(keyOne));
2255         assertFalse(map.containsValue(valueOne));
2256         assertNull(map.get(keyOne));
2257         assertEquals(0, map.size());
2258         assertNull(segment.keyReferenceQueue.poll());
2259       }
2260     }
2261   }
2262 
testDrainValueReferenceQueueOnRead()2263   public void testDrainValueReferenceQueueOnRead() {
2264     for (CacheBuilder<Object, Object> builder : allKeyValueStrengthMakers()) {
2265       LocalCache<Object, Object> map =
2266           makeLocalCache(builder.concurrencyLevel(1));
2267       if (map.usesValueReferences()) {
2268         Segment<Object, Object> segment = map.segments[0];
2269 
2270         Object keyOne = new Object();
2271         int hashOne = map.hash(keyOne);
2272         Object valueOne = new Object();
2273         Object keyTwo = new Object();
2274 
2275         map.put(keyOne, valueOne);
2276         ReferenceEntry<Object, Object> entry = segment.getEntry(keyOne, hashOne);
2277         ValueReference<Object, Object> valueReference = entry.getValueReference();
2278 
2279         @SuppressWarnings("unchecked")
2280         Reference<Object> reference = (Reference) valueReference;
2281         reference.enqueue();
2282 
2283         for (int i = 0; i < SMALL_MAX_SIZE; i++) {
2284           map.get(keyTwo);
2285         }
2286         assertFalse(map.containsKey(keyOne));
2287         assertFalse(map.containsValue(valueOne));
2288         assertNull(map.get(keyOne));
2289         assertEquals(0, map.size());
2290         assertNull(segment.valueReferenceQueue.poll());
2291       }
2292     }
2293   }
2294 
testNullParameters()2295   public void testNullParameters() throws Exception {
2296     NullPointerTester tester = new NullPointerTester();
2297     tester.testAllPublicInstanceMethods(makeLocalCache(createCacheBuilder()));
2298     CacheLoader<Object, Object> loader = identityLoader();
2299     tester.testAllPublicInstanceMethods(makeLocalCache(createCacheBuilder()));
2300   }
2301 
testSerializationProxyLoading()2302   public void testSerializationProxyLoading() {
2303     CacheLoader<Object, Object> loader = new SerializableCacheLoader();
2304     RemovalListener<Object, Object> listener = new SerializableRemovalListener<Object, Object>();
2305     SerializableWeigher<Object, Object> weigher = new SerializableWeigher<Object, Object>();
2306     Ticker ticker = new SerializableTicker();
2307     @SuppressWarnings("unchecked") // createMock
2308     LocalLoadingCache<Object, Object> one = (LocalLoadingCache) CacheBuilder.newBuilder()
2309         .weakKeys()
2310         .softValues()
2311         .expireAfterAccess(123, SECONDS)
2312         .expireAfterWrite(456, MINUTES)
2313         .maximumWeight(789)
2314         .weigher(weigher)
2315         .concurrencyLevel(12)
2316         .removalListener(listener)
2317         .ticker(ticker)
2318         .build(loader);
2319     // add a non-serializable entry
2320     one.getUnchecked(new Object());
2321     assertEquals(1, one.size());
2322     assertFalse(one.asMap().isEmpty());
2323     LocalLoadingCache<Object, Object> two = SerializableTester.reserialize(one);
2324     assertEquals(0, two.size());
2325     assertTrue(two.asMap().isEmpty());
2326 
2327     LocalCache<Object, Object> localCacheOne = one.localCache;
2328     LocalCache<Object, Object> localCacheTwo = two.localCache;
2329 
2330     assertEquals(localCacheOne.keyStrength, localCacheTwo.keyStrength);
2331     assertEquals(localCacheOne.keyStrength, localCacheTwo.keyStrength);
2332     assertEquals(localCacheOne.valueEquivalence, localCacheTwo.valueEquivalence);
2333     assertEquals(localCacheOne.valueEquivalence, localCacheTwo.valueEquivalence);
2334     assertEquals(localCacheOne.maxWeight, localCacheTwo.maxWeight);
2335     assertEquals(localCacheOne.weigher, localCacheTwo.weigher);
2336     assertEquals(localCacheOne.expireAfterAccessNanos, localCacheTwo.expireAfterAccessNanos);
2337     assertEquals(localCacheOne.expireAfterWriteNanos, localCacheTwo.expireAfterWriteNanos);
2338     assertEquals(localCacheOne.refreshNanos, localCacheTwo.refreshNanos);
2339     assertEquals(localCacheOne.removalListener, localCacheTwo.removalListener);
2340     assertEquals(localCacheOne.ticker, localCacheTwo.ticker);
2341 
2342     // serialize the reconstituted version to be sure we haven't lost the ability to reserialize
2343     LocalLoadingCache<Object, Object> three = SerializableTester.reserialize(two);
2344     LocalCache<Object, Object> localCacheThree = three.localCache;
2345 
2346     assertEquals(localCacheTwo.defaultLoader, localCacheThree.defaultLoader);
2347     assertEquals(localCacheTwo.keyStrength, localCacheThree.keyStrength);
2348     assertEquals(localCacheTwo.keyStrength, localCacheThree.keyStrength);
2349     assertEquals(localCacheTwo.valueEquivalence, localCacheThree.valueEquivalence);
2350     assertEquals(localCacheTwo.valueEquivalence, localCacheThree.valueEquivalence);
2351     assertEquals(localCacheTwo.maxWeight, localCacheThree.maxWeight);
2352     assertEquals(localCacheTwo.weigher, localCacheThree.weigher);
2353     assertEquals(localCacheTwo.expireAfterAccessNanos, localCacheThree.expireAfterAccessNanos);
2354     assertEquals(localCacheTwo.expireAfterWriteNanos, localCacheThree.expireAfterWriteNanos);
2355     assertEquals(localCacheTwo.removalListener, localCacheThree.removalListener);
2356     assertEquals(localCacheTwo.ticker, localCacheThree.ticker);
2357   }
2358 
testSerializationProxyManual()2359   public void testSerializationProxyManual() {
2360     RemovalListener<Object, Object> listener = new SerializableRemovalListener<Object, Object>();
2361     SerializableWeigher<Object, Object> weigher = new SerializableWeigher<Object, Object>();
2362     Ticker ticker = new SerializableTicker();
2363     @SuppressWarnings("unchecked") // createMock
2364     LocalManualCache<Object, Object> one = (LocalManualCache) CacheBuilder.newBuilder()
2365         .weakKeys()
2366         .softValues()
2367         .expireAfterAccess(123, NANOSECONDS)
2368         .maximumWeight(789)
2369         .weigher(weigher)
2370         .concurrencyLevel(12)
2371         .removalListener(listener)
2372         .ticker(ticker)
2373         .build();
2374     // add a non-serializable entry
2375     one.put(new Object(), new Object());
2376     assertEquals(1, one.size());
2377     assertFalse(one.asMap().isEmpty());
2378     LocalManualCache<Object, Object> two = SerializableTester.reserialize(one);
2379     assertEquals(0, two.size());
2380     assertTrue(two.asMap().isEmpty());
2381 
2382     LocalCache<Object, Object> localCacheOne = one.localCache;
2383     LocalCache<Object, Object> localCacheTwo = two.localCache;
2384 
2385     assertEquals(localCacheOne.keyStrength, localCacheTwo.keyStrength);
2386     assertEquals(localCacheOne.keyStrength, localCacheTwo.keyStrength);
2387     assertEquals(localCacheOne.valueEquivalence, localCacheTwo.valueEquivalence);
2388     assertEquals(localCacheOne.valueEquivalence, localCacheTwo.valueEquivalence);
2389     assertEquals(localCacheOne.maxWeight, localCacheTwo.maxWeight);
2390     assertEquals(localCacheOne.weigher, localCacheTwo.weigher);
2391     assertEquals(localCacheOne.expireAfterAccessNanos, localCacheTwo.expireAfterAccessNanos);
2392     assertEquals(localCacheOne.expireAfterWriteNanos, localCacheTwo.expireAfterWriteNanos);
2393     assertEquals(localCacheOne.removalListener, localCacheTwo.removalListener);
2394     assertEquals(localCacheOne.ticker, localCacheTwo.ticker);
2395 
2396     // serialize the reconstituted version to be sure we haven't lost the ability to reserialize
2397     LocalManualCache<Object, Object> three = SerializableTester.reserialize(two);
2398     LocalCache<Object, Object> localCacheThree = three.localCache;
2399 
2400     assertEquals(localCacheTwo.keyStrength, localCacheThree.keyStrength);
2401     assertEquals(localCacheTwo.keyStrength, localCacheThree.keyStrength);
2402     assertEquals(localCacheTwo.valueEquivalence, localCacheThree.valueEquivalence);
2403     assertEquals(localCacheTwo.valueEquivalence, localCacheThree.valueEquivalence);
2404     assertEquals(localCacheTwo.maxWeight, localCacheThree.maxWeight);
2405     assertEquals(localCacheTwo.weigher, localCacheThree.weigher);
2406     assertEquals(localCacheTwo.expireAfterAccessNanos, localCacheThree.expireAfterAccessNanos);
2407     assertEquals(localCacheTwo.expireAfterWriteNanos, localCacheThree.expireAfterWriteNanos);
2408     assertEquals(localCacheTwo.removalListener, localCacheThree.removalListener);
2409     assertEquals(localCacheTwo.ticker, localCacheThree.ticker);
2410   }
2411 
2412   // utility methods
2413 
2414   /**
2415    * Returns an iterable containing all combinations of maximumSize, expireAfterAccess/Write,
2416    * weakKeys and weak/softValues.
2417    */
2418   @SuppressWarnings("unchecked") // varargs
allEntryTypeMakers()2419   private static Iterable<CacheBuilder<Object, Object>> allEntryTypeMakers() {
2420     List<CacheBuilder<Object, Object>> result =
2421         newArrayList(allKeyValueStrengthMakers());
2422     for (CacheBuilder<Object, Object> builder : allKeyValueStrengthMakers()) {
2423       result.add(builder.maximumSize(SMALL_MAX_SIZE));
2424     }
2425     for (CacheBuilder<Object, Object> builder : allKeyValueStrengthMakers()) {
2426       result.add(builder.expireAfterAccess(99999, SECONDS));
2427     }
2428     for (CacheBuilder<Object, Object> builder : allKeyValueStrengthMakers()) {
2429       result.add(builder.expireAfterWrite(99999, SECONDS));
2430     }
2431     for (CacheBuilder<Object, Object> builder : allKeyValueStrengthMakers()) {
2432       result.add(builder.maximumSize(SMALL_MAX_SIZE).expireAfterAccess(99999, SECONDS));
2433     }
2434     for (CacheBuilder<Object, Object> builder : allKeyValueStrengthMakers()) {
2435       result.add(builder.maximumSize(SMALL_MAX_SIZE).expireAfterWrite(99999, SECONDS));
2436     }
2437     return result;
2438   }
2439 
2440   /**
2441    * Returns an iterable containing all combinations of maximumSize and expireAfterAccess/Write.
2442    */
2443   @SuppressWarnings("unchecked") // varargs
allEvictingMakers()2444   static Iterable<CacheBuilder<Object, Object>> allEvictingMakers() {
2445     return ImmutableList.of(createCacheBuilder().maximumSize(SMALL_MAX_SIZE),
2446         createCacheBuilder().expireAfterAccess(99999, SECONDS),
2447         createCacheBuilder().expireAfterWrite(99999, SECONDS),
2448         createCacheBuilder()
2449             .maximumSize(SMALL_MAX_SIZE)
2450             .expireAfterAccess(SMALL_MAX_SIZE, TimeUnit.SECONDS),
2451         createCacheBuilder()
2452             .maximumSize(SMALL_MAX_SIZE)
2453             .expireAfterWrite(SMALL_MAX_SIZE, TimeUnit.SECONDS));
2454   }
2455 
2456   /**
2457    * Returns an iterable containing all combinations weakKeys and weak/softValues.
2458    */
2459   @SuppressWarnings("unchecked") // varargs
allKeyValueStrengthMakers()2460   private static Iterable<CacheBuilder<Object, Object>> allKeyValueStrengthMakers() {
2461     return ImmutableList.of(createCacheBuilder(),
2462         createCacheBuilder().weakValues(),
2463         createCacheBuilder().softValues(),
2464         createCacheBuilder().weakKeys(),
2465         createCacheBuilder().weakKeys().weakValues(),
2466         createCacheBuilder().weakKeys().softValues());
2467   }
2468 
2469   // entries and values
2470 
createDummyEntry( K key, int hash, V value, ReferenceEntry<K, V> next)2471   private static <K, V> DummyEntry<K, V> createDummyEntry(
2472       K key, int hash, V value, ReferenceEntry<K, V> next) {
2473     DummyEntry<K, V> entry = DummyEntry.create(key, hash, next);
2474     DummyValueReference<K, V> valueRef = DummyValueReference.create(value, entry);
2475     entry.setValueReference(valueRef);
2476     return entry;
2477   }
2478 
2479   static class DummyEntry<K, V> implements ReferenceEntry<K, V> {
2480     private K key;
2481     private final int hash;
2482     private final ReferenceEntry<K, V> next;
2483 
DummyEntry(K key, int hash, ReferenceEntry<K, V> next)2484     public DummyEntry(K key, int hash, ReferenceEntry<K, V> next) {
2485       this.key = key;
2486       this.hash = hash;
2487       this.next = next;
2488     }
2489 
create(K key, int hash, ReferenceEntry<K, V> next)2490     public static <K, V> DummyEntry<K, V> create(K key, int hash, ReferenceEntry<K, V> next) {
2491       return new DummyEntry<K, V>(key, hash, next);
2492     }
2493 
clearKey()2494     public void clearKey() {
2495       this.key = null;
2496     }
2497 
2498     private ValueReference<K, V> valueReference = unset();
2499 
2500     @Override
getValueReference()2501     public ValueReference<K, V> getValueReference() {
2502       return valueReference;
2503     }
2504 
2505     @Override
setValueReference(ValueReference<K, V> valueReference)2506     public void setValueReference(ValueReference<K, V> valueReference) {
2507       this.valueReference = valueReference;
2508     }
2509 
2510     @Override
getNext()2511     public ReferenceEntry<K, V> getNext() {
2512       return next;
2513     }
2514 
2515     @Override
getHash()2516     public int getHash() {
2517       return hash;
2518     }
2519 
2520     @Override
getKey()2521     public K getKey() {
2522       return key;
2523     }
2524 
2525     private long accessTime = Long.MAX_VALUE;
2526 
2527     @Override
getAccessTime()2528     public long getAccessTime() {
2529       return accessTime;
2530     }
2531 
2532     @Override
setAccessTime(long time)2533     public void setAccessTime(long time) {
2534       this.accessTime = time;
2535     }
2536 
2537     private ReferenceEntry<K, V> nextAccess = nullEntry();
2538 
2539     @Override
getNextInAccessQueue()2540     public ReferenceEntry<K, V> getNextInAccessQueue() {
2541       return nextAccess;
2542     }
2543 
2544     @Override
setNextInAccessQueue(ReferenceEntry<K, V> next)2545     public void setNextInAccessQueue(ReferenceEntry<K, V> next) {
2546       this.nextAccess = next;
2547     }
2548 
2549     private ReferenceEntry<K, V> previousAccess = nullEntry();
2550 
2551     @Override
getPreviousInAccessQueue()2552     public ReferenceEntry<K, V> getPreviousInAccessQueue() {
2553       return previousAccess;
2554     }
2555 
2556     @Override
setPreviousInAccessQueue(ReferenceEntry<K, V> previous)2557     public void setPreviousInAccessQueue(ReferenceEntry<K, V> previous) {
2558       this.previousAccess = previous;
2559     }
2560 
2561     private long writeTime = Long.MAX_VALUE;
2562 
2563     @Override
getWriteTime()2564     public long getWriteTime() {
2565       return writeTime;
2566     }
2567 
2568     @Override
setWriteTime(long time)2569     public void setWriteTime(long time) {
2570       this.writeTime = time;
2571     }
2572 
2573     private ReferenceEntry<K, V> nextWrite = nullEntry();
2574 
2575     @Override
getNextInWriteQueue()2576     public ReferenceEntry<K, V> getNextInWriteQueue() {
2577       return nextWrite;
2578     }
2579 
2580     @Override
setNextInWriteQueue(ReferenceEntry<K, V> next)2581     public void setNextInWriteQueue(ReferenceEntry<K, V> next) {
2582       this.nextWrite = next;
2583     }
2584 
2585     private ReferenceEntry<K, V> previousWrite = nullEntry();
2586 
2587     @Override
getPreviousInWriteQueue()2588     public ReferenceEntry<K, V> getPreviousInWriteQueue() {
2589       return previousWrite;
2590     }
2591 
2592     @Override
setPreviousInWriteQueue(ReferenceEntry<K, V> previous)2593     public void setPreviousInWriteQueue(ReferenceEntry<K, V> previous) {
2594       this.previousWrite = previous;
2595     }
2596   }
2597 
2598   static class DummyValueReference<K, V> implements ValueReference<K, V> {
2599     final ReferenceEntry<K, V> entry;
2600     private V value;
2601     boolean loading = false;
2602 
DummyValueReference(ReferenceEntry<K, V> entry)2603     public DummyValueReference(ReferenceEntry<K, V> entry) {
2604       this(null, entry);
2605       this.loading = true;
2606     }
2607 
DummyValueReference(V value, ReferenceEntry<K, V> entry)2608     public DummyValueReference(V value, ReferenceEntry<K, V> entry) {
2609       this.value = value;
2610       this.entry = entry;
2611     }
2612 
create(V value, ReferenceEntry<K, V> entry)2613     public static <K, V> DummyValueReference<K, V> create(V value, ReferenceEntry<K, V> entry) {
2614       return new DummyValueReference<K, V>(value, entry);
2615     }
2616 
createLoading(ReferenceEntry<K, V> entry)2617     public static <K, V> DummyValueReference<K, V> createLoading(ReferenceEntry<K, V> entry) {
2618       return new DummyValueReference<K, V>(entry);
2619     }
2620 
2621     @Override
get()2622     public V get() {
2623       return value;
2624     }
2625 
2626     @Override
getWeight()2627     public int getWeight() {
2628       return 1;
2629     }
2630 
2631     @Override
getEntry()2632     public ReferenceEntry<K, V> getEntry() {
2633       return entry;
2634     }
2635 
2636     @Override
copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry)2637     public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) {
2638       return new DummyValueReference<K, V>(value, entry);
2639     }
2640 
setLoading(boolean loading)2641     public void setLoading(boolean loading) {
2642       this.loading = loading;
2643     }
2644 
2645     @Override
isLoading()2646     public boolean isLoading() {
2647       return loading;
2648     }
2649 
2650     @Override
isActive()2651     public boolean isActive() {
2652       return !loading;
2653     }
2654 
2655     @Override
waitForValue()2656     public V waitForValue() {
2657       return get();
2658     }
2659 
2660     @Override
notifyNewValue(V newValue)2661     public void notifyNewValue(V newValue) {}
2662 
clear()2663     public void clear() {
2664       value = null;
2665     }
2666   }
2667 
2668   private static class SerializableCacheLoader
2669       extends CacheLoader<Object, Object> implements Serializable {
2670     @Override
load(Object key)2671     public Object load(Object key) {
2672       return new Object();
2673     }
2674 
2675     @Override
hashCode()2676     public int hashCode() {
2677       return 42;
2678     }
2679 
2680     @Override
equals(Object o)2681     public boolean equals(Object o) {
2682       return (o instanceof SerializableCacheLoader);
2683     }
2684   }
2685 
2686   private static class SerializableRemovalListener<K, V>
2687       implements RemovalListener<K, V>, Serializable {
2688     @Override
onRemoval(RemovalNotification<K, V> notification)2689     public void onRemoval(RemovalNotification<K, V> notification) {}
2690 
2691     @Override
hashCode()2692     public int hashCode() {
2693       return 42;
2694     }
2695 
2696     @Override
equals(Object o)2697     public boolean equals(Object o) {
2698       return (o instanceof SerializableRemovalListener);
2699     }
2700   }
2701 
2702   private static class SerializableTicker extends Ticker implements Serializable {
2703     @Override
read()2704     public long read() {
2705       return 42;
2706     }
2707 
2708     @Override
hashCode()2709     public int hashCode() {
2710       return 42;
2711     }
2712 
2713     @Override
equals(Object o)2714     public boolean equals(Object o) {
2715       return (o instanceof SerializableTicker);
2716     }
2717   }
2718 
2719   private static class SerializableWeigher<K, V> implements Weigher<K, V>, Serializable {
2720     @Override
weigh(K key, V value)2721     public int weigh(K key, V value) {
2722       return 42;
2723     }
2724 
2725     @Override
hashCode()2726     public int hashCode() {
2727       return 42;
2728     }
2729 
2730     @Override
equals(Object o)2731     public boolean equals(Object o) {
2732       return (o instanceof SerializableWeigher);
2733     }
2734   }
2735 
2736 }
2737