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 com.google.common.base.Verify.verify;
018import static com.google.common.util.concurrent.Internal.toNanosSaturated;
019import static java.util.concurrent.TimeUnit.NANOSECONDS;
020
021import com.google.common.annotations.Beta;
022import com.google.common.annotations.GwtCompatible;
023import com.google.common.annotations.GwtIncompatible;
024import com.google.common.base.Preconditions;
025import com.google.errorprone.annotations.CanIgnoreReturnValue;
026import java.time.Duration;
027import java.util.concurrent.BlockingQueue;
028import java.util.concurrent.CancellationException;
029import java.util.concurrent.CountDownLatch;
030import java.util.concurrent.ExecutionException;
031import java.util.concurrent.ExecutorService;
032import java.util.concurrent.Future;
033import java.util.concurrent.Semaphore;
034import java.util.concurrent.TimeUnit;
035import java.util.concurrent.TimeoutException;
036import java.util.concurrent.locks.Condition;
037import java.util.concurrent.locks.Lock;
038import org.checkerframework.checker.nullness.qual.Nullable;
039
040/**
041 * Utilities for treating interruptible operations as uninterruptible. In all cases, if a thread is
042 * interrupted during such a call, the call continues to block until the result is available or the
043 * timeout elapses, and only then re-interrupts the thread.
044 *
045 * @author Anthony Zana
046 * @since 10.0
047 */
048@GwtCompatible(emulated = true)
049@ElementTypesAreNonnullByDefault
050public final class Uninterruptibles {
051
052  // Implementation Note: As of 3-7-11, the logic for each blocking/timeout
053  // methods is identical, save for method being invoked.
054
055  /** Invokes {@code latch.}{@link CountDownLatch#await() await()} uninterruptibly. */
056  @GwtIncompatible // concurrency
057  public static void awaitUninterruptibly(CountDownLatch latch) {
058    boolean interrupted = false;
059    try {
060      while (true) {
061        try {
062          latch.await();
063          return;
064        } catch (InterruptedException e) {
065          interrupted = true;
066        }
067      }
068    } finally {
069      if (interrupted) {
070        Thread.currentThread().interrupt();
071      }
072    }
073  }
074
075  /**
076   * Invokes {@code latch.}{@link CountDownLatch#await(long, TimeUnit) await(timeout, unit)}
077   * uninterruptibly.
078   *
079   * @since 28.0
080   */
081  @CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict.
082  @GwtIncompatible // concurrency
083  @Beta
084  public static boolean awaitUninterruptibly(CountDownLatch latch, Duration timeout) {
085    return awaitUninterruptibly(latch, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
086  }
087
088  /**
089   * Invokes {@code latch.}{@link CountDownLatch#await(long, TimeUnit) await(timeout, unit)}
090   * uninterruptibly.
091   */
092  @CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict.
093  @GwtIncompatible // concurrency
094  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
095  public static boolean awaitUninterruptibly(CountDownLatch latch, long timeout, TimeUnit unit) {
096    boolean interrupted = false;
097    try {
098      long remainingNanos = unit.toNanos(timeout);
099      long end = System.nanoTime() + remainingNanos;
100
101      while (true) {
102        try {
103          // CountDownLatch treats negative timeouts just like zero.
104          return latch.await(remainingNanos, NANOSECONDS);
105        } catch (InterruptedException e) {
106          interrupted = true;
107          remainingNanos = end - System.nanoTime();
108        }
109      }
110    } finally {
111      if (interrupted) {
112        Thread.currentThread().interrupt();
113      }
114    }
115  }
116
117  /**
118   * Invokes {@code condition.}{@link Condition#await(long, TimeUnit) await(timeout, unit)}
119   * uninterruptibly.
120   *
121   * @since 28.0
122   */
123  @GwtIncompatible // concurrency
124  @Beta
125  public static boolean awaitUninterruptibly(Condition condition, Duration timeout) {
126    return awaitUninterruptibly(condition, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
127  }
128
129  /**
130   * Invokes {@code condition.}{@link Condition#await(long, TimeUnit) await(timeout, unit)}
131   * uninterruptibly.
132   *
133   * @since 23.6
134   */
135  @GwtIncompatible // concurrency
136  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
137  public static boolean awaitUninterruptibly(Condition condition, long timeout, TimeUnit unit) {
138    boolean interrupted = false;
139    try {
140      long remainingNanos = unit.toNanos(timeout);
141      long end = System.nanoTime() + remainingNanos;
142
143      while (true) {
144        try {
145          return condition.await(remainingNanos, NANOSECONDS);
146        } catch (InterruptedException e) {
147          interrupted = true;
148          remainingNanos = end - System.nanoTime();
149        }
150      }
151    } finally {
152      if (interrupted) {
153        Thread.currentThread().interrupt();
154      }
155    }
156  }
157
158  /** Invokes {@code toJoin.}{@link Thread#join() join()} uninterruptibly. */
159  @GwtIncompatible // concurrency
160  public static void joinUninterruptibly(Thread toJoin) {
161    boolean interrupted = false;
162    try {
163      while (true) {
164        try {
165          toJoin.join();
166          return;
167        } catch (InterruptedException e) {
168          interrupted = true;
169        }
170      }
171    } finally {
172      if (interrupted) {
173        Thread.currentThread().interrupt();
174      }
175    }
176  }
177
178  /**
179   * Invokes {@code unit.}{@link TimeUnit#timedJoin(Thread, long) timedJoin(toJoin, timeout)}
180   * uninterruptibly.
181   *
182   * @since 28.0
183   */
184  @GwtIncompatible // concurrency
185  @Beta
186  public static void joinUninterruptibly(Thread toJoin, Duration timeout) {
187    joinUninterruptibly(toJoin, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
188  }
189
190  /**
191   * Invokes {@code unit.}{@link TimeUnit#timedJoin(Thread, long) timedJoin(toJoin, timeout)}
192   * uninterruptibly.
193   */
194  @GwtIncompatible // concurrency
195  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
196  public static void joinUninterruptibly(Thread toJoin, long timeout, TimeUnit unit) {
197    Preconditions.checkNotNull(toJoin);
198    boolean interrupted = false;
199    try {
200      long remainingNanos = unit.toNanos(timeout);
201      long end = System.nanoTime() + remainingNanos;
202      while (true) {
203        try {
204          // TimeUnit.timedJoin() treats negative timeouts just like zero.
205          NANOSECONDS.timedJoin(toJoin, remainingNanos);
206          return;
207        } catch (InterruptedException e) {
208          interrupted = true;
209          remainingNanos = end - System.nanoTime();
210        }
211      }
212    } finally {
213      if (interrupted) {
214        Thread.currentThread().interrupt();
215      }
216    }
217  }
218
219  /**
220   * Invokes {@code future.}{@link Future#get() get()} uninterruptibly.
221   *
222   * <p>Similar methods:
223   *
224   * <ul>
225   *   <li>To retrieve a result from a {@code Future} that is already done, use {@link
226   *       Futures#getDone Futures.getDone}.
227   *   <li>To treat {@link InterruptedException} uniformly with other exceptions, use {@link
228   *       Futures#getChecked(Future, Class) Futures.getChecked}.
229   *   <li>To get uninterruptibility and remove checked exceptions, use {@link
230   *       Futures#getUnchecked}.
231   * </ul>
232   *
233   * @throws ExecutionException if the computation threw an exception
234   * @throws CancellationException if the computation was cancelled
235   */
236  @CanIgnoreReturnValue
237  @ParametricNullness
238  public static <V extends @Nullable Object> V getUninterruptibly(Future<V> future)
239      throws ExecutionException {
240    boolean interrupted = false;
241    try {
242      while (true) {
243        try {
244          return future.get();
245        } catch (InterruptedException e) {
246          interrupted = true;
247        }
248      }
249    } finally {
250      if (interrupted) {
251        Thread.currentThread().interrupt();
252      }
253    }
254  }
255
256  /**
257   * Invokes {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)} uninterruptibly.
258   *
259   * <p>Similar methods:
260   *
261   * <ul>
262   *   <li>To retrieve a result from a {@code Future} that is already done, use {@link
263   *       Futures#getDone Futures.getDone}.
264   *   <li>To treat {@link InterruptedException} uniformly with other exceptions, use {@link
265   *       Futures#getChecked(Future, Class, long, TimeUnit) Futures.getChecked}.
266   *   <li>To get uninterruptibility and remove checked exceptions, use {@link
267   *       Futures#getUnchecked}.
268   * </ul>
269   *
270   * @throws ExecutionException if the computation threw an exception
271   * @throws CancellationException if the computation was cancelled
272   * @throws TimeoutException if the wait timed out
273   * @since 28.0
274   */
275  @CanIgnoreReturnValue
276  @GwtIncompatible // java.time.Duration
277  @Beta
278  @ParametricNullness
279  public static <V extends @Nullable Object> V getUninterruptibly(
280      Future<V> future, Duration timeout) throws ExecutionException, TimeoutException {
281    return getUninterruptibly(future, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
282  }
283
284  /**
285   * Invokes {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)} uninterruptibly.
286   *
287   * <p>Similar methods:
288   *
289   * <ul>
290   *   <li>To retrieve a result from a {@code Future} that is already done, use {@link
291   *       Futures#getDone Futures.getDone}.
292   *   <li>To treat {@link InterruptedException} uniformly with other exceptions, use {@link
293   *       Futures#getChecked(Future, Class, long, TimeUnit) Futures.getChecked}.
294   *   <li>To get uninterruptibility and remove checked exceptions, use {@link
295   *       Futures#getUnchecked}.
296   * </ul>
297   *
298   * @throws ExecutionException if the computation threw an exception
299   * @throws CancellationException if the computation was cancelled
300   * @throws TimeoutException if the wait timed out
301   */
302  @CanIgnoreReturnValue
303  @GwtIncompatible // TODO
304  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
305  @ParametricNullness
306  public static <V extends @Nullable Object> V getUninterruptibly(
307      Future<V> future, long timeout, TimeUnit unit) throws ExecutionException, TimeoutException {
308    boolean interrupted = false;
309    try {
310      long remainingNanos = unit.toNanos(timeout);
311      long end = System.nanoTime() + remainingNanos;
312
313      while (true) {
314        try {
315          // Future treats negative timeouts just like zero.
316          return future.get(remainingNanos, NANOSECONDS);
317        } catch (InterruptedException e) {
318          interrupted = true;
319          remainingNanos = end - System.nanoTime();
320        }
321      }
322    } finally {
323      if (interrupted) {
324        Thread.currentThread().interrupt();
325      }
326    }
327  }
328
329  /** Invokes {@code queue.}{@link BlockingQueue#take() take()} uninterruptibly. */
330  @GwtIncompatible // concurrency
331  public static <E> E takeUninterruptibly(BlockingQueue<E> queue) {
332    boolean interrupted = false;
333    try {
334      while (true) {
335        try {
336          return queue.take();
337        } catch (InterruptedException e) {
338          interrupted = true;
339        }
340      }
341    } finally {
342      if (interrupted) {
343        Thread.currentThread().interrupt();
344      }
345    }
346  }
347
348  /**
349   * Invokes {@code queue.}{@link BlockingQueue#put(Object) put(element)} uninterruptibly.
350   *
351   * @throws ClassCastException if the class of the specified element prevents it from being added
352   *     to the given queue
353   * @throws IllegalArgumentException if some property of the specified element prevents it from
354   *     being added to the given queue
355   */
356  @GwtIncompatible // concurrency
357  public static <E> void putUninterruptibly(BlockingQueue<E> queue, E element) {
358    boolean interrupted = false;
359    try {
360      while (true) {
361        try {
362          queue.put(element);
363          return;
364        } catch (InterruptedException e) {
365          interrupted = true;
366        }
367      }
368    } finally {
369      if (interrupted) {
370        Thread.currentThread().interrupt();
371      }
372    }
373  }
374
375  // TODO(user): Support Sleeper somehow (wrapper or interface method)?
376  /**
377   * Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)} uninterruptibly.
378   *
379   * @since 28.0
380   */
381  @GwtIncompatible // concurrency
382  @Beta
383  public static void sleepUninterruptibly(Duration sleepFor) {
384    sleepUninterruptibly(toNanosSaturated(sleepFor), TimeUnit.NANOSECONDS);
385  }
386
387  // TODO(user): Support Sleeper somehow (wrapper or interface method)?
388  /** Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)} uninterruptibly. */
389  @GwtIncompatible // concurrency
390  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
391  public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) {
392    boolean interrupted = false;
393    try {
394      long remainingNanos = unit.toNanos(sleepFor);
395      long end = System.nanoTime() + remainingNanos;
396      while (true) {
397        try {
398          // TimeUnit.sleep() treats negative timeouts just like zero.
399          NANOSECONDS.sleep(remainingNanos);
400          return;
401        } catch (InterruptedException e) {
402          interrupted = true;
403          remainingNanos = end - System.nanoTime();
404        }
405      }
406    } finally {
407      if (interrupted) {
408        Thread.currentThread().interrupt();
409      }
410    }
411  }
412
413  /**
414   * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(1,
415   * timeout, unit)} uninterruptibly.
416   *
417   * @since 28.0
418   */
419  @GwtIncompatible // concurrency
420  @Beta
421  public static boolean tryAcquireUninterruptibly(Semaphore semaphore, Duration timeout) {
422    return tryAcquireUninterruptibly(semaphore, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
423  }
424
425  /**
426   * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(1,
427   * timeout, unit)} uninterruptibly.
428   *
429   * @since 18.0
430   */
431  @GwtIncompatible // concurrency
432  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
433  public static boolean tryAcquireUninterruptibly(
434      Semaphore semaphore, long timeout, TimeUnit unit) {
435    return tryAcquireUninterruptibly(semaphore, 1, timeout, unit);
436  }
437
438  /**
439   * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(permits,
440   * timeout, unit)} uninterruptibly.
441   *
442   * @since 28.0
443   */
444  @GwtIncompatible // concurrency
445  @Beta
446  public static boolean tryAcquireUninterruptibly(
447      Semaphore semaphore, int permits, Duration timeout) {
448    return tryAcquireUninterruptibly(
449        semaphore, permits, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
450  }
451
452  /**
453   * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(permits,
454   * timeout, unit)} uninterruptibly.
455   *
456   * @since 18.0
457   */
458  @GwtIncompatible // concurrency
459  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
460  public static boolean tryAcquireUninterruptibly(
461      Semaphore semaphore, int permits, long timeout, TimeUnit unit) {
462    boolean interrupted = false;
463    try {
464      long remainingNanos = unit.toNanos(timeout);
465      long end = System.nanoTime() + remainingNanos;
466
467      while (true) {
468        try {
469          // Semaphore treats negative timeouts just like zero.
470          return semaphore.tryAcquire(permits, remainingNanos, NANOSECONDS);
471        } catch (InterruptedException e) {
472          interrupted = true;
473          remainingNanos = end - System.nanoTime();
474        }
475      }
476    } finally {
477      if (interrupted) {
478        Thread.currentThread().interrupt();
479      }
480    }
481  }
482
483  /**
484   * Invokes {@code lock.}{@link Lock#tryLock(long, TimeUnit) tryLock(timeout, unit)}
485   * uninterruptibly.
486   *
487   * @since 30.0
488   */
489  @GwtIncompatible // concurrency
490  @Beta
491  public static boolean tryLockUninterruptibly(Lock lock, Duration timeout) {
492    return tryLockUninterruptibly(lock, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
493  }
494
495  /**
496   * Invokes {@code lock.}{@link Lock#tryLock(long, TimeUnit) tryLock(timeout, unit)}
497   * uninterruptibly.
498   *
499   * @since 30.0
500   */
501  @GwtIncompatible // concurrency
502  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
503  public static boolean tryLockUninterruptibly(Lock lock, long timeout, TimeUnit unit) {
504    boolean interrupted = false;
505    try {
506      long remainingNanos = unit.toNanos(timeout);
507      long end = System.nanoTime() + remainingNanos;
508
509      while (true) {
510        try {
511          return lock.tryLock(remainingNanos, NANOSECONDS);
512        } catch (InterruptedException e) {
513          interrupted = true;
514          remainingNanos = end - System.nanoTime();
515        }
516      }
517    } finally {
518      if (interrupted) {
519        Thread.currentThread().interrupt();
520      }
521    }
522  }
523
524  /**
525   * Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit)
526   * awaitTermination(long, TimeUnit)} uninterruptibly with no timeout.
527   *
528   * @since 30.0
529   */
530  @Beta
531  @GwtIncompatible // concurrency
532  public static void awaitTerminationUninterruptibly(ExecutorService executor) {
533    // TODO(cpovirk): We could optimize this to avoid calling nanoTime() at all.
534    verify(awaitTerminationUninterruptibly(executor, Long.MAX_VALUE, NANOSECONDS));
535  }
536
537  /**
538   * Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit)
539   * awaitTermination(long, TimeUnit)} uninterruptibly.
540   *
541   * @since 30.0
542   */
543  @Beta
544  @GwtIncompatible // concurrency
545  public static boolean awaitTerminationUninterruptibly(
546      ExecutorService executor, Duration timeout) {
547    return awaitTerminationUninterruptibly(executor, toNanosSaturated(timeout), NANOSECONDS);
548  }
549
550  /**
551   * Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit)
552   * awaitTermination(long, TimeUnit)} uninterruptibly.
553   *
554   * @since 30.0
555   */
556  @Beta
557  @GwtIncompatible // concurrency
558  @SuppressWarnings("GoodTime")
559  public static boolean awaitTerminationUninterruptibly(
560      ExecutorService executor, long timeout, TimeUnit unit) {
561    boolean interrupted = false;
562    try {
563      long remainingNanos = unit.toNanos(timeout);
564      long end = System.nanoTime() + remainingNanos;
565
566      while (true) {
567        try {
568          return executor.awaitTermination(remainingNanos, NANOSECONDS);
569        } catch (InterruptedException e) {
570          interrupted = true;
571          remainingNanos = end - System.nanoTime();
572        }
573      }
574    } finally {
575      if (interrupted) {
576        Thread.currentThread().interrupt();
577      }
578    }
579  }
580
581  // TODO(user): Add support for waitUninterruptibly.
582
583  private Uninterruptibles() {}
584}