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 
testMakeUninterruptible_timeoutPreservedThroughInterruption()119   public void testMakeUninterruptible_timeoutPreservedThroughInterruption()
120       throws ExecutionException {
121 
122     repeatedlyInterruptTestThread(100, tearDownStack);
123 
124     try {
125       getUninterruptibly(delayedFuture, 500, TimeUnit.MILLISECONDS);
126       fail("expected to time out");
127     } catch (TimeoutException expected) {
128     }
129     assertTrue(Thread.interrupted()); // clears the interrupt state, too
130 
131     assertFalse(sleeper.completed);
132     assertTrue(getUninterruptibly(delayedFuture));
133 
134     assertTrue(Thread.interrupted()); // clears the interrupt state, too
135     assertTrue(sleeper.completed);
136   }
137 
138   private static class SleepingRunnable implements Runnable {
139     final int millis;
140     volatile boolean completed;
141 
SleepingRunnable(int millis)142     public SleepingRunnable(int millis) {
143       this.millis = millis;
144     }
145 
146     @Override
run()147     public void run() {
148       try {
149         Thread.sleep(millis);
150       } catch (InterruptedException wontHappen) {
151         throw new AssertionError();
152       }
153       completed = true;
154     }
155   }
156 
testMakeUninterruptible_untimed_uninterrupted()157   public void testMakeUninterruptible_untimed_uninterrupted() throws Exception {
158     runUntimedInterruptsTest(0);
159   }
160 
testMakeUninterruptible_untimed_interrupted()161   public void testMakeUninterruptible_untimed_interrupted() throws Exception {
162     runUntimedInterruptsTest(1);
163   }
164 
testMakeUninterruptible_untimed_multiplyInterrupted()165   public void testMakeUninterruptible_untimed_multiplyInterrupted() throws Exception {
166     runUntimedInterruptsTest(38);
167   }
168 
testMakeUninterruptible_timed_uninterrupted()169   public void testMakeUninterruptible_timed_uninterrupted() throws Exception {
170     runTimedInterruptsTest(0);
171   }
172 
testMakeUninterruptible_timed_interrupted()173   public void testMakeUninterruptible_timed_interrupted() throws Exception {
174     runTimedInterruptsTest(1);
175   }
176 
testMakeUninterruptible_timed_multiplyInterrupted()177   public void testMakeUninterruptible_timed_multiplyInterrupted() throws Exception {
178     runTimedInterruptsTest(38);
179   }
180 
runUntimedInterruptsTest(int times)181   private static void runUntimedInterruptsTest(int times)
182       throws InterruptedException, ExecutionException, TimeoutException {
183     SettableFuture<String> future = SettableFuture.create();
184     FutureTask<Boolean> interruptReporter = untimedInterruptReporter(future, false);
185 
186     runNInterruptsTest(times, future, interruptReporter);
187   }
188 
runTimedInterruptsTest(int times)189   private static void runTimedInterruptsTest(int times)
190       throws InterruptedException, ExecutionException, TimeoutException {
191     SettableFuture<String> future = SettableFuture.create();
192     FutureTask<Boolean> interruptReporter = timedInterruptReporter(future);
193 
194     runNInterruptsTest(times, future, interruptReporter);
195   }
196 
runNInterruptsTest( int times, SettableFuture<String> future, FutureTask<Boolean> interruptReporter)197   private static void runNInterruptsTest(
198       int times, SettableFuture<String> future, FutureTask<Boolean> interruptReporter)
199       throws InterruptedException, ExecutionException, TimeoutException {
200     Thread waitingThread = new Thread(interruptReporter);
201     waitingThread.start();
202     for (int i = 0; i < times; i++) {
203       waitingThread.interrupt();
204     }
205 
206     future.set(RESULT);
207 
208     assertEquals(times > 0, (boolean) interruptReporter.get(20, SECONDS));
209   }
210 
211   /**
212    * Confirms that the test code triggers {@link InterruptedException} in a standard {@link Future}.
213    */
214 
testMakeUninterruptible_plainFutureSanityCheck()215   public void testMakeUninterruptible_plainFutureSanityCheck() throws Exception {
216     SettableFuture<String> future = SettableFuture.create();
217     FutureTask<Boolean> wasInterrupted = untimedInterruptReporter(future, true);
218 
219     Thread waitingThread = new Thread(wasInterrupted);
220     waitingThread.start();
221     waitingThread.interrupt();
222     try {
223       wasInterrupted.get();
224       fail();
225     } catch (ExecutionException expected) {
226       assertTrue(
227           expected.getCause().toString(), expected.getCause() instanceof InterruptedException);
228     }
229   }
230 
testMakeUninterruptible_timedGetZeroTimeoutAttempted()231   public void testMakeUninterruptible_timedGetZeroTimeoutAttempted()
232       throws TimeoutException, ExecutionException {
233     SettableFuture<String> future = SettableFuture.create();
234     future.set(RESULT);
235     /*
236      * getUninterruptibly should call the timed get method once with a
237      * wait of 0 seconds (and it should succeed, since the result is already
238      * available).
239      */
240     assertEquals(RESULT, getUninterruptibly(future, 0, SECONDS));
241   }
242 
testMakeUninterruptible_timedGetNegativeTimeoutAttempted()243   public void testMakeUninterruptible_timedGetNegativeTimeoutAttempted()
244       throws TimeoutException, ExecutionException {
245     SettableFuture<String> future = SettableFuture.create();
246     future.set(RESULT);
247     /*
248      * The getUninterruptibly should call the timed get method once with a
249      * wait of -1 seconds (and it should succeed, since the result is already
250      * available).
251      */
252     assertEquals(RESULT, getUninterruptibly(future, -1, SECONDS));
253   }
254 
untimedInterruptReporter( final Future<?> future, final boolean allowInterruption)255   private static FutureTask<Boolean> untimedInterruptReporter(
256       final Future<?> future, final boolean allowInterruption) {
257     return new FutureTask<>(
258         new Callable<Boolean>() {
259           @Override
260           public Boolean call() throws Exception {
261             Object actual;
262             if (allowInterruption) {
263               actual = future.get();
264             } else {
265               actual = getUninterruptibly(future);
266             }
267             assertEquals(RESULT, actual);
268             return Thread.interrupted();
269           }
270         });
271   }
272 
273   private static FutureTask<Boolean> timedInterruptReporter(final Future<?> future) {
274     return new FutureTask<>(
275         new Callable<Boolean>() {
276           @Override
277           public Boolean call() throws Exception {
278             assertEquals(RESULT, getUninterruptibly(future, 10, MINUTES));
279             return Thread.interrupted();
280           }
281         });
282   }
283 
284   private static final String RESULT = "result";
285 }
286