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;
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)
046public final class Uninterruptibles {
047
048  // Implementation Note: As of 3-7-11, the logic for each blocking/timeout
049  // methods is identical, save for method being invoked.
050
051  /** Invokes {@code latch.}{@link CountDownLatch#await() await()} uninterruptibly. */
052  @GwtIncompatible // concurrency
053  public static void awaitUninterruptibly(CountDownLatch latch) {
054    boolean interrupted = false;
055    try {
056      while (true) {
057        try {
058          latch.await();
059          return;
060        } catch (InterruptedException e) {
061          interrupted = true;
062        }
063      }
064    } finally {
065      if (interrupted) {
066        Thread.currentThread().interrupt();
067      }
068    }
069  }
070
071  /**
072   * Invokes {@code latch.}{@link CountDownLatch#await(long, TimeUnit) await(timeout, unit)}
073   * uninterruptibly.
074   */
075  @CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict.
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  public static <V> V getUninterruptibly(Future<V> future) throws ExecutionException {
197    boolean interrupted = false;
198    try {
199      while (true) {
200        try {
201          return future.get();
202        } catch (InterruptedException e) {
203          interrupted = true;
204        }
205      }
206    } finally {
207      if (interrupted) {
208        Thread.currentThread().interrupt();
209      }
210    }
211  }
212
213  /**
214   * Invokes {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)} uninterruptibly.
215   *
216   * <p>Similar methods:
217   *
218   * <ul>
219   *   <li>To retrieve a result from a {@code Future} that is already done, use {@link
220   *       Futures#getDone Futures.getDone}.
221   *   <li>To treat {@link InterruptedException} uniformly with other exceptions, use {@link
222   *       Futures#getChecked(Future, Class, long, TimeUnit) Futures.getChecked}.
223   *   <li>To get uninterruptibility and remove checked exceptions, use {@link
224   *       Futures#getUnchecked}.
225   * </ul>
226   *
227   * @throws ExecutionException if the computation threw an exception
228   * @throws CancellationException if the computation was cancelled
229   * @throws TimeoutException if the wait timed out
230   */
231  @CanIgnoreReturnValue
232  @GwtIncompatible // TODO
233  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
234  public static <V> V getUninterruptibly(Future<V> future, long timeout, TimeUnit unit)
235      throws ExecutionException, TimeoutException {
236    boolean interrupted = false;
237    try {
238      long remainingNanos = unit.toNanos(timeout);
239      long end = System.nanoTime() + remainingNanos;
240
241      while (true) {
242        try {
243          // Future treats negative timeouts just like zero.
244          return future.get(remainingNanos, NANOSECONDS);
245        } catch (InterruptedException e) {
246          interrupted = true;
247          remainingNanos = end - System.nanoTime();
248        }
249      }
250    } finally {
251      if (interrupted) {
252        Thread.currentThread().interrupt();
253      }
254    }
255  }
256
257  /** Invokes {@code queue.}{@link BlockingQueue#take() take()} uninterruptibly. */
258  @GwtIncompatible // concurrency
259  public static <E> E takeUninterruptibly(BlockingQueue<E> queue) {
260    boolean interrupted = false;
261    try {
262      while (true) {
263        try {
264          return queue.take();
265        } catch (InterruptedException e) {
266          interrupted = true;
267        }
268      }
269    } finally {
270      if (interrupted) {
271        Thread.currentThread().interrupt();
272      }
273    }
274  }
275
276  /**
277   * Invokes {@code queue.}{@link BlockingQueue#put(Object) put(element)} uninterruptibly.
278   *
279   * @throws ClassCastException if the class of the specified element prevents it from being added
280   *     to the given queue
281   * @throws IllegalArgumentException if some property of the specified element prevents it from
282   *     being added to the given queue
283   */
284  @GwtIncompatible // concurrency
285  public static <E> void putUninterruptibly(BlockingQueue<E> queue, E element) {
286    boolean interrupted = false;
287    try {
288      while (true) {
289        try {
290          queue.put(element);
291          return;
292        } catch (InterruptedException e) {
293          interrupted = true;
294        }
295      }
296    } finally {
297      if (interrupted) {
298        Thread.currentThread().interrupt();
299      }
300    }
301  }
302
303  // TODO(user): Support Sleeper somehow (wrapper or interface method)?
304  /** Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)} uninterruptibly. */
305  @GwtIncompatible // concurrency
306  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
307  public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) {
308    boolean interrupted = false;
309    try {
310      long remainingNanos = unit.toNanos(sleepFor);
311      long end = System.nanoTime() + remainingNanos;
312      while (true) {
313        try {
314          // TimeUnit.sleep() treats negative timeouts just like zero.
315          NANOSECONDS.sleep(remainingNanos);
316          return;
317        } catch (InterruptedException e) {
318          interrupted = true;
319          remainingNanos = end - System.nanoTime();
320        }
321      }
322    } finally {
323      if (interrupted) {
324        Thread.currentThread().interrupt();
325      }
326    }
327  }
328
329  /**
330   * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(1,
331   * timeout, unit)} uninterruptibly.
332   *
333   * @since 18.0
334   */
335  @GwtIncompatible // concurrency
336  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
337  public static boolean tryAcquireUninterruptibly(
338      Semaphore semaphore, long timeout, TimeUnit unit) {
339    return tryAcquireUninterruptibly(semaphore, 1, timeout, unit);
340  }
341
342  /**
343   * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(permits,
344   * timeout, unit)} uninterruptibly.
345   *
346   * @since 18.0
347   */
348  @GwtIncompatible // concurrency
349  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
350  public static boolean tryAcquireUninterruptibly(
351      Semaphore semaphore, int permits, long timeout, TimeUnit unit) {
352    boolean interrupted = false;
353    try {
354      long remainingNanos = unit.toNanos(timeout);
355      long end = System.nanoTime() + remainingNanos;
356
357      while (true) {
358        try {
359          // Semaphore treats negative timeouts just like zero.
360          return semaphore.tryAcquire(permits, remainingNanos, NANOSECONDS);
361        } catch (InterruptedException e) {
362          interrupted = true;
363          remainingNanos = end - System.nanoTime();
364        }
365      }
366    } finally {
367      if (interrupted) {
368        Thread.currentThread().interrupt();
369      }
370    }
371  }
372
373  /**
374   * Invokes {@code lock.}{@link Lock#tryLock(long, TimeUnit) tryLock(timeout, unit)}
375   * uninterruptibly.
376   *
377   * @since 30.0
378   */
379  @GwtIncompatible // concurrency
380  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
381  public static boolean tryLockUninterruptibly(Lock lock, long timeout, TimeUnit unit) {
382    boolean interrupted = false;
383    try {
384      long remainingNanos = unit.toNanos(timeout);
385      long end = System.nanoTime() + remainingNanos;
386
387      while (true) {
388        try {
389          return lock.tryLock(remainingNanos, NANOSECONDS);
390        } catch (InterruptedException e) {
391          interrupted = true;
392          remainingNanos = end - System.nanoTime();
393        }
394      }
395    } finally {
396      if (interrupted) {
397        Thread.currentThread().interrupt();
398      }
399    }
400  }
401
402  /**
403   * Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit)
404   * awaitTermination(long, TimeUnit)} uninterruptibly with no timeout.
405   *
406   * @since 30.0
407   */
408  @Beta
409  @GwtIncompatible // concurrency
410  public static void awaitTerminationUninterruptibly(ExecutorService executor) {
411    // TODO(cpovirk): We could optimize this to avoid calling nanoTime() at all.
412    verify(awaitTerminationUninterruptibly(executor, Long.MAX_VALUE, NANOSECONDS));
413  }
414
415  /**
416   * Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit)
417   * awaitTermination(long, TimeUnit)} uninterruptibly.
418   *
419   * @since 30.0
420   */
421  @Beta
422  @GwtIncompatible // concurrency
423  @SuppressWarnings("GoodTime")
424  public static boolean awaitTerminationUninterruptibly(
425      ExecutorService executor, long timeout, TimeUnit unit) {
426    boolean interrupted = false;
427    try {
428      long remainingNanos = unit.toNanos(timeout);
429      long end = System.nanoTime() + remainingNanos;
430
431      while (true) {
432        try {
433          return executor.awaitTermination(remainingNanos, NANOSECONDS);
434        } catch (InterruptedException e) {
435          interrupted = true;
436          remainingNanos = end - System.nanoTime();
437        }
438      }
439    } finally {
440      if (interrupted) {
441        Thread.currentThread().interrupt();
442      }
443    }
444  }
445
446  // TODO(user): Add support for waitUninterruptibly.
447
448  private Uninterruptibles() {}
449}