• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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.util.concurrent.InterruptionUtil.repeatedlyInterruptTestThread;
20 import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly;
21 import static java.util.concurrent.TimeUnit.MINUTES;
22 import static java.util.concurrent.TimeUnit.SECONDS;
23 
24 import com.google.common.testing.TearDown;
25 import com.google.common.testing.TearDownStack;
26 import java.util.concurrent.Callable;
27 import java.util.concurrent.ExecutionException;
28 import java.util.concurrent.ExecutorService;
29 import java.util.concurrent.Executors;
30 import java.util.concurrent.Future;
31 import java.util.concurrent.FutureTask;
32 import java.util.concurrent.TimeUnit;
33 import java.util.concurrent.TimeoutException;
34 import junit.framework.TestCase;
35 
36 // TODO(cpovirk): Should this be merged into UninterruptiblesTest?
37 /**
38  * Unit test for {@link Uninterruptibles#getUninterruptibly}
39  *
40  * @author Kevin Bourrillion
41  * @author Chris Povirk
42  */
43 public class UninterruptibleFutureTest extends TestCase {
44   private SleepingRunnable sleeper;
45   private Future<Boolean> delayedFuture;
46 
47   private final TearDownStack tearDownStack = new TearDownStack();
48 
49   @Override
setUp()50   protected void setUp() {
51     final ExecutorService executor = Executors.newSingleThreadExecutor();
52     tearDownStack.addTearDown(
53         new TearDown() {
54           @Override
55           public void tearDown() {
56             executor.shutdownNow();
57           }
58         });
59     sleeper = new SleepingRunnable(1000);
60     delayedFuture = executor.submit(sleeper, true);
61 
62     tearDownStack.addTearDown(
63         new TearDown() {
64           @Override
65           public void tearDown() {
66             Thread.interrupted();
67           }
68         });
69   }
70 
71   @Override
tearDown()72   protected void tearDown() {
73     tearDownStack.runTearDown();
74   }
75 
76   /**
77    * This first test doesn't test anything in Uninterruptibles, just demonstrates some normal
78    * behavior of futures so that you can contrast the next test with it.
79    */
80 
testRegularFutureInterrupted()81   public void testRegularFutureInterrupted() throws ExecutionException {
82 
83     /*
84      * Here's the order of events that we want.
85      *
86      * 1. The client thread begins to block on a get() call to a future.
87      * 2. The client thread is interrupted sometime before the result would be
88      *   available.
89      * 3. We expect the client's get() to throw an InterruptedException.
90      * 4. We expect the client thread's interrupt state to be false.
91      * 5. The client thread again makes a blocking call to get().
92      * 6. Now the result becomes available.
93      * 7. We expect get() to return this result.
94      * 8. We expect the test thread's interrupt state to be false.
95      */
96     InterruptionUtil.requestInterruptIn(200, TimeUnit.MILLISECONDS);
97 
98     assertFalse(Thread.interrupted());
99     try {
100       delayedFuture.get(20000, TimeUnit.MILLISECONDS);
101       fail("expected to be interrupted");
102     } catch (InterruptedException expected) {
103     } catch (TimeoutException e) {
104       throw new RuntimeException(e);
105     }
106 
107     // we were interrupted, but it's been cleared now
108     assertFalse(Thread.interrupted());
109 
110     assertFalse(sleeper.completed);
111     try {
112       assertTrue(delayedFuture.get());
113     } catch (InterruptedException e) {
114       throw new RuntimeException(e);
115     }
116     assertTrue(sleeper.completed);
117   }
118 
119 
testMakeUninterruptible_timeoutPreservedThroughInterruption()120   public void testMakeUninterruptible_timeoutPreservedThroughInterruption()
121       throws ExecutionException {
122 
123     repeatedlyInterruptTestThread(100, tearDownStack);
124 
125     try {
126       getUninterruptibly(delayedFuture, 500, TimeUnit.MILLISECONDS);
127       fail("expected to time out");
128     } catch (TimeoutException expected) {
129     }
130     assertTrue(Thread.interrupted()); // clears the interrupt state, too
131 
132     assertFalse(sleeper.completed);
133     assertTrue(getUninterruptibly(delayedFuture));
134 
135     assertTrue(Thread.interrupted()); // clears the interrupt state, too
136     assertTrue(sleeper.completed);
137   }
138 
139   private static class SleepingRunnable implements Runnable {
140     final int millis;
141     volatile boolean completed;
142 
SleepingRunnable(int millis)143     public SleepingRunnable(int millis) {
144       this.millis = millis;
145     }
146 
147     @Override
run()148     public void run() {
149       try {
150         Thread.sleep(millis);
151       } catch (InterruptedException wontHappen) {
152         throw new AssertionError();
153       }
154       completed = true;
155     }
156   }
157 
158 
testMakeUninterruptible_untimed_uninterrupted()159   public void testMakeUninterruptible_untimed_uninterrupted() throws Exception {
160     runUntimedInterruptsTest(0);
161   }
162 
163 
testMakeUninterruptible_untimed_interrupted()164   public void testMakeUninterruptible_untimed_interrupted() throws Exception {
165     runUntimedInterruptsTest(1);
166   }
167 
168 
testMakeUninterruptible_untimed_multiplyInterrupted()169   public void testMakeUninterruptible_untimed_multiplyInterrupted() throws Exception {
170     runUntimedInterruptsTest(38);
171   }
172 
173 
testMakeUninterruptible_timed_uninterrupted()174   public void testMakeUninterruptible_timed_uninterrupted() throws Exception {
175     runTimedInterruptsTest(0);
176   }
177 
178 
testMakeUninterruptible_timed_interrupted()179   public void testMakeUninterruptible_timed_interrupted() throws Exception {
180     runTimedInterruptsTest(1);
181   }
182 
183 
testMakeUninterruptible_timed_multiplyInterrupted()184   public void testMakeUninterruptible_timed_multiplyInterrupted() throws Exception {
185     runTimedInterruptsTest(38);
186   }
187 
runUntimedInterruptsTest(int times)188   private static void runUntimedInterruptsTest(int times)
189       throws InterruptedException, ExecutionException, TimeoutException {
190     SettableFuture<String> future = SettableFuture.create();
191     FutureTask<Boolean> interruptReporter = untimedInterruptReporter(future, false);
192 
193     runNInterruptsTest(times, future, interruptReporter);
194   }
195 
runTimedInterruptsTest(int times)196   private static void runTimedInterruptsTest(int times)
197       throws InterruptedException, ExecutionException, TimeoutException {
198     SettableFuture<String> future = SettableFuture.create();
199     FutureTask<Boolean> interruptReporter = timedInterruptReporter(future);
200 
201     runNInterruptsTest(times, future, interruptReporter);
202   }
203 
runNInterruptsTest( int times, SettableFuture<String> future, FutureTask<Boolean> interruptReporter)204   private static void runNInterruptsTest(
205       int times, SettableFuture<String> future, FutureTask<Boolean> interruptReporter)
206       throws InterruptedException, ExecutionException, TimeoutException {
207     Thread waitingThread = new Thread(interruptReporter);
208     waitingThread.start();
209     for (int i = 0; i < times; i++) {
210       waitingThread.interrupt();
211     }
212 
213     future.set(RESULT);
214 
215     assertEquals(times > 0, (boolean) interruptReporter.get(20, SECONDS));
216   }
217 
218   /**
219    * Confirms that the test code triggers {@link InterruptedException} in a standard {@link Future}.
220    */
221 
testMakeUninterruptible_plainFutureSanityCheck()222   public void testMakeUninterruptible_plainFutureSanityCheck() throws Exception {
223     SettableFuture<String> future = SettableFuture.create();
224     FutureTask<Boolean> wasInterrupted = untimedInterruptReporter(future, true);
225 
226     Thread waitingThread = new Thread(wasInterrupted);
227     waitingThread.start();
228     waitingThread.interrupt();
229     try {
230       wasInterrupted.get();
231       fail();
232     } catch (ExecutionException expected) {
233       assertTrue(
234           expected.getCause().toString(), expected.getCause() instanceof InterruptedException);
235     }
236   }
237 
238 
testMakeUninterruptible_timedGetZeroTimeoutAttempted()239   public void testMakeUninterruptible_timedGetZeroTimeoutAttempted()
240       throws TimeoutException, ExecutionException {
241     SettableFuture<String> future = SettableFuture.create();
242     future.set(RESULT);
243     /*
244      * getUninterruptibly should call the timed get method once with a
245      * wait of 0 seconds (and it should succeed, since the result is already
246      * available).
247      */
248     assertEquals(RESULT, getUninterruptibly(future, 0, SECONDS));
249   }
250 
251 
testMakeUninterruptible_timedGetNegativeTimeoutAttempted()252   public void testMakeUninterruptible_timedGetNegativeTimeoutAttempted()
253       throws TimeoutException, ExecutionException {
254     SettableFuture<String> future = SettableFuture.create();
255     future.set(RESULT);
256     /*
257      * The getUninterruptibly should call the timed get method once with a
258      * wait of -1 seconds (and it should succeed, since the result is already
259      * available).
260      */
261     assertEquals(RESULT, getUninterruptibly(future, -1, SECONDS));
262   }
263 
untimedInterruptReporter( final Future<?> future, final boolean allowInterruption)264   private static FutureTask<Boolean> untimedInterruptReporter(
265       final Future<?> future, final boolean allowInterruption) {
266     return new FutureTask<>(
267         new Callable<Boolean>() {
268           @Override
269           public Boolean call() throws Exception {
270             Object actual;
271             if (allowInterruption) {
272               actual = future.get();
273             } else {
274               actual = getUninterruptibly(future);
275             }
276             assertEquals(RESULT, actual);
277             return Thread.interrupted();
278           }
279         });
280   }
281 
282   private static FutureTask<Boolean> timedInterruptReporter(final Future<?> future) {
283     return new FutureTask<>(
284         new Callable<Boolean>() {
285           @Override
286           public Boolean call() throws Exception {
287             assertEquals(RESULT, getUninterruptibly(future, 10, MINUTES));
288             return Thread.interrupted();
289           }
290         });
291   }
292 
293   private static final String RESULT = "result";
294 }
295