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