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