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