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