• 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.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