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.util.concurrent; 18 19 import static com.google.common.collect.Iterables.concat; 20 21 import com.google.common.base.Functions; 22 import com.google.common.base.Supplier; 23 import com.google.common.collect.ImmutableList; 24 import com.google.common.collect.Lists; 25 import com.google.common.collect.Maps; 26 import com.google.common.collect.Ordering; 27 import com.google.common.collect.Sets; 28 import com.google.common.testing.GcFinalization; 29 import com.google.common.testing.NullPointerTester; 30 import java.lang.ref.WeakReference; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.Set; 34 import java.util.concurrent.Semaphore; 35 import java.util.concurrent.locks.Lock; 36 import java.util.concurrent.locks.ReadWriteLock; 37 import java.util.concurrent.locks.ReentrantLock; 38 import java.util.concurrent.locks.ReentrantReadWriteLock; 39 import junit.framework.TestCase; 40 41 /** 42 * Tests for Striped. 43 * 44 * @author Dimitris Andreou 45 */ 46 public class StripedTest extends TestCase { strongImplementations()47 private static List<Striped<?>> strongImplementations() { 48 return ImmutableList.of( 49 Striped.readWriteLock(100), 50 Striped.readWriteLock(256), 51 Striped.lock(100), 52 Striped.lock(256), 53 Striped.custom( 54 100, 55 new Supplier<Lock>() { 56 @Override 57 public Lock get() { 58 return new ReentrantLock(true); 59 } 60 }), 61 Striped.custom( 62 256, 63 new Supplier<Lock>() { 64 @Override 65 public Lock get() { 66 return new ReentrantLock(true); 67 } 68 }), 69 Striped.semaphore(100, 1), 70 Striped.semaphore(256, 1)); 71 } 72 73 private static final Supplier<ReadWriteLock> READ_WRITE_LOCK_SUPPLIER = 74 new Supplier<ReadWriteLock>() { 75 @Override 76 public ReadWriteLock get() { 77 return new ReentrantReadWriteLock(); 78 } 79 }; 80 81 private static final Supplier<Lock> LOCK_SUPPLER = 82 new Supplier<Lock>() { 83 @Override 84 public Lock get() { 85 return new ReentrantLock(); 86 } 87 }; 88 89 private static final Supplier<Semaphore> SEMAPHORE_SUPPLER = 90 new Supplier<Semaphore>() { 91 @Override 92 public Semaphore get() { 93 return new Semaphore(1, false); 94 } 95 }; 96 97 private static List<Striped<?>> weakImplementations() { 98 return ImmutableList.<Striped<?>>builder() 99 .add(new Striped.SmallLazyStriped<ReadWriteLock>(50, READ_WRITE_LOCK_SUPPLIER)) 100 .add(new Striped.SmallLazyStriped<ReadWriteLock>(64, READ_WRITE_LOCK_SUPPLIER)) 101 .add(new Striped.LargeLazyStriped<ReadWriteLock>(50, READ_WRITE_LOCK_SUPPLIER)) 102 .add(new Striped.LargeLazyStriped<ReadWriteLock>(64, READ_WRITE_LOCK_SUPPLIER)) 103 .add(new Striped.SmallLazyStriped<Lock>(50, LOCK_SUPPLER)) 104 .add(new Striped.SmallLazyStriped<Lock>(64, LOCK_SUPPLER)) 105 .add(new Striped.LargeLazyStriped<Lock>(50, LOCK_SUPPLER)) 106 .add(new Striped.LargeLazyStriped<Lock>(64, LOCK_SUPPLER)) 107 .add(new Striped.SmallLazyStriped<Semaphore>(50, SEMAPHORE_SUPPLER)) 108 .add(new Striped.SmallLazyStriped<Semaphore>(64, SEMAPHORE_SUPPLER)) 109 .add(new Striped.LargeLazyStriped<Semaphore>(50, SEMAPHORE_SUPPLER)) 110 .add(new Striped.LargeLazyStriped<Semaphore>(64, SEMAPHORE_SUPPLER)) 111 .build(); 112 } 113 114 private static Iterable<Striped<?>> allImplementations() { 115 return concat(strongImplementations(), weakImplementations()); 116 } 117 118 public void testNull() throws Exception { 119 for (Striped<?> striped : allImplementations()) { 120 new NullPointerTester().testAllPublicInstanceMethods(striped); 121 } 122 } 123 124 public void testSizes() { 125 // not bothering testing all variations, since we know they share implementations 126 assertTrue(Striped.lock(100).size() >= 100); 127 assertTrue(Striped.lock(256).size() == 256); 128 assertTrue(Striped.lazyWeakLock(100).size() >= 100); 129 assertTrue(Striped.lazyWeakLock(256).size() == 256); 130 } 131 132 @AndroidIncompatible // Presumably GC doesn't trigger, despite our efforts. 133 public void testWeakImplementations() { 134 for (Striped<?> striped : weakImplementations()) { 135 WeakReference<Object> weakRef = new WeakReference<>(striped.get(new Object())); 136 GcFinalization.awaitClear(weakRef); 137 } 138 } 139 140 @AndroidIncompatible // Presumably GC doesn't trigger, despite our efforts. 141 public void testWeakReadWrite() { 142 Striped<ReadWriteLock> striped = Striped.lazyWeakReadWriteLock(1000); 143 Object key = new Object(); 144 Lock readLock = striped.get(key).readLock(); 145 WeakReference<Object> garbage = new WeakReference<>(new Object()); 146 GcFinalization.awaitClear(garbage); 147 Lock writeLock = striped.get(key).writeLock(); 148 readLock.lock(); 149 assertFalse(writeLock.tryLock()); 150 readLock.unlock(); 151 } 152 153 @AndroidIncompatible // Presumably GC doesn't trigger, despite our efforts. 154 public void testStrongImplementations() { 155 for (Striped<?> striped : strongImplementations()) { 156 WeakReference<Object> weakRef = new WeakReference<>(striped.get(new Object())); 157 WeakReference<Object> garbage = new WeakReference<>(new Object()); 158 GcFinalization.awaitClear(garbage); 159 assertNotNull(weakRef.get()); 160 } 161 } 162 163 public void testMaximalWeakStripedLock() { 164 Striped<Lock> stripedLock = Striped.lazyWeakLock(Integer.MAX_VALUE); 165 for (int i = 0; i < 10000; i++) { 166 stripedLock.get(new Object()).lock(); 167 // nothing special (e.g. an exception) happens 168 } 169 } 170 171 public void testBulkGetReturnsSorted() { 172 for (Striped<?> striped : allImplementations()) { 173 Map<Object, Integer> indexByLock = Maps.newHashMap(); 174 for (int i = 0; i < striped.size(); i++) { 175 indexByLock.put(striped.getAt(i), i); 176 } 177 178 // ensure that bulkGet returns locks in monotonically increasing order 179 for (int objectsNum = 1; objectsNum <= striped.size() * 2; objectsNum++) { 180 Set<Object> objects = Sets.newHashSetWithExpectedSize(objectsNum); 181 for (int i = 0; i < objectsNum; i++) { 182 objects.add(new Object()); 183 } 184 185 Iterable<?> locks = striped.bulkGet(objects); 186 assertTrue(Ordering.natural().onResultOf(Functions.forMap(indexByLock)).isOrdered(locks)); 187 188 // check idempotency 189 Iterable<?> locks2 = striped.bulkGet(objects); 190 assertEquals(Lists.newArrayList(locks), Lists.newArrayList(locks2)); 191 } 192 } 193 } 194 195 /** Checks idempotency, and that we observe the promised number of stripes. */ 196 public void testBasicInvariants() { 197 for (Striped<?> striped : allImplementations()) { 198 assertBasicInvariants(striped); 199 } 200 } 201 202 private static void assertBasicInvariants(Striped<?> striped) { 203 Set<Object> observed = Sets.newIdentityHashSet(); // for the sake of weakly referenced locks. 204 // this gets the stripes with #getAt(index) 205 for (int i = 0; i < striped.size(); i++) { 206 Object object = striped.getAt(i); 207 assertNotNull(object); 208 assertSame(object, striped.getAt(i)); // idempotent 209 observed.add(object); 210 } 211 assertTrue("All stripes observed", observed.size() == striped.size()); 212 213 // this uses #get(key), makes sure an already observed stripe is returned 214 for (int i = 0; i < striped.size() * 100; i++) { 215 assertTrue(observed.contains(striped.get(new Object()))); 216 } 217 218 try { 219 striped.getAt(-1); 220 fail(); 221 } catch (RuntimeException expected) { 222 } 223 224 try { 225 striped.getAt(striped.size()); 226 fail(); 227 } catch (RuntimeException expected) { 228 } 229 } 230 231 public void testMaxSize() { 232 for (Striped<?> striped : 233 ImmutableList.of( 234 Striped.lazyWeakLock(Integer.MAX_VALUE), 235 Striped.lazyWeakSemaphore(Integer.MAX_VALUE, Integer.MAX_VALUE), 236 Striped.lazyWeakReadWriteLock(Integer.MAX_VALUE))) { 237 for (int i = 0; i < 3; i++) { 238 // doesn't throw exception 239 Object unused = striped.getAt(Integer.MAX_VALUE - i); 240 } 241 } 242 } 243 } 244