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