1 package com.atlassian.util.concurrent;
2
3 import static com.atlassian.util.concurrent.TestUtil.pause;
4 import static org.junit.Assert.assertEquals;
5 import static org.junit.Assert.assertFalse;
6 import static org.junit.Assert.assertNotNull;
7 import static org.junit.Assert.assertNull;
8 import static org.junit.Assert.assertSame;
9 import static org.junit.Assert.assertTrue;
10 import static org.junit.Assert.fail;
11
12 import org.junit.Test;
13
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.concurrent.Callable;
17 import java.util.concurrent.CancellationException;
18 import java.util.concurrent.CountDownLatch;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.ExecutorService;
21 import java.util.concurrent.Executors;
22 import java.util.concurrent.Future;
23 import java.util.concurrent.atomic.AtomicInteger;
24 import java.util.concurrent.atomic.AtomicReference;
25
26 public class ResettableLazyReferenceTest {
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 @Test
43 public void concurrentCreate() throws Exception {
44 final int nThreads = 40;
45 final Object[] results = new Object[nThreads];
46 final AtomicInteger createCallCount = new AtomicInteger(0);
47 final ResettableLazyReference<Object> ref = new ResettableLazyReference<Object>() {
48 @Override
49 protected Object create() {
50
51
52
53
54
55
56
57 createCallCount.incrementAndGet();
58 pause();
59 pause();
60 pause();
61 pause();
62 pause();
63 return new Object();
64 }
65 };
66
67
68
69
70
71 final ExecutorService pool = Executors.newFixedThreadPool(nThreads);
72 final CountDownLatch latch = new CountDownLatch(nThreads);
73
74 final List<Callable<Object>> tasks = new ArrayList<Callable<Object>>(nThreads);
75
76 for (int i = 0; i < nThreads; i++) {
77 final int j = i;
78 tasks.add(new Callable<Object>() {
79 public Object call() throws Exception {
80
81
82
83
84
85 latch.countDown();
86 latch.await();
87 results[j] = ref.get();
88 return results[j];
89 }
90 });
91 }
92
93 List<Future<Object>> futures = null;
94 futures = pool.invokeAll(tasks);
95
96
97 assertEquals(1, createCallCount.get());
98
99
100
101
102
103 final Object result = results[0];
104 for (final Future<Object> future : futures) {
105 assertSame(result, future.get());
106 }
107 for (int i = 0; i < results.length; i++) {
108 assertSame("got back different reference in '" + i + "' place", result, results[i]);
109 }
110 pool.shutdown();
111 }
112
113 @Test
114 public void exception() {
115 final Exception myException = new Exception();
116
117 final ResettableLazyReference<Object> ref = new ResettableLazyReference<Object>() {
118 @Override
119 protected Object create() throws Exception {
120 throw myException;
121 }
122 };
123
124 try {
125 ref.get();
126 fail("RuntimeException should have been thrown");
127 } catch (final RuntimeException yay) {
128 assertNotNull(yay.getCause());
129 assertTrue(myException == yay.getCause());
130 }
131 }
132
133 @Test
134 public void getNotInterruptable() throws Exception {
135 final BooleanLatch latch = new BooleanLatch();
136
137 final ResettableLazyReference<Integer> ref = new ResettableLazyReference<Integer>() {
138 @Override
139 protected Integer create() {
140
141 while (true) {
142 try {
143 latch.await();
144 return 10;
145 } catch (final InterruptedException e) {}
146 }
147 }
148 };
149
150 final Thread client = new Thread(new Runnable() {
151 public void run() {
152 ref.get();
153 }
154 }, this.getClass().getName());
155 client.start();
156
157 for (int i = 0; i < 10; i++) {
158 pause();
159 assertFalse(ref.isInitialized());
160 client.interrupt();
161 }
162 pause();
163 assertFalse(ref.isInitialized());
164
165 latch.release();
166 pause();
167 assertTrue(ref.isInitialized());
168
169 final int obj = ref.get();
170 assertEquals(10, obj);
171 }
172
173 @Test(expected = InterruptedException.class)
174 public void getInterruptiblyThrowsInterrupted() throws Exception {
175 final ResettableLazyReference<String> ref = new ResettableLazyReference<String>() {
176 @Override
177 protected String create() throws Exception {
178 return "test";
179 }
180 };
181 Thread.currentThread().interrupt();
182 ref.getInterruptibly();
183 }
184
185 @Test
186 public void getInterruptibly() throws Exception {
187 final class Result<T> {
188 final T result;
189 final Exception exception;
190
191 Result(final T result) {
192 this.result = result;
193 this.exception = null;
194 }
195
196 Result(final Exception exception) {
197 this.result = null;
198 this.exception = exception;
199 }
200 }
201 final BooleanLatch latch = new BooleanLatch();
202
203 final ResettableLazyReference<Integer> ref = new ResettableLazyReference<Integer>() {
204 @Override
205 protected Integer create() {
206
207 while (true) {
208 try {
209 latch.await();
210 return 10;
211 } catch (final InterruptedException e) {}
212 }
213 }
214 };
215
216 final AtomicReference<Result<Integer>> result1 = new AtomicReference<Result<Integer>>();
217 final Thread client1 = new Thread(new Runnable() {
218 public void run() {
219 try {
220 result1.compareAndSet(null, new Result<Integer>(ref.getInterruptibly()));
221 } catch (final Exception e) {
222 result1.compareAndSet(null, new Result<Integer>(e));
223 }
224 }
225 }, this.getClass().getName());
226 client1.start();
227
228 pause();
229 final AtomicReference<Result<Integer>> result2 = new AtomicReference<Result<Integer>>();
230 final Thread client2 = new Thread(new Runnable() {
231 public void run() {
232 try {
233 result2.compareAndSet(null, new Result<Integer>(ref.getInterruptibly()));
234 } catch (final Exception e) {
235 result2.compareAndSet(null, new Result<Integer>(e));
236 }
237 }
238 }, this.getClass().getName());
239 client2.start();
240
241 for (int i = 0; i < 10; i++) {
242 pause();
243 assertFalse(ref.isInitialized());
244 client1.interrupt();
245 client2.interrupt();
246 }
247
248 assertNull(result1.get());
249 assertNotNull(result2.get().exception);
250 assertEquals(InterruptedException.class, result2.get().exception.getClass());
251 pause();
252 assertFalse(ref.isInitialized());
253
254 latch.release();
255 pause();
256 assertTrue(ref.isInitialized());
257
258 {
259 final int result = ref.get();
260 assertEquals(10, result);
261 }
262 assertNotNull(result1.get());
263 assertNotNull(result1.get().result);
264 {
265 final int result = result1.get().result;
266 assertEquals(10, result);
267 }
268 }
269
270 @Test(expected = CancellationException.class)
271 public void cancellable() throws Exception {
272 final ResettableLazyReference<String> ref = new ResettableLazyReference<String>() {
273
274 @Override
275 protected String create() throws Exception {
276 return "created!";
277 }
278
279 };
280 ref.cancel();
281 ref.get();
282 }
283
284 @Test
285 public void getNotInterruptible() throws Exception {
286 final ResettableLazyReference<String> ref = new ResettableLazyReference<String>() {
287 @Override
288 protected String create() throws Exception {
289 return "test!";
290 }
291 };
292 Thread.currentThread().interrupt();
293 ref.get();
294 assertTrue(Thread.interrupted());
295 }
296
297 @Test
298 public void initExConstructorWithBlankExecExCause() throws Exception {
299 @SuppressWarnings("serial")
300 final ExecutionException e = new ExecutionException("") {};
301 final Exception ex = new LazyReference.InitializationException(e);
302 assertSame(e, ex.getCause());
303 }
304
305 @Test
306 public void initExConstructorWithRealExecExCause() throws Exception {
307 final NoSuchMethodError er = new NoSuchMethodError();
308 final ExecutionException e = new ExecutionException("", er);
309 final Exception ex = new LazyReference.InitializationException(e);
310 assertSame(er, ex.getCause());
311 }
312
313 @Test
314 public void resettable() throws Exception {
315 final ResettableLazyReference<Integer> count = new ResettableLazyReference<Integer>() {
316 private int createCallCount;
317
318 @Override
319 protected Integer create() throws Exception {
320 return ++createCallCount;
321 }
322 };
323 assertEquals(Integer.valueOf(1), count.get());
324 assertEquals(Integer.valueOf(1), count.get());
325 assertEquals(Integer.valueOf(1), count.get());
326 count.reset();
327 assertEquals(Integer.valueOf(2), count.get());
328 assertEquals(Integer.valueOf(2), count.get());
329 count.reset();
330 assertEquals(Integer.valueOf(3), count.get());
331 assertEquals(Integer.valueOf(3), count.get());
332 assertEquals(Integer.valueOf(3), count.get());
333 }
334 }