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