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.collect; 18 19 import static com.google.common.collect.Lists.newArrayList; 20 import static com.google.common.collect.MapMakerInternalMap.DISCARDING_QUEUE; 21 import static com.google.common.collect.MapMakerInternalMap.DRAIN_THRESHOLD; 22 import static com.google.common.collect.MapMakerInternalMap.nullEntry; 23 import static com.google.common.collect.MapMakerInternalMap.unset; 24 import static java.util.concurrent.TimeUnit.SECONDS; 25 26 import com.google.common.base.Equivalence; 27 import com.google.common.base.Ticker; 28 import com.google.common.collect.MapMaker.RemovalCause; 29 import com.google.common.collect.MapMaker.RemovalListener; 30 import com.google.common.collect.MapMaker.RemovalNotification; 31 import com.google.common.collect.MapMakerInternalMap.EntryFactory; 32 import com.google.common.collect.MapMakerInternalMap.ReferenceEntry; 33 import com.google.common.collect.MapMakerInternalMap.Segment; 34 import com.google.common.collect.MapMakerInternalMap.Strength; 35 import com.google.common.collect.MapMakerInternalMap.ValueReference; 36 import com.google.common.testing.NullPointerTester; 37 38 import junit.framework.TestCase; 39 40 import java.lang.ref.Reference; 41 import java.lang.ref.ReferenceQueue; 42 import java.util.Iterator; 43 import java.util.LinkedHashMap; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Random; 47 import java.util.concurrent.ConcurrentLinkedQueue; 48 import java.util.concurrent.TimeUnit; 49 import java.util.concurrent.atomic.AtomicInteger; 50 import java.util.concurrent.atomic.AtomicReferenceArray; 51 52 /** 53 * @author Charles Fry 54 */ 55 @SuppressWarnings("deprecation") // many tests of deprecated methods 56 public class MapMakerInternalMapTest extends TestCase { 57 58 static final int SMALL_MAX_SIZE = DRAIN_THRESHOLD * 5; 59 60 private static <K, V> MapMakerInternalMap<K, V> makeMap(GenericMapMaker<K, V> maker) { 61 return new MapMakerInternalMap<K, V>((MapMaker) maker); 62 } 63 64 private static <K, V> MapMakerInternalMap<K, V> makeMap(MapMaker maker) { 65 return new MapMakerInternalMap<K, V>(maker); 66 } 67 68 private static MapMaker createMapMaker() { 69 MapMaker maker = new MapMaker(); 70 maker.useCustomMap = true; 71 return maker; 72 } 73 74 // constructor tests 75 76 public void testDefaults() { 77 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker()); 78 79 assertSame(Strength.STRONG, map.keyStrength); 80 assertSame(Strength.STRONG, map.valueStrength); 81 assertSame(map.keyStrength.defaultEquivalence(), map.keyEquivalence); 82 assertSame(map.valueStrength.defaultEquivalence(), map.valueEquivalence); 83 84 assertEquals(0, map.expireAfterAccessNanos); 85 assertEquals(0, map.expireAfterWriteNanos); 86 assertEquals(MapMaker.UNSET_INT, map.maximumSize); 87 88 assertSame(EntryFactory.STRONG, map.entryFactory); 89 assertSame(MapMaker.NullListener.INSTANCE, map.removalListener); 90 assertSame(DISCARDING_QUEUE, map.removalNotificationQueue); 91 assertSame(Ticker.systemTicker(), map.ticker); 92 93 assertEquals(4, map.concurrencyLevel); 94 95 // concurrency level 96 assertEquals(4, map.segments.length); 97 // initial capacity / concurrency level 98 assertEquals(16 / map.segments.length, map.segments[0].table.length()); 99 100 assertFalse(map.evictsBySize()); 101 assertFalse(map.expires()); 102 assertFalse(map.expiresAfterWrite()); 103 assertFalse(map.expiresAfterAccess()); 104 } 105 106 public void testSetKeyEquivalence() { 107 Equivalence<Object> testEquivalence = new Equivalence<Object>() { 108 @Override 109 protected boolean doEquivalent(Object a, Object b) { 110 return false; 111 } 112 113 @Override 114 protected int doHash(Object t) { 115 return 0; 116 } 117 }; 118 119 MapMakerInternalMap<Object, Object> map = 120 makeMap(createMapMaker().keyEquivalence(testEquivalence)); 121 assertSame(testEquivalence, map.keyEquivalence); 122 assertSame(map.valueStrength.defaultEquivalence(), map.valueEquivalence); 123 } 124 125 public void testSetValueEquivalence() { 126 Equivalence<Object> testEquivalence = new Equivalence<Object>() { 127 @Override 128 protected boolean doEquivalent(Object a, Object b) { 129 return false; 130 } 131 132 @Override 133 protected int doHash(Object t) { 134 return 0; 135 } 136 }; 137 138 MapMakerInternalMap<Object, Object> map = 139 makeMap(createMapMaker().valueEquivalence(testEquivalence)); 140 assertSame(testEquivalence, map.valueEquivalence); 141 assertSame(map.keyStrength.defaultEquivalence(), map.keyEquivalence); 142 } 143 144 public void testSetConcurrencyLevel() { 145 // round up to nearest power of two 146 147 checkConcurrencyLevel(1, 1); 148 checkConcurrencyLevel(2, 2); 149 checkConcurrencyLevel(3, 4); 150 checkConcurrencyLevel(4, 4); 151 checkConcurrencyLevel(5, 8); 152 checkConcurrencyLevel(6, 8); 153 checkConcurrencyLevel(7, 8); 154 checkConcurrencyLevel(8, 8); 155 } 156 157 private static void checkConcurrencyLevel(int concurrencyLevel, int segmentCount) { 158 MapMakerInternalMap<Object, Object> map = 159 makeMap(createMapMaker().concurrencyLevel(concurrencyLevel)); 160 assertEquals(segmentCount, map.segments.length); 161 } 162 163 public void testSetInitialCapacity() { 164 // share capacity over each segment, then round up to nearest power of two 165 166 checkInitialCapacity(1, 0, 1); 167 checkInitialCapacity(1, 1, 1); 168 checkInitialCapacity(1, 2, 2); 169 checkInitialCapacity(1, 3, 4); 170 checkInitialCapacity(1, 4, 4); 171 checkInitialCapacity(1, 5, 8); 172 checkInitialCapacity(1, 6, 8); 173 checkInitialCapacity(1, 7, 8); 174 checkInitialCapacity(1, 8, 8); 175 176 checkInitialCapacity(2, 0, 1); 177 checkInitialCapacity(2, 1, 1); 178 checkInitialCapacity(2, 2, 1); 179 checkInitialCapacity(2, 3, 2); 180 checkInitialCapacity(2, 4, 2); 181 checkInitialCapacity(2, 5, 4); 182 checkInitialCapacity(2, 6, 4); 183 checkInitialCapacity(2, 7, 4); 184 checkInitialCapacity(2, 8, 4); 185 186 checkInitialCapacity(4, 0, 1); 187 checkInitialCapacity(4, 1, 1); 188 checkInitialCapacity(4, 2, 1); 189 checkInitialCapacity(4, 3, 1); 190 checkInitialCapacity(4, 4, 1); 191 checkInitialCapacity(4, 5, 2); 192 checkInitialCapacity(4, 6, 2); 193 checkInitialCapacity(4, 7, 2); 194 checkInitialCapacity(4, 8, 2); 195 } 196 197 private static void checkInitialCapacity( 198 int concurrencyLevel, int initialCapacity, int segmentSize) { 199 MapMakerInternalMap<Object, Object> map = makeMap( 200 createMapMaker().concurrencyLevel(concurrencyLevel).initialCapacity(initialCapacity)); 201 for (int i = 0; i < map.segments.length; i++) { 202 assertEquals(segmentSize, map.segments[i].table.length()); 203 } 204 } 205 206 public void testSetMaximumSize() { 207 // vary maximumSize wrt concurrencyLevel 208 209 for (int maxSize = 1; maxSize < 8; maxSize++) { 210 checkMaximumSize(1, 8, maxSize); 211 checkMaximumSize(2, 8, maxSize); 212 checkMaximumSize(4, 8, maxSize); 213 checkMaximumSize(8, 8, maxSize); 214 } 215 216 checkMaximumSize(1, 8, Integer.MAX_VALUE); 217 checkMaximumSize(2, 8, Integer.MAX_VALUE); 218 checkMaximumSize(4, 8, Integer.MAX_VALUE); 219 checkMaximumSize(8, 8, Integer.MAX_VALUE); 220 221 // vary initial capacity wrt maximumSize 222 223 for (int capacity = 0; capacity < 8; capacity++) { 224 checkMaximumSize(1, capacity, 4); 225 checkMaximumSize(2, capacity, 4); 226 checkMaximumSize(4, capacity, 4); 227 checkMaximumSize(8, capacity, 4); 228 } 229 } 230 231 private static void checkMaximumSize(int concurrencyLevel, int initialCapacity, int maxSize) { 232 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker() 233 .concurrencyLevel(concurrencyLevel) 234 .initialCapacity(initialCapacity) 235 .maximumSize(maxSize)); 236 int totalCapacity = 0; 237 for (int i = 0; i < map.segments.length; i++) { 238 totalCapacity += map.segments[i].maxSegmentSize; 239 } 240 assertTrue("totalCapcity=" + totalCapacity + ", maxSize=" + maxSize, totalCapacity <= maxSize); 241 } 242 243 public void testSetWeakKeys() { 244 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker().weakKeys()); 245 checkStrength(map, Strength.WEAK, Strength.STRONG); 246 assertSame(EntryFactory.WEAK, map.entryFactory); 247 } 248 249 @SuppressWarnings("deprecation") 250 public void testSetSoftKeys() { 251 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker().softKeys()); 252 checkStrength(map, Strength.SOFT, Strength.STRONG); 253 assertSame(EntryFactory.SOFT, map.entryFactory); 254 } 255 256 public void testSetWeakValues() { 257 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker().weakValues()); 258 checkStrength(map, Strength.STRONG, Strength.WEAK); 259 assertSame(EntryFactory.STRONG, map.entryFactory); 260 } 261 262 public void testSetSoftValues() { 263 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker().softValues()); 264 checkStrength(map, Strength.STRONG, Strength.SOFT); 265 assertSame(EntryFactory.STRONG, map.entryFactory); 266 } 267 268 private static void checkStrength( 269 MapMakerInternalMap<Object, Object> map, Strength keyStrength, Strength valueStrength) { 270 assertSame(keyStrength, map.keyStrength); 271 assertSame(valueStrength, map.valueStrength); 272 assertSame(keyStrength.defaultEquivalence(), map.keyEquivalence); 273 assertSame(valueStrength.defaultEquivalence(), map.valueEquivalence); 274 } 275 276 public void testSetExpireAfterWrite() { 277 long duration = 42; 278 TimeUnit unit = SECONDS; 279 MapMakerInternalMap<Object, Object> map = 280 makeMap(createMapMaker().expireAfterWrite(duration, unit)); 281 assertEquals(unit.toNanos(duration), map.expireAfterWriteNanos); 282 } 283 284 public void testSetExpireAfterAccess() { 285 long duration = 42; 286 TimeUnit unit = SECONDS; 287 MapMakerInternalMap<Object, Object> map = 288 makeMap(createMapMaker().expireAfterAccess(duration, unit)); 289 assertEquals(unit.toNanos(duration), map.expireAfterAccessNanos); 290 } 291 292 public void testSetRemovalListener() { 293 RemovalListener<Object, Object> testListener = new RemovalListener<Object, Object>() { 294 @Override 295 public void onRemoval(RemovalNotification<Object, Object> notification) {} 296 }; 297 MapMakerInternalMap<Object, Object> map = 298 makeMap(createMapMaker().removalListener(testListener)); 299 assertSame(testListener, map.removalListener); 300 } 301 302 // Removal listener tests 303 304 public void testRemovalListener_explicit() { 305 QueuingRemovalListener<Object, Object> listener = 306 new QueuingRemovalListener<Object, Object>(); 307 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker() 308 .removalListener(listener)); 309 assertTrue(listener.isEmpty()); 310 311 Object one = new Object(); 312 Object two = new Object(); 313 Object three = new Object(); 314 Object four = new Object(); 315 Object five = new Object(); 316 Object six = new Object(); 317 318 map.put(one, two); 319 map.remove(one); 320 assertNotified(listener, one, two, RemovalCause.EXPLICIT); 321 322 map.put(two, three); 323 map.remove(two, three); 324 assertNotified(listener, two, three, RemovalCause.EXPLICIT); 325 326 map.put(three, four); 327 Iterator<?> i = map.entrySet().iterator(); 328 i.next(); 329 i.remove(); 330 assertNotified(listener, three, four, RemovalCause.EXPLICIT); 331 332 map.put(four, five); 333 i = map.keySet().iterator(); 334 i.next(); 335 i.remove(); 336 assertNotified(listener, four, five, RemovalCause.EXPLICIT); 337 338 map.put(five, six); 339 i = map.values().iterator(); 340 i.next(); 341 i.remove(); 342 assertNotified(listener, five, six, RemovalCause.EXPLICIT); 343 344 assertTrue(listener.isEmpty()); 345 } 346 347 public void testRemovalListener_replaced() { 348 QueuingRemovalListener<Object, Object> listener = 349 new QueuingRemovalListener<Object, Object>(); 350 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker() 351 .removalListener(listener)); 352 assertTrue(listener.isEmpty()); 353 354 Object one = new Object(); 355 Object two = new Object(); 356 Object three = new Object(); 357 Object four = new Object(); 358 Object five = new Object(); 359 Object six = new Object(); 360 361 map.put(one, two); 362 map.put(one, three); 363 assertNotified(listener, one, two, RemovalCause.REPLACED); 364 365 Map<Object, Object> newMap = ImmutableMap.of(one, four); 366 map.putAll(newMap); 367 assertNotified(listener, one, three, RemovalCause.REPLACED); 368 369 map.replace(one, five); 370 assertNotified(listener, one, four, RemovalCause.REPLACED); 371 372 map.replace(one, five, six); 373 assertNotified(listener, one, five, RemovalCause.REPLACED); 374 } 375 376 public void testRemovalListener_collected() { 377 QueuingRemovalListener<Object, Object> listener = 378 new QueuingRemovalListener<Object, Object>(); 379 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker() 380 .concurrencyLevel(1) 381 .softValues() 382 .removalListener(listener)); 383 Segment<Object, Object> segment = map.segments[0]; 384 assertTrue(listener.isEmpty()); 385 386 Object one = new Object(); 387 Object two = new Object(); 388 Object three = new Object(); 389 390 map.put(one, two); 391 map.put(two, three); 392 assertTrue(listener.isEmpty()); 393 394 int hash = map.hash(one); 395 ReferenceEntry<Object, Object> entry = segment.getEntry(one, hash); 396 map.reclaimValue(entry.getValueReference()); 397 assertNotified(listener, one, two, RemovalCause.COLLECTED); 398 399 assertTrue(listener.isEmpty()); 400 } 401 402 public void testRemovalListener_size() { 403 QueuingRemovalListener<Object, Object> listener = 404 new QueuingRemovalListener<Object, Object>(); 405 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker() 406 .concurrencyLevel(1) 407 .maximumSize(2) 408 .removalListener(listener)); 409 assertTrue(listener.isEmpty()); 410 411 Object one = new Object(); 412 Object two = new Object(); 413 Object three = new Object(); 414 Object four = new Object(); 415 416 map.put(one, two); 417 map.put(two, three); 418 assertTrue(listener.isEmpty()); 419 map.put(three, four); 420 assertNotified(listener, one, two, RemovalCause.SIZE); 421 422 assertTrue(listener.isEmpty()); 423 } 424 425 static <K, V> void assertNotified( 426 QueuingRemovalListener<K, V> listener, K key, V value, RemovalCause cause) { 427 RemovalNotification<K, V> notification = listener.remove(); 428 assertSame(key, notification.getKey()); 429 assertSame(value, notification.getValue()); 430 assertSame(cause, notification.getCause()); 431 } 432 433 // Segment core tests 434 435 public void testNewEntry() { 436 for (MapMaker maker : allEntryTypeMakers()) { 437 MapMakerInternalMap<Object, Object> map = makeMap(maker); 438 439 Object keyOne = new Object(); 440 Object valueOne = new Object(); 441 int hashOne = map.hash(keyOne); 442 ReferenceEntry<Object, Object> entryOne = map.newEntry(keyOne, hashOne, null); 443 ValueReference<Object, Object> valueRefOne = map.newValueReference(entryOne, valueOne); 444 assertSame(valueOne, valueRefOne.get()); 445 entryOne.setValueReference(valueRefOne); 446 447 assertSame(keyOne, entryOne.getKey()); 448 assertEquals(hashOne, entryOne.getHash()); 449 assertNull(entryOne.getNext()); 450 assertSame(valueRefOne, entryOne.getValueReference()); 451 452 Object keyTwo = new Object(); 453 Object valueTwo = new Object(); 454 int hashTwo = map.hash(keyTwo); 455 ReferenceEntry<Object, Object> entryTwo = map.newEntry(keyTwo, hashTwo, entryOne); 456 ValueReference<Object, Object> valueRefTwo = map.newValueReference(entryTwo, valueTwo); 457 assertSame(valueTwo, valueRefTwo.get()); 458 entryTwo.setValueReference(valueRefTwo); 459 460 assertSame(keyTwo, entryTwo.getKey()); 461 assertEquals(hashTwo, entryTwo.getHash()); 462 assertSame(entryOne, entryTwo.getNext()); 463 assertSame(valueRefTwo, entryTwo.getValueReference()); 464 } 465 } 466 467 public void testCopyEntry() { 468 for (MapMaker maker : allEntryTypeMakers()) { 469 MapMakerInternalMap<Object, Object> map = makeMap(maker); 470 471 Object keyOne = new Object(); 472 Object valueOne = new Object(); 473 int hashOne = map.hash(keyOne); 474 ReferenceEntry<Object, Object> entryOne = map.newEntry(keyOne, hashOne, null); 475 entryOne.setValueReference(map.newValueReference(entryOne, valueOne)); 476 477 Object keyTwo = new Object(); 478 Object valueTwo = new Object(); 479 int hashTwo = map.hash(keyTwo); 480 ReferenceEntry<Object, Object> entryTwo = map.newEntry(keyTwo, hashTwo, entryOne); 481 entryTwo.setValueReference(map.newValueReference(entryTwo, valueTwo)); 482 if (map.evictsBySize()) { 483 MapMakerInternalMap.connectEvictables(entryOne, entryTwo); 484 } 485 if (map.expires()) { 486 MapMakerInternalMap.connectExpirables(entryOne, entryTwo); 487 } 488 assertConnected(map, entryOne, entryTwo); 489 490 ReferenceEntry<Object, Object> copyOne = map.copyEntry(entryOne, null); 491 assertSame(keyOne, entryOne.getKey()); 492 assertEquals(hashOne, entryOne.getHash()); 493 assertNull(entryOne.getNext()); 494 assertSame(valueOne, copyOne.getValueReference().get()); 495 assertConnected(map, copyOne, entryTwo); 496 497 ReferenceEntry<Object, Object> copyTwo = map.copyEntry(entryTwo, copyOne); 498 assertSame(keyTwo, copyTwo.getKey()); 499 assertEquals(hashTwo, copyTwo.getHash()); 500 assertSame(copyOne, copyTwo.getNext()); 501 assertSame(valueTwo, copyTwo.getValueReference().get()); 502 assertConnected(map, copyOne, copyTwo); 503 } 504 } 505 506 private static <K, V> void assertConnected( 507 MapMakerInternalMap<K, V> map, ReferenceEntry<K, V> one, ReferenceEntry<K, V> two) { 508 if (map.evictsBySize()) { 509 assertSame(two, one.getNextEvictable()); 510 } 511 if (map.expires()) { 512 assertSame(two, one.getNextExpirable()); 513 } 514 } 515 516 public void testSegmentGetAndContains() { 517 MapMakerInternalMap<Object, Object> map = 518 makeMap(createMapMaker().concurrencyLevel(1).expireAfterAccess(99999, SECONDS)); 519 Segment<Object, Object> segment = map.segments[0]; 520 // TODO(fry): check recency ordering 521 522 Object key = new Object(); 523 int hash = map.hash(key); 524 Object value = new Object(); 525 AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; 526 int index = hash & (table.length() - 1); 527 528 ReferenceEntry<Object, Object> entry = map.newEntry(key, hash, null); 529 ValueReference<Object, Object> valueRef = map.newValueReference(entry, value); 530 entry.setValueReference(valueRef); 531 532 assertNull(segment.get(key, hash)); 533 534 // count == 0 535 table.set(index, entry); 536 assertNull(segment.get(key, hash)); 537 assertFalse(segment.containsKey(key, hash)); 538 assertFalse(segment.containsValue(value)); 539 540 // count == 1 541 segment.count++; 542 assertSame(value, segment.get(key, hash)); 543 assertTrue(segment.containsKey(key, hash)); 544 assertTrue(segment.containsValue(value)); 545 // don't see absent values now that count > 0 546 assertNull(segment.get(new Object(), hash)); 547 548 // null key 549 DummyEntry<Object, Object> nullEntry = DummyEntry.create(null, hash, entry); 550 Object nullValue = new Object(); 551 ValueReference<Object, Object> nullValueRef = map.newValueReference(nullEntry, nullValue); 552 nullEntry.setValueReference(nullValueRef); 553 table.set(index, nullEntry); 554 // skip the null key 555 assertSame(value, segment.get(key, hash)); 556 assertTrue(segment.containsKey(key, hash)); 557 assertTrue(segment.containsValue(value)); 558 assertFalse(segment.containsValue(nullValue)); 559 560 // hash collision 561 DummyEntry<Object, Object> dummy = DummyEntry.create(new Object(), hash, entry); 562 Object dummyValue = new Object(); 563 ValueReference<Object, Object> dummyValueRef = map.newValueReference(dummy, dummyValue); 564 dummy.setValueReference(dummyValueRef); 565 table.set(index, dummy); 566 assertSame(value, segment.get(key, hash)); 567 assertTrue(segment.containsKey(key, hash)); 568 assertTrue(segment.containsValue(value)); 569 assertTrue(segment.containsValue(dummyValue)); 570 571 // key collision 572 dummy = DummyEntry.create(key, hash, entry); 573 dummyValue = new Object(); 574 dummyValueRef = map.newValueReference(dummy, dummyValue); 575 dummy.setValueReference(dummyValueRef); 576 table.set(index, dummy); 577 // returns the most recent entry 578 assertSame(dummyValue, segment.get(key, hash)); 579 assertTrue(segment.containsKey(key, hash)); 580 assertTrue(segment.containsValue(value)); 581 assertTrue(segment.containsValue(dummyValue)); 582 583 // expired 584 dummy.setExpirationTime(0); 585 assertNull(segment.get(key, hash)); 586 assertFalse(segment.containsKey(key, hash)); 587 assertTrue(segment.containsValue(value)); 588 assertFalse(segment.containsValue(dummyValue)); 589 } 590 591 public void testSegmentReplaceValue() { 592 MapMakerInternalMap<Object, Object> map = 593 makeMap(createMapMaker().concurrencyLevel(1).expireAfterAccess(99999, SECONDS)); 594 Segment<Object, Object> segment = map.segments[0]; 595 // TODO(fry): check recency ordering 596 597 Object key = new Object(); 598 int hash = map.hash(key); 599 Object oldValue = new Object(); 600 Object newValue = new Object(); 601 AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; 602 int index = hash & (table.length() - 1); 603 604 DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null); 605 DummyValueReference<Object, Object> oldValueRef = DummyValueReference.create(oldValue, entry); 606 entry.setValueReference(oldValueRef); 607 608 // no entry 609 assertFalse(segment.replace(key, hash, oldValue, newValue)); 610 assertEquals(0, segment.count); 611 612 // same value 613 table.set(index, entry); 614 segment.count++; 615 assertEquals(1, segment.count); 616 assertSame(oldValue, segment.get(key, hash)); 617 assertTrue(segment.replace(key, hash, oldValue, newValue)); 618 assertEquals(1, segment.count); 619 assertSame(newValue, segment.get(key, hash)); 620 621 // different value 622 assertFalse(segment.replace(key, hash, oldValue, newValue)); 623 assertEquals(1, segment.count); 624 assertSame(newValue, segment.get(key, hash)); 625 626 // cleared 627 entry.setValueReference(oldValueRef); 628 assertSame(oldValue, segment.get(key, hash)); 629 oldValueRef.clear(null); 630 assertFalse(segment.replace(key, hash, oldValue, newValue)); 631 assertEquals(0, segment.count); 632 assertNull(segment.get(key, hash)); 633 } 634 635 public void testSegmentReplace() { 636 MapMakerInternalMap<Object, Object> map = 637 makeMap(createMapMaker().concurrencyLevel(1).expireAfterAccess(99999, SECONDS)); 638 Segment<Object, Object> segment = map.segments[0]; 639 // TODO(fry): check recency ordering 640 641 Object key = new Object(); 642 int hash = map.hash(key); 643 Object oldValue = new Object(); 644 Object newValue = new Object(); 645 AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; 646 int index = hash & (table.length() - 1); 647 648 DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null); 649 DummyValueReference<Object, Object> oldValueRef = DummyValueReference.create(oldValue, entry); 650 entry.setValueReference(oldValueRef); 651 652 // no entry 653 assertNull(segment.replace(key, hash, newValue)); 654 assertEquals(0, segment.count); 655 656 // same key 657 table.set(index, entry); 658 segment.count++; 659 assertEquals(1, segment.count); 660 assertSame(oldValue, segment.get(key, hash)); 661 assertSame(oldValue, segment.replace(key, hash, newValue)); 662 assertEquals(1, segment.count); 663 assertSame(newValue, segment.get(key, hash)); 664 665 // cleared 666 entry.setValueReference(oldValueRef); 667 assertSame(oldValue, segment.get(key, hash)); 668 oldValueRef.clear(null); 669 assertNull(segment.replace(key, hash, newValue)); 670 assertEquals(0, segment.count); 671 assertNull(segment.get(key, hash)); 672 } 673 674 public void testSegmentPut() { 675 MapMakerInternalMap<Object, Object> map = 676 makeMap(createMapMaker().concurrencyLevel(1).expireAfterAccess(99999, SECONDS)); 677 Segment<Object, Object> segment = map.segments[0]; 678 // TODO(fry): check recency ordering 679 680 Object key = new Object(); 681 int hash = map.hash(key); 682 Object oldValue = new Object(); 683 Object newValue = new Object(); 684 685 // no entry 686 assertEquals(0, segment.count); 687 assertNull(segment.put(key, hash, oldValue, false)); 688 assertEquals(1, segment.count); 689 690 // same key 691 assertSame(oldValue, segment.put(key, hash, newValue, false)); 692 assertEquals(1, segment.count); 693 assertSame(newValue, segment.get(key, hash)); 694 695 // cleared 696 ReferenceEntry<Object, Object> entry = segment.getEntry(key, hash); 697 DummyValueReference<Object, Object> oldValueRef = DummyValueReference.create(oldValue, entry); 698 entry.setValueReference(oldValueRef); 699 assertSame(oldValue, segment.get(key, hash)); 700 oldValueRef.clear(null); 701 assertNull(segment.put(key, hash, newValue, false)); 702 assertEquals(1, segment.count); 703 assertSame(newValue, segment.get(key, hash)); 704 } 705 706 public void testSegmentPutIfAbsent() { 707 MapMakerInternalMap<Object, Object> map = 708 makeMap(createMapMaker().concurrencyLevel(1).expireAfterAccess(99999, SECONDS)); 709 Segment<Object, Object> segment = map.segments[0]; 710 // TODO(fry): check recency ordering 711 712 Object key = new Object(); 713 int hash = map.hash(key); 714 Object oldValue = new Object(); 715 Object newValue = new Object(); 716 717 // no entry 718 assertEquals(0, segment.count); 719 assertNull(segment.put(key, hash, oldValue, true)); 720 assertEquals(1, segment.count); 721 722 // same key 723 assertSame(oldValue, segment.put(key, hash, newValue, true)); 724 assertEquals(1, segment.count); 725 assertSame(oldValue, segment.get(key, hash)); 726 727 // cleared 728 ReferenceEntry<Object, Object> entry = segment.getEntry(key, hash); 729 DummyValueReference<Object, Object> oldValueRef = DummyValueReference.create(oldValue, entry); 730 entry.setValueReference(oldValueRef); 731 assertSame(oldValue, segment.get(key, hash)); 732 oldValueRef.clear(null); 733 assertNull(segment.put(key, hash, newValue, true)); 734 assertEquals(1, segment.count); 735 assertSame(newValue, segment.get(key, hash)); 736 } 737 738 public void testSegmentPut_expand() { 739 MapMakerInternalMap<Object, Object> map = 740 makeMap(createMapMaker().concurrencyLevel(1).initialCapacity(1)); 741 Segment<Object, Object> segment = map.segments[0]; 742 assertEquals(1, segment.table.length()); 743 744 int count = 1024; 745 for (int i = 0; i < count; i++) { 746 Object key = new Object(); 747 Object value = new Object(); 748 int hash = map.hash(key); 749 assertNull(segment.put(key, hash, value, false)); 750 assertTrue(segment.table.length() > i); 751 } 752 } 753 754 public void testSegmentPut_evict() { 755 int maxSize = 10; 756 MapMakerInternalMap<Object, Object> map = 757 makeMap(createMapMaker().concurrencyLevel(1).maximumSize(maxSize)); 758 759 // manually add elements to avoid eviction 760 int originalCount = 1024; 761 LinkedHashMap<Object, Object> originalMap = Maps.newLinkedHashMap(); 762 for (int i = 0; i < originalCount; i++) { 763 Object key = new Object(); 764 Object value = new Object(); 765 map.put(key, value); 766 originalMap.put(key, value); 767 if (i >= maxSize) { 768 Iterator<Object> it = originalMap.keySet().iterator(); 769 it.next(); 770 it.remove(); 771 } 772 assertEquals(originalMap, map); 773 } 774 } 775 776 public void testSegmentRemove() { 777 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker().concurrencyLevel(1)); 778 Segment<Object, Object> segment = map.segments[0]; 779 780 Object key = new Object(); 781 int hash = map.hash(key); 782 Object oldValue = new Object(); 783 AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; 784 int index = hash & (table.length() - 1); 785 786 DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null); 787 DummyValueReference<Object, Object> oldValueRef = DummyValueReference.create(oldValue, entry); 788 entry.setValueReference(oldValueRef); 789 790 // no entry 791 assertEquals(0, segment.count); 792 assertNull(segment.remove(key, hash)); 793 assertEquals(0, segment.count); 794 795 // same key 796 table.set(index, entry); 797 segment.count++; 798 assertEquals(1, segment.count); 799 assertSame(oldValue, segment.get(key, hash)); 800 assertSame(oldValue, segment.remove(key, hash)); 801 assertEquals(0, segment.count); 802 assertNull(segment.get(key, hash)); 803 804 // cleared 805 table.set(index, entry); 806 segment.count++; 807 assertEquals(1, segment.count); 808 assertSame(oldValue, segment.get(key, hash)); 809 oldValueRef.clear(null); 810 assertNull(segment.remove(key, hash)); 811 assertEquals(0, segment.count); 812 assertNull(segment.get(key, hash)); 813 } 814 815 public void testSegmentRemoveValue() { 816 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker().concurrencyLevel(1)); 817 Segment<Object, Object> segment = map.segments[0]; 818 819 Object key = new Object(); 820 int hash = map.hash(key); 821 Object oldValue = new Object(); 822 Object newValue = new Object(); 823 AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; 824 int index = hash & (table.length() - 1); 825 826 DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null); 827 DummyValueReference<Object, Object> oldValueRef = DummyValueReference.create(oldValue, entry); 828 entry.setValueReference(oldValueRef); 829 830 // no entry 831 assertEquals(0, segment.count); 832 assertNull(segment.remove(key, hash)); 833 assertEquals(0, segment.count); 834 835 // same value 836 table.set(index, entry); 837 segment.count++; 838 assertEquals(1, segment.count); 839 assertSame(oldValue, segment.get(key, hash)); 840 assertTrue(segment.remove(key, hash, oldValue)); 841 assertEquals(0, segment.count); 842 assertNull(segment.get(key, hash)); 843 844 // different value 845 table.set(index, entry); 846 segment.count++; 847 assertEquals(1, segment.count); 848 assertSame(oldValue, segment.get(key, hash)); 849 assertFalse(segment.remove(key, hash, newValue)); 850 assertEquals(1, segment.count); 851 assertSame(oldValue, segment.get(key, hash)); 852 853 // cleared 854 assertSame(oldValue, segment.get(key, hash)); 855 oldValueRef.clear(null); 856 assertFalse(segment.remove(key, hash, oldValue)); 857 assertEquals(0, segment.count); 858 assertNull(segment.get(key, hash)); 859 } 860 861 public void testExpand() { 862 MapMakerInternalMap<Object, Object> map = 863 makeMap(createMapMaker().concurrencyLevel(1).initialCapacity(1)); 864 Segment<Object, Object> segment = map.segments[0]; 865 assertEquals(1, segment.table.length()); 866 867 // manually add elements to avoid expansion 868 int originalCount = 1024; 869 ReferenceEntry<Object, Object> entry = null; 870 for (int i = 0; i < originalCount; i++) { 871 Object key = new Object(); 872 Object value = new Object(); 873 int hash = map.hash(key); 874 // chain all entries together as we only have a single bucket 875 entry = map.newEntry(key, hash, entry); 876 ValueReference<Object, Object> valueRef = map.newValueReference(entry, value); 877 entry.setValueReference(valueRef); 878 } 879 segment.table.set(0, entry); 880 segment.count = originalCount; 881 ImmutableMap<Object, Object> originalMap = ImmutableMap.copyOf(map); 882 assertEquals(originalCount, originalMap.size()); 883 assertEquals(originalMap, map); 884 885 for (int i = 1; i <= originalCount * 2; i *= 2) { 886 if (i > 1) { 887 segment.expand(); 888 } 889 assertEquals(i, segment.table.length()); 890 assertEquals(originalCount, countLiveEntries(map)); 891 assertEquals(originalCount, segment.count); 892 assertEquals(originalMap, map); 893 } 894 } 895 896 public void testReclaimKey() { 897 CountingRemovalListener<Object, Object> listener = 898 new CountingRemovalListener<Object, Object>(); 899 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker() 900 .concurrencyLevel(1) 901 .initialCapacity(1) 902 .maximumSize(SMALL_MAX_SIZE) 903 .expireAfterWrite(99999, SECONDS) 904 .removalListener(listener)); 905 Segment<Object, Object> segment = map.segments[0]; 906 AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; 907 assertEquals(1, table.length()); 908 909 // create 3 objects and chain them together 910 Object keyOne = new Object(); 911 Object valueOne = new Object(); 912 int hashOne = map.hash(keyOne); 913 DummyEntry<Object, Object> entryOne = createDummyEntry(keyOne, hashOne, valueOne, null); 914 Object keyTwo = new Object(); 915 Object valueTwo = new Object(); 916 int hashTwo = map.hash(keyTwo); 917 DummyEntry<Object, Object> entryTwo = createDummyEntry(keyTwo, hashTwo, valueTwo, entryOne); 918 Object keyThree = new Object(); 919 Object valueThree = new Object(); 920 int hashThree = map.hash(keyThree); 921 DummyEntry<Object, Object> entryThree = 922 createDummyEntry(keyThree, hashThree, valueThree, entryTwo); 923 924 // absent 925 assertEquals(0, listener.getCount()); 926 assertFalse(segment.reclaimKey(entryOne, hashOne)); 927 assertEquals(0, listener.getCount()); 928 table.set(0, entryOne); 929 assertFalse(segment.reclaimKey(entryTwo, hashTwo)); 930 assertEquals(0, listener.getCount()); 931 table.set(0, entryTwo); 932 assertFalse(segment.reclaimKey(entryThree, hashThree)); 933 assertEquals(0, listener.getCount()); 934 935 // present 936 table.set(0, entryOne); 937 segment.count = 1; 938 assertTrue(segment.reclaimKey(entryOne, hashOne)); 939 assertEquals(1, listener.getCount()); 940 assertSame(keyOne, listener.getLastEvictedKey()); 941 assertSame(valueOne, listener.getLastEvictedValue()); 942 assertTrue(map.removalNotificationQueue.isEmpty()); 943 assertFalse(segment.evictionQueue.contains(entryOne)); 944 assertFalse(segment.expirationQueue.contains(entryOne)); 945 assertEquals(0, segment.count); 946 assertNull(table.get(0)); 947 } 948 949 public void testRemoveFromChain() { 950 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker().concurrencyLevel(1)); 951 Segment<Object, Object> segment = map.segments[0]; 952 953 // create 3 objects and chain them together 954 Object keyOne = new Object(); 955 Object valueOne = new Object(); 956 int hashOne = map.hash(keyOne); 957 DummyEntry<Object, Object> entryOne = createDummyEntry(keyOne, hashOne, valueOne, null); 958 Object keyTwo = new Object(); 959 Object valueTwo = new Object(); 960 int hashTwo = map.hash(keyTwo); 961 DummyEntry<Object, Object> entryTwo = createDummyEntry(keyTwo, hashTwo, valueTwo, entryOne); 962 Object keyThree = new Object(); 963 Object valueThree = new Object(); 964 int hashThree = map.hash(keyThree); 965 DummyEntry<Object, Object> entryThree = 966 createDummyEntry(keyThree, hashThree, valueThree, entryTwo); 967 968 // alone 969 assertNull(segment.removeFromChain(entryOne, entryOne)); 970 971 // head 972 assertSame(entryOne, segment.removeFromChain(entryTwo, entryTwo)); 973 974 // middle 975 ReferenceEntry<Object, Object> newFirst = segment.removeFromChain(entryThree, entryTwo); 976 assertSame(keyThree, newFirst.getKey()); 977 assertSame(valueThree, newFirst.getValueReference().get()); 978 assertEquals(hashThree, newFirst.getHash()); 979 assertSame(entryOne, newFirst.getNext()); 980 981 // tail (remaining entries are copied in reverse order) 982 newFirst = segment.removeFromChain(entryThree, entryOne); 983 assertSame(keyTwo, newFirst.getKey()); 984 assertSame(valueTwo, newFirst.getValueReference().get()); 985 assertEquals(hashTwo, newFirst.getHash()); 986 newFirst = newFirst.getNext(); 987 assertSame(keyThree, newFirst.getKey()); 988 assertSame(valueThree, newFirst.getValueReference().get()); 989 assertEquals(hashThree, newFirst.getHash()); 990 assertNull(newFirst.getNext()); 991 } 992 993 public void testExpand_cleanup() { 994 MapMakerInternalMap<Object, Object> map = 995 makeMap(createMapMaker().concurrencyLevel(1).initialCapacity(1)); 996 Segment<Object, Object> segment = map.segments[0]; 997 assertEquals(1, segment.table.length()); 998 999 // manually add elements to avoid expansion 1000 // 1/3 null keys, 1/3 null values 1001 int originalCount = 1024; 1002 ReferenceEntry<Object, Object> entry = null; 1003 for (int i = 0; i < originalCount; i++) { 1004 Object key = new Object(); 1005 Object value = (i % 3 == 0) ? null : new Object(); 1006 int hash = map.hash(key); 1007 if (i % 3 == 1) { 1008 key = null; 1009 } 1010 // chain all entries together as we only have a single bucket 1011 entry = DummyEntry.create(key, hash, entry); 1012 ValueReference<Object, Object> valueRef = DummyValueReference.create(value, entry); 1013 entry.setValueReference(valueRef); 1014 } 1015 segment.table.set(0, entry); 1016 segment.count = originalCount; 1017 int liveCount = originalCount / 3; 1018 assertEquals(1, segment.table.length()); 1019 assertEquals(liveCount, countLiveEntries(map)); 1020 ImmutableMap<Object, Object> originalMap = ImmutableMap.copyOf(map); 1021 assertEquals(liveCount, originalMap.size()); 1022 // can't compare map contents until cleanup occurs 1023 1024 for (int i = 1; i <= originalCount * 2; i *= 2) { 1025 if (i > 1) { 1026 segment.expand(); 1027 } 1028 assertEquals(i, segment.table.length()); 1029 assertEquals(liveCount, countLiveEntries(map)); 1030 // expansion cleanup is sloppy, with a goal of avoiding unnecessary copies 1031 assertTrue(segment.count >= liveCount); 1032 assertTrue(segment.count <= originalCount); 1033 assertEquals(originalMap, ImmutableMap.copyOf(map)); 1034 } 1035 } 1036 1037 private static <K, V> int countLiveEntries(MapMakerInternalMap<K, V> map) { 1038 int result = 0; 1039 for (Segment<K, V> segment : map.segments) { 1040 AtomicReferenceArray<ReferenceEntry<K, V>> table = segment.table; 1041 for (int i = 0; i < table.length(); i++) { 1042 for (ReferenceEntry<K, V> e = table.get(i); e != null; e = e.getNext()) { 1043 if (map.isLive(e)) { 1044 result++; 1045 } 1046 } 1047 } 1048 } 1049 return result; 1050 } 1051 1052 public void testClear() { 1053 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker() 1054 .concurrencyLevel(1) 1055 .initialCapacity(1) 1056 .maximumSize(SMALL_MAX_SIZE) 1057 .expireAfterWrite(99999, SECONDS)); 1058 Segment<Object, Object> segment = map.segments[0]; 1059 AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; 1060 assertEquals(1, table.length()); 1061 1062 Object key = new Object(); 1063 Object value = new Object(); 1064 int hash = map.hash(key); 1065 DummyEntry<Object, Object> entry = createDummyEntry(key, hash, value, null); 1066 segment.recordWrite(entry); 1067 segment.table.set(0, entry); 1068 segment.readCount.incrementAndGet(); 1069 segment.count = 1; 1070 1071 assertSame(entry, table.get(0)); 1072 assertSame(entry, segment.evictionQueue.peek()); 1073 assertSame(entry, segment.expirationQueue.peek()); 1074 1075 segment.clear(); 1076 assertNull(table.get(0)); 1077 assertTrue(segment.evictionQueue.isEmpty()); 1078 assertTrue(segment.expirationQueue.isEmpty()); 1079 assertEquals(0, segment.readCount.get()); 1080 assertEquals(0, segment.count); 1081 } 1082 1083 public void testRemoveEntry() { 1084 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker() 1085 .concurrencyLevel(1) 1086 .initialCapacity(1) 1087 .maximumSize(SMALL_MAX_SIZE) 1088 .expireAfterWrite(99999, SECONDS) 1089 .removalListener(new CountingRemovalListener<Object, Object>())); 1090 Segment<Object, Object> segment = map.segments[0]; 1091 AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; 1092 assertEquals(1, table.length()); 1093 1094 Object key = new Object(); 1095 Object value = new Object(); 1096 int hash = map.hash(key); 1097 DummyEntry<Object, Object> entry = createDummyEntry(key, hash, value, null); 1098 1099 // remove absent 1100 assertFalse(segment.removeEntry(entry, hash, RemovalCause.COLLECTED)); 1101 1102 // remove live 1103 segment.recordWrite(entry); 1104 table.set(0, entry); 1105 segment.count = 1; 1106 assertTrue(segment.removeEntry(entry, hash, RemovalCause.COLLECTED)); 1107 assertNotificationEnqueued(map, key, value, hash); 1108 assertTrue(map.removalNotificationQueue.isEmpty()); 1109 assertFalse(segment.evictionQueue.contains(entry)); 1110 assertFalse(segment.expirationQueue.contains(entry)); 1111 assertEquals(0, segment.count); 1112 assertNull(table.get(0)); 1113 } 1114 1115 public void testReclaimValue() { 1116 CountingRemovalListener<Object, Object> listener = 1117 new CountingRemovalListener<Object, Object>(); 1118 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker() 1119 .concurrencyLevel(1) 1120 .initialCapacity(1) 1121 .maximumSize(SMALL_MAX_SIZE) 1122 .expireAfterWrite(99999, SECONDS) 1123 .removalListener(listener)); 1124 Segment<Object, Object> segment = map.segments[0]; 1125 AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; 1126 assertEquals(1, table.length()); 1127 1128 Object key = new Object(); 1129 Object value = new Object(); 1130 int hash = map.hash(key); 1131 DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null); 1132 DummyValueReference<Object, Object> valueRef = DummyValueReference.create(value, entry); 1133 entry.setValueReference(valueRef); 1134 1135 // reclaim absent 1136 assertFalse(segment.reclaimValue(key, hash, valueRef)); 1137 1138 // reclaim live 1139 segment.recordWrite(entry); 1140 table.set(0, entry); 1141 segment.count = 1; 1142 assertTrue(segment.reclaimValue(key, hash, valueRef)); 1143 assertEquals(1, listener.getCount()); 1144 assertSame(key, listener.getLastEvictedKey()); 1145 assertSame(value, listener.getLastEvictedValue()); 1146 assertTrue(map.removalNotificationQueue.isEmpty()); 1147 assertFalse(segment.evictionQueue.contains(entry)); 1148 assertFalse(segment.expirationQueue.contains(entry)); 1149 assertEquals(0, segment.count); 1150 assertNull(table.get(0)); 1151 1152 // reclaim wrong value reference 1153 table.set(0, entry); 1154 DummyValueReference<Object, Object> otherValueRef = DummyValueReference.create(value, entry); 1155 entry.setValueReference(otherValueRef); 1156 assertFalse(segment.reclaimValue(key, hash, valueRef)); 1157 assertEquals(1, listener.getCount()); 1158 assertTrue(segment.reclaimValue(key, hash, otherValueRef)); 1159 assertEquals(2, listener.getCount()); 1160 assertSame(key, listener.getLastEvictedKey()); 1161 assertSame(value, listener.getLastEvictedValue()); 1162 } 1163 1164 public void testClearValue() { 1165 MapMakerInternalMap<Object, Object> map = makeMap(createMapMaker() 1166 .concurrencyLevel(1) 1167 .initialCapacity(1) 1168 .maximumSize(SMALL_MAX_SIZE) 1169 .expireAfterWrite(99999, SECONDS) 1170 .removalListener(new CountingRemovalListener<Object, Object>())); 1171 Segment<Object, Object> segment = map.segments[0]; 1172 AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; 1173 assertEquals(1, table.length()); 1174 1175 Object key = new Object(); 1176 Object value = new Object(); 1177 int hash = map.hash(key); 1178 DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null); 1179 DummyValueReference<Object, Object> valueRef = DummyValueReference.create(value, entry); 1180 entry.setValueReference(valueRef); 1181 1182 // clear absent 1183 assertFalse(segment.clearValue(key, hash, valueRef)); 1184 1185 // clear live 1186 segment.recordWrite(entry); 1187 table.set(0, entry); 1188 // don't increment count; this is used during computation 1189 assertTrue(segment.clearValue(key, hash, valueRef)); 1190 // no notification sent with clearValue 1191 assertTrue(map.removalNotificationQueue.isEmpty()); 1192 assertFalse(segment.evictionQueue.contains(entry)); 1193 assertFalse(segment.expirationQueue.contains(entry)); 1194 assertEquals(0, segment.count); 1195 assertNull(table.get(0)); 1196 1197 // clear wrong value reference 1198 table.set(0, entry); 1199 DummyValueReference<Object, Object> otherValueRef = DummyValueReference.create(value, entry); 1200 entry.setValueReference(otherValueRef); 1201 assertFalse(segment.clearValue(key, hash, valueRef)); 1202 entry.setValueReference(valueRef); 1203 assertTrue(segment.clearValue(key, hash, valueRef)); 1204 } 1205 1206 private static <K, V> void assertNotificationEnqueued( 1207 MapMakerInternalMap<K, V> map, K key, V value, int hash) { 1208 RemovalNotification<K, V> notification = map.removalNotificationQueue.poll(); 1209 assertSame(key, notification.getKey()); 1210 assertSame(value, notification.getValue()); 1211 } 1212 1213 // Segment eviction tests 1214 1215 public void testDrainRecencyQueueOnWrite() { 1216 for (MapMaker maker : allEvictingMakers()) { 1217 MapMakerInternalMap<Object, Object> map = makeMap(maker.concurrencyLevel(1)); 1218 Segment<Object, Object> segment = map.segments[0]; 1219 1220 if (segment.recencyQueue != DISCARDING_QUEUE) { 1221 Object keyOne = new Object(); 1222 Object valueOne = new Object(); 1223 Object keyTwo = new Object(); 1224 Object valueTwo = new Object(); 1225 1226 map.put(keyOne, valueOne); 1227 assertTrue(segment.recencyQueue.isEmpty()); 1228 1229 for (int i = 0; i < DRAIN_THRESHOLD / 2; i++) { 1230 map.get(keyOne); 1231 } 1232 assertFalse(segment.recencyQueue.isEmpty()); 1233 1234 map.put(keyTwo, valueTwo); 1235 assertTrue(segment.recencyQueue.isEmpty()); 1236 } 1237 } 1238 } 1239 1240 public void testDrainRecencyQueueOnRead() { 1241 for (MapMaker maker : allEvictingMakers()) { 1242 MapMakerInternalMap<Object, Object> map = makeMap(maker.concurrencyLevel(1)); 1243 Segment<Object, Object> segment = map.segments[0]; 1244 1245 if (segment.recencyQueue != DISCARDING_QUEUE) { 1246 Object keyOne = new Object(); 1247 Object valueOne = new Object(); 1248 1249 // repeated get of the same key 1250 1251 map.put(keyOne, valueOne); 1252 assertTrue(segment.recencyQueue.isEmpty()); 1253 1254 for (int i = 0; i < DRAIN_THRESHOLD / 2; i++) { 1255 map.get(keyOne); 1256 } 1257 assertFalse(segment.recencyQueue.isEmpty()); 1258 1259 for (int i = 0; i < DRAIN_THRESHOLD * 2; i++) { 1260 map.get(keyOne); 1261 assertTrue(segment.recencyQueue.size() <= DRAIN_THRESHOLD); 1262 } 1263 1264 // get over many different keys 1265 1266 for (int i = 0; i < DRAIN_THRESHOLD * 2; i++) { 1267 map.put(new Object(), new Object()); 1268 } 1269 assertTrue(segment.recencyQueue.isEmpty()); 1270 1271 for (int i = 0; i < DRAIN_THRESHOLD / 2; i++) { 1272 map.get(keyOne); 1273 } 1274 assertFalse(segment.recencyQueue.isEmpty()); 1275 1276 for (Object key : map.keySet()) { 1277 map.get(key); 1278 assertTrue(segment.recencyQueue.size() <= DRAIN_THRESHOLD); 1279 } 1280 } 1281 } 1282 } 1283 1284 public void testRecordRead() { 1285 for (MapMaker maker : allEvictingMakers()) { 1286 MapMakerInternalMap<Object, Object> map = makeMap(maker.concurrencyLevel(1)); 1287 Segment<Object, Object> segment = map.segments[0]; 1288 List<ReferenceEntry<Object, Object>> writeOrder = Lists.newLinkedList(); 1289 List<ReferenceEntry<Object, Object>> readOrder = Lists.newLinkedList(); 1290 for (int i = 0; i < DRAIN_THRESHOLD * 2; i++) { 1291 Object key = new Object(); 1292 int hash = map.hash(key); 1293 Object value = new Object(); 1294 1295 ReferenceEntry<Object, Object> entry = createDummyEntry(key, hash, value, null); 1296 // must recordRead for drainRecencyQueue to believe this entry is live 1297 segment.recordWrite(entry); 1298 writeOrder.add(entry); 1299 readOrder.add(entry); 1300 } 1301 1302 checkEvictionQueues(map, segment, readOrder, writeOrder); 1303 checkExpirationTimes(map); 1304 1305 // access some of the elements 1306 Random random = new Random(); 1307 List<ReferenceEntry<Object, Object>> reads = Lists.newArrayList(); 1308 Iterator<ReferenceEntry<Object, Object>> i = readOrder.iterator(); 1309 while (i.hasNext()) { 1310 ReferenceEntry<Object, Object> entry = i.next(); 1311 if (random.nextBoolean()) { 1312 segment.recordRead(entry); 1313 reads.add(entry); 1314 i.remove(); 1315 } 1316 } 1317 checkAndDrainRecencyQueue(map, segment, reads); 1318 readOrder.addAll(reads); 1319 1320 checkEvictionQueues(map, segment, readOrder, writeOrder); 1321 checkExpirationTimes(map); 1322 } 1323 } 1324 1325 public void testRecordReadOnGet() { 1326 for (MapMaker maker : allEvictingMakers()) { 1327 MapMakerInternalMap<Object, Object> map = makeMap(maker.concurrencyLevel(1)); 1328 Segment<Object, Object> segment = map.segments[0]; 1329 List<ReferenceEntry<Object, Object>> writeOrder = Lists.newLinkedList(); 1330 List<ReferenceEntry<Object, Object>> readOrder = Lists.newLinkedList(); 1331 for (int i = 0; i < DRAIN_THRESHOLD * 2; i++) { 1332 Object key = new Object(); 1333 int hash = map.hash(key); 1334 Object value = new Object(); 1335 1336 map.put(key, value); 1337 ReferenceEntry<Object, Object> entry = segment.getEntry(key, hash); 1338 writeOrder.add(entry); 1339 readOrder.add(entry); 1340 } 1341 1342 checkEvictionQueues(map, segment, readOrder, writeOrder); 1343 checkExpirationTimes(map); 1344 assertTrue(segment.recencyQueue.isEmpty()); 1345 1346 // access some of the elements 1347 Random random = new Random(); 1348 List<ReferenceEntry<Object, Object>> reads = Lists.newArrayList(); 1349 Iterator<ReferenceEntry<Object, Object>> i = readOrder.iterator(); 1350 while (i.hasNext()) { 1351 ReferenceEntry<Object, Object> entry = i.next(); 1352 if (random.nextBoolean()) { 1353 map.get(entry.getKey()); 1354 reads.add(entry); 1355 i.remove(); 1356 assertTrue(segment.recencyQueue.size() <= DRAIN_THRESHOLD); 1357 } 1358 } 1359 int undrainedIndex = reads.size() - segment.recencyQueue.size(); 1360 checkAndDrainRecencyQueue(map, segment, reads.subList(undrainedIndex, reads.size())); 1361 readOrder.addAll(reads); 1362 1363 checkEvictionQueues(map, segment, readOrder, writeOrder); 1364 checkExpirationTimes(map); 1365 } 1366 } 1367 1368 public void testRecordWrite() { 1369 for (MapMaker maker : allEvictingMakers()) { 1370 MapMakerInternalMap<Object, Object> map = makeMap(maker.concurrencyLevel(1)); 1371 Segment<Object, Object> segment = map.segments[0]; 1372 List<ReferenceEntry<Object, Object>> writeOrder = Lists.newLinkedList(); 1373 for (int i = 0; i < DRAIN_THRESHOLD * 2; i++) { 1374 Object key = new Object(); 1375 int hash = map.hash(key); 1376 Object value = new Object(); 1377 1378 ReferenceEntry<Object, Object> entry = createDummyEntry(key, hash, value, null); 1379 // must recordRead for drainRecencyQueue to believe this entry is live 1380 segment.recordWrite(entry); 1381 writeOrder.add(entry); 1382 } 1383 1384 checkEvictionQueues(map, segment, writeOrder, writeOrder); 1385 checkExpirationTimes(map); 1386 1387 // access some of the elements 1388 Random random = new Random(); 1389 List<ReferenceEntry<Object, Object>> writes = Lists.newArrayList(); 1390 Iterator<ReferenceEntry<Object, Object>> i = writeOrder.iterator(); 1391 while (i.hasNext()) { 1392 ReferenceEntry<Object, Object> entry = i.next(); 1393 if (random.nextBoolean()) { 1394 segment.recordWrite(entry); 1395 writes.add(entry); 1396 i.remove(); 1397 } 1398 } 1399 writeOrder.addAll(writes); 1400 1401 checkEvictionQueues(map, segment, writeOrder, writeOrder); 1402 checkExpirationTimes(map); 1403 } 1404 } 1405 1406 static <K, V> void checkAndDrainRecencyQueue(MapMakerInternalMap<K, V> map, 1407 Segment<K, V> segment, List<ReferenceEntry<K, V>> reads) { 1408 if (map.evictsBySize() || map.expiresAfterAccess()) { 1409 assertSameEntries(reads, ImmutableList.copyOf(segment.recencyQueue)); 1410 } 1411 segment.drainRecencyQueue(); 1412 } 1413 1414 static <K, V> void checkEvictionQueues(MapMakerInternalMap<K, V> map, 1415 Segment<K, V> segment, List<ReferenceEntry<K, V>> readOrder, 1416 List<ReferenceEntry<K, V>> writeOrder) { 1417 if (map.evictsBySize()) { 1418 assertSameEntries(readOrder, ImmutableList.copyOf(segment.evictionQueue)); 1419 } 1420 if (map.expiresAfterAccess()) { 1421 assertSameEntries(readOrder, ImmutableList.copyOf(segment.expirationQueue)); 1422 } 1423 if (map.expiresAfterWrite()) { 1424 assertSameEntries(writeOrder, ImmutableList.copyOf(segment.expirationQueue)); 1425 } 1426 } 1427 1428 private static <K, V> void assertSameEntries(List<ReferenceEntry<K, V>> expectedEntries, 1429 List<ReferenceEntry<K, V>> actualEntries) { 1430 int size = expectedEntries.size(); 1431 assertEquals(size, actualEntries.size()); 1432 for (int i = 0; i < size; i++) { 1433 ReferenceEntry<K, V> expectedEntry = expectedEntries.get(0); 1434 ReferenceEntry<K, V> actualEntry = actualEntries.get(0); 1435 assertSame(expectedEntry.getKey(), actualEntry.getKey()); 1436 assertSame(expectedEntry.getValueReference().get(), actualEntry.getValueReference().get()); 1437 } 1438 } 1439 1440 static <K, V> void checkExpirationTimes(MapMakerInternalMap<K, V> map) { 1441 if (!map.expires()) { 1442 return; 1443 } 1444 1445 for (Segment<K, V> segment : map.segments) { 1446 long lastExpirationTime = 0; 1447 for (ReferenceEntry<K, V> e : segment.recencyQueue) { 1448 long expirationTime = e.getExpirationTime(); 1449 assertTrue(expirationTime >= lastExpirationTime); 1450 lastExpirationTime = expirationTime; 1451 } 1452 1453 lastExpirationTime = 0; 1454 for (ReferenceEntry<K, V> e : segment.expirationQueue) { 1455 long expirationTime = e.getExpirationTime(); 1456 assertTrue(expirationTime >= lastExpirationTime); 1457 lastExpirationTime = expirationTime; 1458 } 1459 } 1460 } 1461 1462 public void testEvictEntries() { 1463 int maxSize = 10; 1464 MapMakerInternalMap<Object, Object> map = 1465 makeMap(createMapMaker().concurrencyLevel(1).maximumSize(maxSize)); 1466 Segment<Object, Object> segment = map.segments[0]; 1467 1468 // manually add elements to avoid eviction 1469 int originalCount = 1024; 1470 ReferenceEntry<Object, Object> entry = null; 1471 LinkedHashMap<Object, Object> originalMap = Maps.newLinkedHashMap(); 1472 for (int i = 0; i < originalCount; i++) { 1473 Object key = new Object(); 1474 Object value = new Object(); 1475 AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; 1476 int hash = map.hash(key); 1477 int index = hash & (table.length() - 1); 1478 ReferenceEntry<Object, Object> first = table.get(index); 1479 entry = map.newEntry(key, hash, first); 1480 ValueReference<Object, Object> valueRef = map.newValueReference(entry, value); 1481 entry.setValueReference(valueRef); 1482 segment.recordWrite(entry); 1483 table.set(index, entry); 1484 originalMap.put(key, value); 1485 } 1486 segment.count = originalCount; 1487 assertEquals(originalCount, originalMap.size()); 1488 assertEquals(originalMap, map); 1489 1490 for (int i = maxSize - 1; i < originalCount; i++) { 1491 assertTrue(segment.evictEntries()); 1492 Iterator<Object> it = originalMap.keySet().iterator(); 1493 it.next(); 1494 it.remove(); 1495 assertEquals(originalMap, map); 1496 } 1497 assertFalse(segment.evictEntries()); 1498 } 1499 1500 // reference queues 1501 1502 public void testDrainKeyReferenceQueueOnWrite() { 1503 for (MapMaker maker : allKeyValueStrengthMakers()) { 1504 MapMakerInternalMap<Object, Object> map = 1505 makeMap(maker.concurrencyLevel(1)); 1506 if (map.usesKeyReferences()) { 1507 Segment<Object, Object> segment = map.segments[0]; 1508 1509 Object keyOne = new Object(); 1510 int hashOne = map.hash(keyOne); 1511 Object valueOne = new Object(); 1512 Object keyTwo = new Object(); 1513 Object valueTwo = new Object(); 1514 1515 map.put(keyOne, valueOne); 1516 ReferenceEntry<Object, Object> entry = segment.getEntry(keyOne, hashOne); 1517 1518 @SuppressWarnings("unchecked") 1519 Reference<Object> reference = (Reference) entry; 1520 reference.enqueue(); 1521 1522 map.put(keyTwo, valueTwo); 1523 assertFalse(map.containsKey(keyOne)); 1524 assertFalse(map.containsValue(valueOne)); 1525 assertNull(map.get(keyOne)); 1526 assertEquals(1, map.size()); 1527 assertNull(segment.keyReferenceQueue.poll()); 1528 } 1529 } 1530 } 1531 1532 public void testDrainValueReferenceQueueOnWrite() { 1533 for (MapMaker maker : allKeyValueStrengthMakers()) { 1534 MapMakerInternalMap<Object, Object> map = 1535 makeMap(maker.concurrencyLevel(1)); 1536 if (map.usesValueReferences()) { 1537 Segment<Object, Object> segment = map.segments[0]; 1538 1539 Object keyOne = new Object(); 1540 int hashOne = map.hash(keyOne); 1541 Object valueOne = new Object(); 1542 Object keyTwo = new Object(); 1543 Object valueTwo = new Object(); 1544 1545 map.put(keyOne, valueOne); 1546 ReferenceEntry<Object, Object> entry = segment.getEntry(keyOne, hashOne); 1547 ValueReference<Object, Object> valueReference = entry.getValueReference(); 1548 1549 @SuppressWarnings("unchecked") 1550 Reference<Object> reference = (Reference) valueReference; 1551 reference.enqueue(); 1552 1553 map.put(keyTwo, valueTwo); 1554 assertFalse(map.containsKey(keyOne)); 1555 assertFalse(map.containsValue(valueOne)); 1556 assertNull(map.get(keyOne)); 1557 assertEquals(1, map.size()); 1558 assertNull(segment.valueReferenceQueue.poll()); 1559 } 1560 } 1561 } 1562 1563 public void testDrainKeyReferenceQueueOnRead() { 1564 for (MapMaker maker : allKeyValueStrengthMakers()) { 1565 MapMakerInternalMap<Object, Object> map = 1566 makeMap(maker.concurrencyLevel(1)); 1567 if (map.usesKeyReferences()) { 1568 Segment<Object, Object> segment = map.segments[0]; 1569 1570 Object keyOne = new Object(); 1571 int hashOne = map.hash(keyOne); 1572 Object valueOne = new Object(); 1573 Object keyTwo = new Object(); 1574 1575 map.put(keyOne, valueOne); 1576 ReferenceEntry<Object, Object> entry = segment.getEntry(keyOne, hashOne); 1577 1578 @SuppressWarnings("unchecked") 1579 Reference<Object> reference = (Reference) entry; 1580 reference.enqueue(); 1581 1582 for (int i = 0; i < SMALL_MAX_SIZE; i++) { 1583 map.get(keyTwo); 1584 } 1585 assertFalse(map.containsKey(keyOne)); 1586 assertFalse(map.containsValue(valueOne)); 1587 assertNull(map.get(keyOne)); 1588 assertEquals(0, map.size()); 1589 assertNull(segment.keyReferenceQueue.poll()); 1590 } 1591 } 1592 } 1593 1594 public void testDrainValueReferenceQueueOnRead() { 1595 for (MapMaker maker : allKeyValueStrengthMakers()) { 1596 MapMakerInternalMap<Object, Object> map = 1597 makeMap(maker.concurrencyLevel(1)); 1598 if (map.usesValueReferences()) { 1599 Segment<Object, Object> segment = map.segments[0]; 1600 1601 Object keyOne = new Object(); 1602 int hashOne = map.hash(keyOne); 1603 Object valueOne = new Object(); 1604 Object keyTwo = new Object(); 1605 1606 map.put(keyOne, valueOne); 1607 ReferenceEntry<Object, Object> entry = segment.getEntry(keyOne, hashOne); 1608 ValueReference<Object, Object> valueReference = entry.getValueReference(); 1609 1610 @SuppressWarnings("unchecked") 1611 Reference<Object> reference = (Reference) valueReference; 1612 reference.enqueue(); 1613 1614 for (int i = 0; i < SMALL_MAX_SIZE; i++) { 1615 map.get(keyTwo); 1616 } 1617 assertFalse(map.containsKey(keyOne)); 1618 assertFalse(map.containsValue(valueOne)); 1619 assertNull(map.get(keyOne)); 1620 assertEquals(0, map.size()); 1621 assertNull(segment.valueReferenceQueue.poll()); 1622 } 1623 } 1624 } 1625 1626 // utility methods 1627 1628 /** 1629 * Returns an iterable containing all combinations of maximumSize, expireAfterAccess/Write, 1630 * weak/softKeys and weak/softValues. 1631 */ 1632 private static Iterable<MapMaker> allEntryTypeMakers() { 1633 List<MapMaker> result = newArrayList(allKeyValueStrengthMakers()); 1634 for (MapMaker maker : allKeyValueStrengthMakers()) { 1635 result.add(maker.maximumSize(SMALL_MAX_SIZE)); 1636 } 1637 for (MapMaker maker : allKeyValueStrengthMakers()) { 1638 result.add(maker.expireAfterAccess(99999, SECONDS)); 1639 } 1640 for (MapMaker maker : allKeyValueStrengthMakers()) { 1641 result.add(maker.expireAfterWrite(99999, SECONDS)); 1642 } 1643 for (MapMaker maker : allKeyValueStrengthMakers()) { 1644 result.add(maker.maximumSize(SMALL_MAX_SIZE).expireAfterAccess(99999, SECONDS)); 1645 } 1646 for (MapMaker maker : allKeyValueStrengthMakers()) { 1647 result.add(maker.maximumSize(SMALL_MAX_SIZE).expireAfterWrite(99999, SECONDS)); 1648 } 1649 return result; 1650 } 1651 1652 /** 1653 * Returns an iterable containing all combinations of maximumSize and expireAfterAccess/Write. 1654 */ 1655 static Iterable<MapMaker> allEvictingMakers() { 1656 return ImmutableList.of(createMapMaker().maximumSize(SMALL_MAX_SIZE), 1657 createMapMaker().expireAfterAccess(99999, SECONDS), 1658 createMapMaker().expireAfterWrite(99999, SECONDS), 1659 createMapMaker() 1660 .maximumSize(SMALL_MAX_SIZE) 1661 .expireAfterAccess(SMALL_MAX_SIZE, TimeUnit.SECONDS), 1662 createMapMaker() 1663 .maximumSize(SMALL_MAX_SIZE) 1664 .expireAfterWrite(SMALL_MAX_SIZE, TimeUnit.SECONDS)); 1665 } 1666 1667 /** 1668 * Returns an iterable containing all combinations weak/softKeys and weak/softValues. 1669 */ 1670 @SuppressWarnings("deprecation") 1671 private static Iterable<MapMaker> allKeyValueStrengthMakers() { 1672 return ImmutableList.of(createMapMaker(), 1673 createMapMaker().weakValues(), 1674 createMapMaker().softValues(), 1675 createMapMaker().weakKeys(), 1676 createMapMaker().weakKeys().weakValues(), 1677 createMapMaker().weakKeys().softValues(), 1678 createMapMaker().softKeys(), 1679 createMapMaker().softKeys().weakValues(), 1680 createMapMaker().softKeys().softValues()); 1681 } 1682 1683 // listeners 1684 1685 private static class CountingRemovalListener<K, V> implements RemovalListener<K, V> { 1686 private final AtomicInteger count = new AtomicInteger(); 1687 private K lastKey; 1688 private V lastValue; 1689 1690 @Override 1691 public void onRemoval(RemovalNotification<K, V> notification) { 1692 count.incrementAndGet(); 1693 lastKey = notification.getKey(); 1694 lastValue = notification.getValue(); 1695 } 1696 1697 public int getCount() { 1698 return count.get(); 1699 } 1700 1701 public K getLastEvictedKey() { 1702 return lastKey; 1703 } 1704 1705 public V getLastEvictedValue() { 1706 return lastValue; 1707 } 1708 } 1709 1710 static class QueuingRemovalListener<K, V> 1711 extends ConcurrentLinkedQueue<RemovalNotification<K, V>> implements RemovalListener<K, V> { 1712 @Override 1713 public void onRemoval(RemovalNotification<K, V> notification) { 1714 add(notification); 1715 } 1716 } 1717 1718 // entries and values 1719 1720 private static <K, V> DummyEntry<K, V> createDummyEntry( 1721 K key, int hash, V value, ReferenceEntry<K, V> next) { 1722 DummyEntry<K, V> entry = DummyEntry.create(key, hash, next); 1723 DummyValueReference<K, V> valueRef = DummyValueReference.create(value, entry); 1724 entry.setValueReference(valueRef); 1725 return entry; 1726 } 1727 1728 static class DummyEntry<K, V> implements ReferenceEntry<K, V> { 1729 private K key; 1730 private final int hash; 1731 private final ReferenceEntry<K, V> next; 1732 1733 public DummyEntry(K key, int hash, ReferenceEntry<K, V> next) { 1734 this.key = key; 1735 this.hash = hash; 1736 this.next = next; 1737 } 1738 1739 public static <K, V> DummyEntry<K, V> create(K key, int hash, ReferenceEntry<K, V> next) { 1740 return new DummyEntry<K, V>(key, hash, next); 1741 } 1742 1743 public void clearKey() { 1744 this.key = null; 1745 } 1746 1747 private ValueReference<K, V> valueReference = unset(); 1748 1749 @Override 1750 public ValueReference<K, V> getValueReference() { 1751 return valueReference; 1752 } 1753 1754 @Override 1755 public void setValueReference(ValueReference<K, V> valueReference) { 1756 this.valueReference = valueReference; 1757 } 1758 1759 @Override 1760 public ReferenceEntry<K, V> getNext() { 1761 return next; 1762 } 1763 1764 @Override 1765 public int getHash() { 1766 return hash; 1767 } 1768 1769 @Override 1770 public K getKey() { 1771 return key; 1772 } 1773 1774 private long expirationTime = Long.MAX_VALUE; 1775 1776 @Override 1777 public long getExpirationTime() { 1778 return expirationTime; 1779 } 1780 1781 @Override 1782 public void setExpirationTime(long time) { 1783 this.expirationTime = time; 1784 } 1785 1786 private ReferenceEntry<K, V> nextExpirable = nullEntry(); 1787 1788 @Override 1789 public ReferenceEntry<K, V> getNextExpirable() { 1790 return nextExpirable; 1791 } 1792 1793 @Override 1794 public void setNextExpirable(ReferenceEntry<K, V> next) { 1795 this.nextExpirable = next; 1796 } 1797 1798 private ReferenceEntry<K, V> previousExpirable = nullEntry(); 1799 1800 @Override 1801 public ReferenceEntry<K, V> getPreviousExpirable() { 1802 return previousExpirable; 1803 } 1804 1805 @Override 1806 public void setPreviousExpirable(ReferenceEntry<K, V> previous) { 1807 this.previousExpirable = previous; 1808 } 1809 1810 private ReferenceEntry<K, V> nextEvictable = nullEntry(); 1811 1812 @Override 1813 public ReferenceEntry<K, V> getNextEvictable() { 1814 return nextEvictable; 1815 } 1816 1817 @Override 1818 public void setNextEvictable(ReferenceEntry<K, V> next) { 1819 this.nextEvictable = next; 1820 } 1821 1822 private ReferenceEntry<K, V> previousEvictable = nullEntry(); 1823 1824 @Override 1825 public ReferenceEntry<K, V> getPreviousEvictable() { 1826 return previousEvictable; 1827 } 1828 1829 @Override 1830 public void setPreviousEvictable(ReferenceEntry<K, V> previous) { 1831 this.previousEvictable = previous; 1832 } 1833 } 1834 1835 static class DummyValueReference<K, V> implements ValueReference<K, V> { 1836 final ReferenceEntry<K, V> entry; 1837 private V value; 1838 1839 public DummyValueReference(V value, ReferenceEntry<K, V> entry) { 1840 this.value = value; 1841 this.entry = entry; 1842 } 1843 1844 public static <K, V> DummyValueReference<K, V> create(V value, ReferenceEntry<K, V> entry) { 1845 return new DummyValueReference<K, V>(value, entry); 1846 } 1847 1848 @Override 1849 public V get() { 1850 return value; 1851 } 1852 1853 @Override 1854 public ReferenceEntry<K, V> getEntry() { 1855 return entry; 1856 } 1857 1858 @Override 1859 public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { 1860 return new DummyValueReference<K, V>(value, entry); 1861 } 1862 1863 boolean computing = false; 1864 1865 public void setComputing(boolean computing) { 1866 this.computing = computing; 1867 } 1868 1869 @Override 1870 public boolean isComputingReference() { 1871 return computing; 1872 } 1873 1874 @Override 1875 public V waitForValue() { 1876 return get(); 1877 } 1878 1879 @Override 1880 public void clear(ValueReference<K, V> newValue) { 1881 value = null; 1882 } 1883 } 1884 1885 public void testNullParameters() throws Exception { 1886 NullPointerTester tester = new NullPointerTester(); 1887 tester.testAllPublicInstanceMethods(makeMap(createMapMaker())); 1888 } 1889 1890 } 1891