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  public static boolean awaitUninterruptibly(CountDownLatch latch, long timeout, TimeUnit unit) {
076    boolean interrupted = false;
077    try {
078      long remainingNanos = unit.toNanos(timeout);
079      long end = System.nanoTime() + remainingNanos;
080
081      while (true) {
082        try {
083          // CountDownLatch treats negative timeouts just like zero.
084          return latch.await(remainingNanos, NANOSECONDS);
085        } catch (InterruptedException e) {
086          interrupted = true;
087          remainingNanos = end - System.nanoTime();
088        }
089      }
090    } finally {
091      if (interrupted) {
092        Thread.currentThread().interrupt();
093      }
094    }
095  }
096
097  /**
098   * Invokes {@code condition.}{@link Condition#await(long, TimeUnit) await(timeout, unit)}
099   * uninterruptibly.
100   *
101   * @since 23.6
102   */
103  @GwtIncompatible // concurrency
104  public static boolean awaitUninterruptibly(Condition condition, long timeout, TimeUnit unit) {
105    boolean interrupted = false;
106    try {
107      long remainingNanos = unit.toNanos(timeout);
108      long end = System.nanoTime() + remainingNanos;
109
110      while (true) {
111        try {
112          return condition.await(remainingNanos, NANOSECONDS);
113        } catch (InterruptedException e) {
114          interrupted = true;
115          remainingNanos = end - System.nanoTime();
116        }
117      }
118    } finally {
119      if (interrupted) {
120        Thread.currentThread().interrupt();
121      }
122    }
123  }
124
125  /** Invokes {@code toJoin.}{@link Thread#join() join()} uninterruptibly. */
126  @GwtIncompatible // concurrency
127  public static void joinUninterruptibly(Thread toJoin) {
128    boolean interrupted = false;
129    try {
130      while (true) {
131        try {
132          toJoin.join();
133          return;
134        } catch (InterruptedException e) {
135          interrupted = true;
136        }
137      }
138    } finally {
139      if (interrupted) {
140        Thread.currentThread().interrupt();
141      }
142    }
143  }
144
145  /**
146   * Invokes {@code future.}{@link Future#get() get()} uninterruptibly.
147   *
148   * <p>Similar methods:
149   *
150   * <ul>
151   *   <li>To retrieve a result from a {@code Future} that is already done, use {@link
152   *       Futures#getDone Futures.getDone}.
153   *   <li>To treat {@link InterruptedException} uniformly with other exceptions, use {@link
154   *       Futures#getChecked(Future, Class) Futures.getChecked}.
155   *   <li>To get uninterruptibility and remove checked exceptions, use {@link
156   *       Futures#getUnchecked}.
157   * </ul>
158   *
159   * @throws ExecutionException if the computation threw an exception
160   * @throws CancellationException if the computation was cancelled
161   */
162  @CanIgnoreReturnValue
163  public static <V> V getUninterruptibly(Future<V> future) throws ExecutionException {
164    boolean interrupted = false;
165    try {
166      while (true) {
167        try {
168          return future.get();
169        } catch (InterruptedException e) {
170          interrupted = true;
171        }
172      }
173    } finally {
174      if (interrupted) {
175        Thread.currentThread().interrupt();
176      }
177    }
178  }
179
180  /**
181   * Invokes {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)} 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, long, TimeUnit) 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   * @throws TimeoutException if the wait timed out
197   */
198  @CanIgnoreReturnValue
199  @GwtIncompatible // TODO
200  public static <V> V getUninterruptibly(Future<V> future, long timeout, TimeUnit unit)
201      throws ExecutionException, TimeoutException {
202    boolean interrupted = false;
203    try {
204      long remainingNanos = unit.toNanos(timeout);
205      long end = System.nanoTime() + remainingNanos;
206
207      while (true) {
208        try {
209          // Future treats negative timeouts just like zero.
210          return future.get(remainingNanos, NANOSECONDS);
211        } catch (InterruptedException e) {
212          interrupted = true;
213          remainingNanos = end - System.nanoTime();
214        }
215      }
216    } finally {
217      if (interrupted) {
218        Thread.currentThread().interrupt();
219      }
220    }
221  }
222
223  /**
224   * Invokes {@code unit.}{@link TimeUnit#timedJoin(Thread, long) timedJoin(toJoin, timeout)}
225   * uninterruptibly.
226   */
227  @GwtIncompatible // concurrency
228  public static void joinUninterruptibly(Thread toJoin, long timeout, TimeUnit unit) {
229    Preconditions.checkNotNull(toJoin);
230    boolean interrupted = false;
231    try {
232      long remainingNanos = unit.toNanos(timeout);
233      long end = System.nanoTime() + remainingNanos;
234      while (true) {
235        try {
236          // TimeUnit.timedJoin() treats negative timeouts just like zero.
237          NANOSECONDS.timedJoin(toJoin, remainingNanos);
238          return;
239        } catch (InterruptedException e) {
240          interrupted = true;
241          remainingNanos = end - System.nanoTime();
242        }
243      }
244    } finally {
245      if (interrupted) {
246        Thread.currentThread().interrupt();
247      }
248    }
249  }
250
251  /** Invokes {@code queue.}{@link BlockingQueue#take() take()} uninterruptibly. */
252  @GwtIncompatible // concurrency
253  public static <E> E takeUninterruptibly(BlockingQueue<E> queue) {
254    boolean interrupted = false;
255    try {
256      while (true) {
257        try {
258          return queue.take();
259        } catch (InterruptedException e) {
260          interrupted = true;
261        }
262      }
263    } finally {
264      if (interrupted) {
265        Thread.currentThread().interrupt();
266      }
267    }
268  }
269
270  /**
271   * Invokes {@code queue.}{@link BlockingQueue#put(Object) put(element)} uninterruptibly.
272   *
273   * @throws ClassCastException if the class of the specified element prevents it from being added
274   *     to the given queue
275   * @throws IllegalArgumentException if some property of the specified element prevents it from
276   *     being added to the given queue
277   */
278  @GwtIncompatible // concurrency
279  public static <E> void putUninterruptibly(BlockingQueue<E> queue, E element) {
280    boolean interrupted = false;
281    try {
282      while (true) {
283        try {
284          queue.put(element);
285          return;
286        } catch (InterruptedException e) {
287          interrupted = true;
288        }
289      }
290    } finally {
291      if (interrupted) {
292        Thread.currentThread().interrupt();
293      }
294    }
295  }
296
297  // TODO(user): Support Sleeper somehow (wrapper or interface method)?
298  /** Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)} uninterruptibly. */
299  @GwtIncompatible // concurrency
300  public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) {
301    boolean interrupted = false;
302    try {
303      long remainingNanos = unit.toNanos(sleepFor);
304      long end = System.nanoTime() + remainingNanos;
305      while (true) {
306        try {
307          // TimeUnit.sleep() treats negative timeouts just like zero.
308          NANOSECONDS.sleep(remainingNanos);
309          return;
310        } catch (InterruptedException e) {
311          interrupted = true;
312          remainingNanos = end - System.nanoTime();
313        }
314      }
315    } finally {
316      if (interrupted) {
317        Thread.currentThread().interrupt();
318      }
319    }
320  }
321
322  /**
323   * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(1,
324   * timeout, unit)} uninterruptibly.
325   *
326   * @since 18.0
327   */
328  @GwtIncompatible // concurrency
329  public static boolean tryAcquireUninterruptibly(
330      Semaphore semaphore, long timeout, TimeUnit unit) {
331    return tryAcquireUninterruptibly(semaphore, 1, timeout, unit);
332  }
333
334  /**
335   * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(permits,
336   * timeout, unit)} uninterruptibly.
337   *
338   * @since 18.0
339   */
340  @GwtIncompatible // concurrency
341  public static boolean tryAcquireUninterruptibly(
342      Semaphore semaphore, int permits, long timeout, TimeUnit unit) {
343    boolean interrupted = false;
344    try {
345      long remainingNanos = unit.toNanos(timeout);
346      long end = System.nanoTime() + remainingNanos;
347
348      while (true) {
349        try {
350          // Semaphore treats negative timeouts just like zero.
351          return semaphore.tryAcquire(permits, remainingNanos, NANOSECONDS);
352        } catch (InterruptedException e) {
353          interrupted = true;
354          remainingNanos = end - System.nanoTime();
355        }
356      }
357    } finally {
358      if (interrupted) {
359        Thread.currentThread().interrupt();
360      }
361    }
362  }
363
364  // TODO(user): Add support for waitUninterruptibly.
365
366  private Uninterruptibles() {}
367}