001/* 002 * Copyright (C) 2011 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014 015package com.google.common.util.concurrent; 016 017import static java.util.concurrent.TimeUnit.NANOSECONDS; 018 019import com.google.common.annotations.Beta; 020import com.google.common.annotations.GwtCompatible; 021import com.google.common.annotations.GwtIncompatible; 022import com.google.common.base.Preconditions; 023import com.google.errorprone.annotations.CanIgnoreReturnValue; 024import java.util.concurrent.BlockingQueue; 025import java.util.concurrent.CancellationException; 026import java.util.concurrent.CountDownLatch; 027import java.util.concurrent.ExecutionException; 028import java.util.concurrent.Future; 029import java.util.concurrent.Semaphore; 030import java.util.concurrent.TimeUnit; 031import java.util.concurrent.TimeoutException; 032import java.util.concurrent.locks.Condition; 033 034/** 035 * Utilities for treating interruptible operations as uninterruptible. In all cases, if a thread is 036 * interrupted during such a call, the call continues to block until the result is available or the 037 * timeout elapses, and only then re-interrupts the thread. 038 * 039 * @author Anthony Zana 040 * @since 10.0 041 */ 042@Beta 043@GwtCompatible(emulated = true) 044public final class Uninterruptibles { 045 046 // Implementation Note: As of 3-7-11, the logic for each blocking/timeout 047 // methods is identical, save for method being invoked. 048 049 /** Invokes {@code latch.}{@link CountDownLatch#await() await()} uninterruptibly. */ 050 @GwtIncompatible // concurrency 051 public static void awaitUninterruptibly(CountDownLatch latch) { 052 boolean interrupted = false; 053 try { 054 while (true) { 055 try { 056 latch.await(); 057 return; 058 } catch (InterruptedException e) { 059 interrupted = true; 060 } 061 } 062 } finally { 063 if (interrupted) { 064 Thread.currentThread().interrupt(); 065 } 066 } 067 } 068 069 /** 070 * Invokes {@code latch.}{@link CountDownLatch#await(long, TimeUnit) await(timeout, unit)} 071 * uninterruptibly. 072 */ 073 @CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict. 074 @GwtIncompatible // concurrency 075 @SuppressWarnings("GoodTime") // should accept a java.time.Duration 076 public static boolean awaitUninterruptibly(CountDownLatch latch, long timeout, TimeUnit unit) { 077 boolean interrupted = false; 078 try { 079 long remainingNanos = unit.toNanos(timeout); 080 long end = System.nanoTime() + remainingNanos; 081 082 while (true) { 083 try { 084 // CountDownLatch treats negative timeouts just like zero. 085 return latch.await(remainingNanos, NANOSECONDS); 086 } catch (InterruptedException e) { 087 interrupted = true; 088 remainingNanos = end - System.nanoTime(); 089 } 090 } 091 } finally { 092 if (interrupted) { 093 Thread.currentThread().interrupt(); 094 } 095 } 096 } 097 098 /** 099 * Invokes {@code condition.}{@link Condition#await(long, TimeUnit) await(timeout, unit)} 100 * uninterruptibly. 101 * 102 * @since 23.6 103 */ 104 @GwtIncompatible // concurrency 105 @SuppressWarnings("GoodTime") // should accept a java.time.Duration 106 public static boolean awaitUninterruptibly(Condition condition, long timeout, TimeUnit unit) { 107 boolean interrupted = false; 108 try { 109 long remainingNanos = unit.toNanos(timeout); 110 long end = System.nanoTime() + remainingNanos; 111 112 while (true) { 113 try { 114 return condition.await(remainingNanos, NANOSECONDS); 115 } catch (InterruptedException e) { 116 interrupted = true; 117 remainingNanos = end - System.nanoTime(); 118 } 119 } 120 } finally { 121 if (interrupted) { 122 Thread.currentThread().interrupt(); 123 } 124 } 125 } 126 127 /** Invokes {@code toJoin.}{@link Thread#join() join()} uninterruptibly. */ 128 @GwtIncompatible // concurrency 129 public static void joinUninterruptibly(Thread toJoin) { 130 boolean interrupted = false; 131 try { 132 while (true) { 133 try { 134 toJoin.join(); 135 return; 136 } catch (InterruptedException e) { 137 interrupted = true; 138 } 139 } 140 } finally { 141 if (interrupted) { 142 Thread.currentThread().interrupt(); 143 } 144 } 145 } 146 147 /** 148 * Invokes {@code unit.}{@link TimeUnit#timedJoin(Thread, long) timedJoin(toJoin, timeout)} 149 * uninterruptibly. 150 */ 151 @GwtIncompatible // concurrency 152 @SuppressWarnings("GoodTime") // should accept a java.time.Duration 153 public static void joinUninterruptibly(Thread toJoin, long timeout, TimeUnit unit) { 154 Preconditions.checkNotNull(toJoin); 155 boolean interrupted = false; 156 try { 157 long remainingNanos = unit.toNanos(timeout); 158 long end = System.nanoTime() + remainingNanos; 159 while (true) { 160 try { 161 // TimeUnit.timedJoin() treats negative timeouts just like zero. 162 NANOSECONDS.timedJoin(toJoin, remainingNanos); 163 return; 164 } catch (InterruptedException e) { 165 interrupted = true; 166 remainingNanos = end - System.nanoTime(); 167 } 168 } 169 } finally { 170 if (interrupted) { 171 Thread.currentThread().interrupt(); 172 } 173 } 174 } 175 176 /** 177 * Invokes {@code future.}{@link Future#get() get()} uninterruptibly. 178 * 179 * <p>Similar methods: 180 * 181 * <ul> 182 * <li>To retrieve a result from a {@code Future} that is already done, use {@link 183 * Futures#getDone Futures.getDone}. 184 * <li>To treat {@link InterruptedException} uniformly with other exceptions, use {@link 185 * Futures#getChecked(Future, Class) Futures.getChecked}. 186 * <li>To get uninterruptibility and remove checked exceptions, use {@link 187 * Futures#getUnchecked}. 188 * </ul> 189 * 190 * @throws ExecutionException if the computation threw an exception 191 * @throws CancellationException if the computation was cancelled 192 */ 193 @CanIgnoreReturnValue 194 public static <V> V getUninterruptibly(Future<V> future) throws ExecutionException { 195 boolean interrupted = false; 196 try { 197 while (true) { 198 try { 199 return future.get(); 200 } catch (InterruptedException e) { 201 interrupted = true; 202 } 203 } 204 } finally { 205 if (interrupted) { 206 Thread.currentThread().interrupt(); 207 } 208 } 209 } 210 211 /** 212 * Invokes {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)} uninterruptibly. 213 * 214 * <p>Similar methods: 215 * 216 * <ul> 217 * <li>To retrieve a result from a {@code Future} that is already done, use {@link 218 * Futures#getDone Futures.getDone}. 219 * <li>To treat {@link InterruptedException} uniformly with other exceptions, use {@link 220 * Futures#getChecked(Future, Class, long, TimeUnit) Futures.getChecked}. 221 * <li>To get uninterruptibility and remove checked exceptions, use {@link 222 * Futures#getUnchecked}. 223 * </ul> 224 * 225 * @throws ExecutionException if the computation threw an exception 226 * @throws CancellationException if the computation was cancelled 227 * @throws TimeoutException if the wait timed out 228 */ 229 @CanIgnoreReturnValue 230 @GwtIncompatible // TODO 231 @SuppressWarnings("GoodTime") // should accept a java.time.Duration 232 public static <V> V getUninterruptibly(Future<V> future, long timeout, TimeUnit unit) 233 throws ExecutionException, TimeoutException { 234 boolean interrupted = false; 235 try { 236 long remainingNanos = unit.toNanos(timeout); 237 long end = System.nanoTime() + remainingNanos; 238 239 while (true) { 240 try { 241 // Future treats negative timeouts just like zero. 242 return future.get(remainingNanos, NANOSECONDS); 243 } catch (InterruptedException e) { 244 interrupted = true; 245 remainingNanos = end - System.nanoTime(); 246 } 247 } 248 } finally { 249 if (interrupted) { 250 Thread.currentThread().interrupt(); 251 } 252 } 253 } 254 255 /** Invokes {@code queue.}{@link BlockingQueue#take() take()} uninterruptibly. */ 256 @GwtIncompatible // concurrency 257 public static <E> E takeUninterruptibly(BlockingQueue<E> queue) { 258 boolean interrupted = false; 259 try { 260 while (true) { 261 try { 262 return queue.take(); 263 } catch (InterruptedException e) { 264 interrupted = true; 265 } 266 } 267 } finally { 268 if (interrupted) { 269 Thread.currentThread().interrupt(); 270 } 271 } 272 } 273 274 /** 275 * Invokes {@code queue.}{@link BlockingQueue#put(Object) put(element)} uninterruptibly. 276 * 277 * @throws ClassCastException if the class of the specified element prevents it from being added 278 * to the given queue 279 * @throws IllegalArgumentException if some property of the specified element prevents it from 280 * being added to the given queue 281 */ 282 @GwtIncompatible // concurrency 283 public static <E> void putUninterruptibly(BlockingQueue<E> queue, E element) { 284 boolean interrupted = false; 285 try { 286 while (true) { 287 try { 288 queue.put(element); 289 return; 290 } catch (InterruptedException e) { 291 interrupted = true; 292 } 293 } 294 } finally { 295 if (interrupted) { 296 Thread.currentThread().interrupt(); 297 } 298 } 299 } 300 301 // TODO(user): Support Sleeper somehow (wrapper or interface method)? 302 /** Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)} uninterruptibly. */ 303 @GwtIncompatible // concurrency 304 @SuppressWarnings("GoodTime") // should accept a java.time.Duration 305 public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) { 306 boolean interrupted = false; 307 try { 308 long remainingNanos = unit.toNanos(sleepFor); 309 long end = System.nanoTime() + remainingNanos; 310 while (true) { 311 try { 312 // TimeUnit.sleep() treats negative timeouts just like zero. 313 NANOSECONDS.sleep(remainingNanos); 314 return; 315 } catch (InterruptedException e) { 316 interrupted = true; 317 remainingNanos = end - System.nanoTime(); 318 } 319 } 320 } finally { 321 if (interrupted) { 322 Thread.currentThread().interrupt(); 323 } 324 } 325 } 326 327 /** 328 * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(1, 329 * timeout, unit)} uninterruptibly. 330 * 331 * @since 18.0 332 */ 333 @GwtIncompatible // concurrency 334 @SuppressWarnings("GoodTime") // should accept a java.time.Duration 335 public static boolean tryAcquireUninterruptibly( 336 Semaphore semaphore, long timeout, TimeUnit unit) { 337 return tryAcquireUninterruptibly(semaphore, 1, timeout, unit); 338 } 339 340 /** 341 * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(permits, 342 * timeout, unit)} uninterruptibly. 343 * 344 * @since 18.0 345 */ 346 @GwtIncompatible // concurrency 347 @SuppressWarnings("GoodTime") // should accept a java.time.Duration 348 public static boolean tryAcquireUninterruptibly( 349 Semaphore semaphore, int permits, long timeout, TimeUnit unit) { 350 boolean interrupted = false; 351 try { 352 long remainingNanos = unit.toNanos(timeout); 353 long end = System.nanoTime() + remainingNanos; 354 355 while (true) { 356 try { 357 // Semaphore treats negative timeouts just like zero. 358 return semaphore.tryAcquire(permits, remainingNanos, NANOSECONDS); 359 } catch (InterruptedException e) { 360 interrupted = true; 361 remainingNanos = end - System.nanoTime(); 362 } 363 } 364 } finally { 365 if (interrupted) { 366 Thread.currentThread().interrupt(); 367 } 368 } 369 } 370 371 // TODO(user): Add support for waitUninterruptibly. 372 373 private Uninterruptibles() {} 374}