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