• 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 import static org.junit.Assert.assertThrows;
24 
25 import com.google.common.testing.TearDown;
26 import com.google.common.testing.TearDownStack;
27 import java.util.concurrent.Callable;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.ExecutorService;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.FutureTask;
33 import java.util.concurrent.TimeUnit;
34 import java.util.concurrent.TimeoutException;
35 import junit.framework.TestCase;
36 
37 // TODO(cpovirk): Should this be merged into UninterruptiblesTest?
38 /**
39  * Unit test for {@link Uninterruptibles#getUninterruptibly}
40  *
41  * @author Kevin Bourrillion
42  * @author Chris Povirk
43  */
44 public class UninterruptibleFutureTest extends TestCase {
45   private SleepingRunnable sleeper;
46   private Future<Boolean> delayedFuture;
47 
48   private final TearDownStack tearDownStack = new TearDownStack();
49 
50   @Override
setUp()51   protected void setUp() {
52     final ExecutorService executor = Executors.newSingleThreadExecutor();
53     tearDownStack.addTearDown(
54         new TearDown() {
55           @Override
56           public void tearDown() {
57             executor.shutdownNow();
58           }
59         });
60     sleeper = new SleepingRunnable(1000);
61     delayedFuture = executor.submit(sleeper, true);
62 
63     tearDownStack.addTearDown(
64         new TearDown() {
65           @Override
66           public void tearDown() {
67             Thread.interrupted();
68           }
69         });
70   }
71 
72   @Override
tearDown()73   protected void tearDown() {
74     tearDownStack.runTearDown();
75   }
76 
77   /**
78    * This first test doesn't test anything in Uninterruptibles, just demonstrates some normal
79    * behavior of futures so that you can contrast the next test with it.
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     assertThrows(
125         TimeoutException.class,
126         () -> getUninterruptibly(delayedFuture, 500, TimeUnit.MILLISECONDS));
127     assertTrue(Thread.interrupted()); // clears the interrupt state, too
128 
129     assertFalse(sleeper.completed);
130     assertTrue(getUninterruptibly(delayedFuture));
131 
132     assertTrue(Thread.interrupted()); // clears the interrupt state, too
133     assertTrue(sleeper.completed);
134   }
135 
136   private static class SleepingRunnable implements Runnable {
137     final int millis;
138     volatile boolean completed;
139 
SleepingRunnable(int millis)140     public SleepingRunnable(int millis) {
141       this.millis = millis;
142     }
143 
144     @Override
run()145     public void run() {
146       try {
147         Thread.sleep(millis);
148       } catch (InterruptedException wontHappen) {
149         throw new AssertionError();
150       }
151       completed = true;
152     }
153   }
154 
testMakeUninterruptible_untimed_uninterrupted()155   public void testMakeUninterruptible_untimed_uninterrupted() throws Exception {
156     runUntimedInterruptsTest(0);
157   }
158 
testMakeUninterruptible_untimed_interrupted()159   public void testMakeUninterruptible_untimed_interrupted() throws Exception {
160     runUntimedInterruptsTest(1);
161   }
162 
testMakeUninterruptible_untimed_multiplyInterrupted()163   public void testMakeUninterruptible_untimed_multiplyInterrupted() throws Exception {
164     runUntimedInterruptsTest(38);
165   }
166 
testMakeUninterruptible_timed_uninterrupted()167   public void testMakeUninterruptible_timed_uninterrupted() throws Exception {
168     runTimedInterruptsTest(0);
169   }
170 
testMakeUninterruptible_timed_interrupted()171   public void testMakeUninterruptible_timed_interrupted() throws Exception {
172     runTimedInterruptsTest(1);
173   }
174 
testMakeUninterruptible_timed_multiplyInterrupted()175   public void testMakeUninterruptible_timed_multiplyInterrupted() throws Exception {
176     runTimedInterruptsTest(38);
177   }
178 
runUntimedInterruptsTest(int times)179   private static void runUntimedInterruptsTest(int times)
180       throws InterruptedException, ExecutionException, TimeoutException {
181     SettableFuture<String> future = SettableFuture.create();
182     FutureTask<Boolean> interruptReporter = untimedInterruptReporter(future, false);
183 
184     runNInterruptsTest(times, future, interruptReporter);
185   }
186 
runTimedInterruptsTest(int times)187   private static void runTimedInterruptsTest(int times)
188       throws InterruptedException, ExecutionException, TimeoutException {
189     SettableFuture<String> future = SettableFuture.create();
190     FutureTask<Boolean> interruptReporter = timedInterruptReporter(future);
191 
192     runNInterruptsTest(times, future, interruptReporter);
193   }
194 
runNInterruptsTest( int times, SettableFuture<String> future, FutureTask<Boolean> interruptReporter)195   private static void runNInterruptsTest(
196       int times, SettableFuture<String> future, FutureTask<Boolean> interruptReporter)
197       throws InterruptedException, ExecutionException, TimeoutException {
198     Thread waitingThread = new Thread(interruptReporter);
199     waitingThread.start();
200     for (int i = 0; i < times; i++) {
201       waitingThread.interrupt();
202     }
203 
204     future.set(RESULT);
205 
206     assertEquals(times > 0, (boolean) interruptReporter.get(20, SECONDS));
207   }
208 
209   /**
210    * Confirms that the test code triggers {@link InterruptedException} in a standard {@link Future}.
211    */
testMakeUninterruptible_plainFutureSanityCheck()212   public void testMakeUninterruptible_plainFutureSanityCheck() throws Exception {
213     SettableFuture<String> future = SettableFuture.create();
214     FutureTask<Boolean> wasInterrupted = untimedInterruptReporter(future, true);
215 
216     Thread waitingThread = new Thread(wasInterrupted);
217     waitingThread.start();
218     waitingThread.interrupt();
219     ExecutionException expected =
220         assertThrows(ExecutionException.class, () -> wasInterrupted.get());
221     assertTrue(expected.getCause().toString(), expected.getCause() instanceof InterruptedException);
222   }
223 
testMakeUninterruptible_timedGetZeroTimeoutAttempted()224   public void testMakeUninterruptible_timedGetZeroTimeoutAttempted()
225       throws TimeoutException, ExecutionException {
226     SettableFuture<String> future = SettableFuture.create();
227     future.set(RESULT);
228     /*
229      * getUninterruptibly should call the timed get method once with a
230      * wait of 0 seconds (and it should succeed, since the result is already
231      * available).
232      */
233     assertEquals(RESULT, getUninterruptibly(future, 0, SECONDS));
234   }
235 
testMakeUninterruptible_timedGetNegativeTimeoutAttempted()236   public void testMakeUninterruptible_timedGetNegativeTimeoutAttempted()
237       throws TimeoutException, ExecutionException {
238     SettableFuture<String> future = SettableFuture.create();
239     future.set(RESULT);
240     /*
241      * The getUninterruptibly should call the timed get method once with a
242      * wait of -1 seconds (and it should succeed, since the result is already
243      * available).
244      */
245     assertEquals(RESULT, getUninterruptibly(future, -1, SECONDS));
246   }
247 
untimedInterruptReporter( final Future<?> future, final boolean allowInterruption)248   private static FutureTask<Boolean> untimedInterruptReporter(
249       final Future<?> future, final boolean allowInterruption) {
250     return new FutureTask<>(
251         new Callable<Boolean>() {
252           @Override
253           public Boolean call() throws Exception {
254             Object actual;
255             if (allowInterruption) {
256               actual = future.get();
257             } else {
258               actual = getUninterruptibly(future);
259             }
260             assertEquals(RESULT, actual);
261             return Thread.interrupted();
262           }
263         });
264   }
265 
266   private static FutureTask<Boolean> timedInterruptReporter(final Future<?> future) {
267     return new FutureTask<>(
268         new Callable<Boolean>() {
269           @Override
270           public Boolean call() throws Exception {
271             assertEquals(RESULT, getUninterruptibly(future, 10, MINUTES));
272             return Thread.interrupted();
273           }
274         });
275   }
276 
277   private static final String RESULT = "result";
278 }
279