• 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    */
testRegularFutureInterrupted()80   public void testRegularFutureInterrupted() throws ExecutionException {
81 
82     /*
83      * Here's the order of events that we want.
84      *
85      * 1. The client thread begins to block on a get() call to a future.
86      * 2. The client thread is interrupted sometime before the result would be
87      *   available.
88      * 3. We expect the client's get() to throw an InterruptedException.
89      * 4. We expect the client thread's interrupt state to be false.
90      * 5. The client thread again makes a blocking call to get().
91      * 6. Now the result becomes available.
92      * 7. We expect get() to return this result.
93      * 8. We expect the test thread's interrupt state to be false.
94      */
95     InterruptionUtil.requestInterruptIn(200, TimeUnit.MILLISECONDS);
96 
97     assertFalse(Thread.interrupted());
98     try {
99       delayedFuture.get(20000, TimeUnit.MILLISECONDS);
100       fail("expected to be interrupted");
101     } catch (InterruptedException expected) {
102     } catch (TimeoutException e) {
103       throw new RuntimeException(e);
104     }
105 
106     // we were interrupted, but it's been cleared now
107     assertFalse(Thread.interrupted());
108 
109     assertFalse(sleeper.completed);
110     try {
111       assertTrue(delayedFuture.get());
112     } catch (InterruptedException e) {
113       throw new RuntimeException(e);
114     }
115     assertTrue(sleeper.completed);
116   }
117 
testMakeUninterruptible_timeoutPreservedThroughInterruption()118   public void testMakeUninterruptible_timeoutPreservedThroughInterruption()
119       throws ExecutionException {
120 
121     repeatedlyInterruptTestThread(100, tearDownStack);
122 
123     try {
124       getUninterruptibly(delayedFuture, 500, TimeUnit.MILLISECONDS);
125       fail("expected to time out");
126     } catch (TimeoutException expected) {
127     }
128     assertTrue(Thread.interrupted()); // clears the interrupt state, too
129 
130     assertFalse(sleeper.completed);
131     assertTrue(getUninterruptibly(delayedFuture));
132 
133     assertTrue(Thread.interrupted()); // clears the interrupt state, too
134     assertTrue(sleeper.completed);
135   }
136 
137   private static class SleepingRunnable implements Runnable {
138     final int millis;
139     volatile boolean completed;
140 
SleepingRunnable(int millis)141     public SleepingRunnable(int millis) {
142       this.millis = millis;
143     }
144 
145     @Override
run()146     public void run() {
147       try {
148         Thread.sleep(millis);
149       } catch (InterruptedException wontHappen) {
150         throw new AssertionError();
151       }
152       completed = true;
153     }
154   }
155 
testMakeUninterruptible_untimed_uninterrupted()156   public void testMakeUninterruptible_untimed_uninterrupted() throws Exception {
157     runUntimedInterruptsTest(0);
158   }
159 
testMakeUninterruptible_untimed_interrupted()160   public void testMakeUninterruptible_untimed_interrupted() throws Exception {
161     runUntimedInterruptsTest(1);
162   }
163 
testMakeUninterruptible_untimed_multiplyInterrupted()164   public void testMakeUninterruptible_untimed_multiplyInterrupted() throws Exception {
165     runUntimedInterruptsTest(38);
166   }
167 
testMakeUninterruptible_timed_uninterrupted()168   public void testMakeUninterruptible_timed_uninterrupted() throws Exception {
169     runTimedInterruptsTest(0);
170   }
171 
testMakeUninterruptible_timed_interrupted()172   public void testMakeUninterruptible_timed_interrupted() throws Exception {
173     runTimedInterruptsTest(1);
174   }
175 
testMakeUninterruptible_timed_multiplyInterrupted()176   public void testMakeUninterruptible_timed_multiplyInterrupted() throws Exception {
177     runTimedInterruptsTest(38);
178   }
179 
runUntimedInterruptsTest(int times)180   private static void runUntimedInterruptsTest(int times)
181       throws InterruptedException, ExecutionException, TimeoutException {
182     SettableFuture<String> future = SettableFuture.create();
183     FutureTask<Boolean> interruptReporter = untimedInterruptReporter(future, false);
184 
185     runNInterruptsTest(times, future, interruptReporter);
186   }
187 
runTimedInterruptsTest(int times)188   private static void runTimedInterruptsTest(int times)
189       throws InterruptedException, ExecutionException, TimeoutException {
190     SettableFuture<String> future = SettableFuture.create();
191     FutureTask<Boolean> interruptReporter = timedInterruptReporter(future);
192 
193     runNInterruptsTest(times, future, interruptReporter);
194   }
195 
runNInterruptsTest( int times, SettableFuture<String> future, FutureTask<Boolean> interruptReporter)196   private static void runNInterruptsTest(
197       int times, SettableFuture<String> future, FutureTask<Boolean> interruptReporter)
198       throws InterruptedException, ExecutionException, TimeoutException {
199     Thread waitingThread = new Thread(interruptReporter);
200     waitingThread.start();
201     for (int i = 0; i < times; i++) {
202       waitingThread.interrupt();
203     }
204 
205     future.set(RESULT);
206 
207     assertEquals(times > 0, (boolean) interruptReporter.get(20, SECONDS));
208   }
209 
210   /**
211    * Confirms that the test code triggers {@link InterruptedException} in a standard {@link Future}.
212    */
testMakeUninterruptible_plainFutureSanityCheck()213   public void testMakeUninterruptible_plainFutureSanityCheck() throws Exception {
214     SettableFuture<String> future = SettableFuture.create();
215     FutureTask<Boolean> wasInterrupted = untimedInterruptReporter(future, true);
216 
217     Thread waitingThread = new Thread(wasInterrupted);
218     waitingThread.start();
219     waitingThread.interrupt();
220     try {
221       wasInterrupted.get();
222       fail();
223     } catch (ExecutionException expected) {
224       assertTrue(
225           expected.getCause().toString(), expected.getCause() instanceof InterruptedException);
226     }
227   }
228 
testMakeUninterruptible_timedGetZeroTimeoutAttempted()229   public void testMakeUninterruptible_timedGetZeroTimeoutAttempted()
230       throws TimeoutException, ExecutionException {
231     SettableFuture<String> future = SettableFuture.create();
232     future.set(RESULT);
233     /*
234      * getUninterruptibly should call the timed get method once with a
235      * wait of 0 seconds (and it should succeed, since the result is already
236      * available).
237      */
238     assertEquals(RESULT, getUninterruptibly(future, 0, SECONDS));
239   }
240 
testMakeUninterruptible_timedGetNegativeTimeoutAttempted()241   public void testMakeUninterruptible_timedGetNegativeTimeoutAttempted()
242       throws TimeoutException, ExecutionException {
243     SettableFuture<String> future = SettableFuture.create();
244     future.set(RESULT);
245     /*
246      * The getUninterruptibly should call the timed get method once with a
247      * wait of -1 seconds (and it should succeed, since the result is already
248      * available).
249      */
250     assertEquals(RESULT, getUninterruptibly(future, -1, SECONDS));
251   }
252 
untimedInterruptReporter( final Future<?> future, final boolean allowInterruption)253   private static FutureTask<Boolean> untimedInterruptReporter(
254       final Future<?> future, final boolean allowInterruption) {
255     return new FutureTask<>(
256         new Callable<Boolean>() {
257           @Override
258           public Boolean call() throws Exception {
259             Object actual;
260             if (allowInterruption) {
261               actual = future.get();
262             } else {
263               actual = getUninterruptibly(future);
264             }
265             assertEquals(RESULT, actual);
266             return Thread.interrupted();
267           }
268         });
269   }
270 
271   private static FutureTask<Boolean> timedInterruptReporter(final Future<?> future) {
272     return new FutureTask<>(
273         new Callable<Boolean>() {
274           @Override
275           public Boolean call() throws Exception {
276             assertEquals(RESULT, getUninterruptibly(future, 10, MINUTES));
277             return Thread.interrupted();
278           }
279         });
280   }
281 
282   private static final String RESULT = "result";
283 }
284