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