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(azana/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(10000, 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