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