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