1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.util.concurrent;
18 
19 import static java.util.concurrent.TimeUnit.NANOSECONDS;
20 
21 import com.google.common.annotations.Beta;
22 import com.google.common.base.Preconditions;
23 
24 import java.util.concurrent.BlockingQueue;
25 import java.util.concurrent.CancellationException;
26 import java.util.concurrent.CountDownLatch;
27 import java.util.concurrent.ExecutionException;
28 import java.util.concurrent.Future;
29 import java.util.concurrent.Semaphore;
30 import java.util.concurrent.TimeUnit;
31 import java.util.concurrent.TimeoutException;
32 
33 /**
34  * Utilities for treating interruptible operations as uninterruptible.
35  * In all cases, if a thread is interrupted during such a call, the call
36  * continues to block until the result is available or the timeout elapses,
37  * and only then re-interrupts the thread.
38  *
39  * @author Anthony Zana
40  * @since 10.0
41  */
42 @Beta
43 public final class Uninterruptibles {
44 
45   // Implementation Note: As of 3-7-11, the logic for each blocking/timeout
46   // methods is identical, save for method being invoked.
47 
48   /**
49    * Invokes {@code latch.}{@link CountDownLatch#await() await()}
50    * uninterruptibly.
51    */
awaitUninterruptibly(CountDownLatch latch)52   public static void awaitUninterruptibly(CountDownLatch latch) {
53     boolean interrupted = false;
54     try {
55       while (true) {
56         try {
57           latch.await();
58           return;
59         } catch (InterruptedException e) {
60           interrupted = true;
61         }
62       }
63     } finally {
64       if (interrupted) {
65         Thread.currentThread().interrupt();
66       }
67     }
68   }
69 
70   /**
71    * Invokes
72    * {@code latch.}{@link CountDownLatch#await(long, TimeUnit)
73    * await(timeout, unit)} uninterruptibly.
74    */
awaitUninterruptibly(CountDownLatch latch, long timeout, TimeUnit unit)75   public static boolean awaitUninterruptibly(CountDownLatch latch,
76       long timeout, TimeUnit unit) {
77     boolean interrupted = false;
78     try {
79       long remainingNanos = unit.toNanos(timeout);
80       long end = System.nanoTime() + remainingNanos;
81 
82       while (true) {
83         try {
84           // CountDownLatch treats negative timeouts just like zero.
85           return latch.await(remainingNanos, NANOSECONDS);
86         } catch (InterruptedException e) {
87           interrupted = true;
88           remainingNanos = end - System.nanoTime();
89         }
90       }
91     } finally {
92       if (interrupted) {
93         Thread.currentThread().interrupt();
94       }
95     }
96   }
97 
98   /**
99    * Invokes {@code toJoin.}{@link Thread#join() join()} uninterruptibly.
100    */
joinUninterruptibly(Thread toJoin)101   public static void joinUninterruptibly(Thread toJoin) {
102     boolean interrupted = false;
103     try {
104       while (true) {
105         try {
106           toJoin.join();
107           return;
108         } catch (InterruptedException e) {
109           interrupted = true;
110         }
111       }
112     } finally {
113       if (interrupted) {
114         Thread.currentThread().interrupt();
115       }
116     }
117   }
118 
119   /**
120    * Invokes {@code future.}{@link Future#get() get()} uninterruptibly.
121    * To get uninterruptibility and remove checked exceptions, see
122    * {@link Futures#getUnchecked}.
123    *
124    * <p>If instead, you wish to treat {@link InterruptedException} uniformly
125    * with other exceptions, see {@link Futures#get(Future, Class) Futures.get}
126    * or {@link Futures#makeChecked}.
127    *
128    * @throws ExecutionException if the computation threw an exception
129    * @throws CancellationException if the computation was cancelled
130    */
getUninterruptibly(Future<V> future)131   public static <V> V getUninterruptibly(Future<V> future)
132       throws ExecutionException {
133     boolean interrupted = false;
134     try {
135       while (true) {
136         try {
137           return future.get();
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
151    * {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)}
152    * uninterruptibly.
153    *
154    * <p>If instead, you wish to treat {@link InterruptedException} uniformly
155    * with other exceptions, see {@link Futures#get(Future, Class) Futures.get}
156    * or {@link Futures#makeChecked}.
157    *
158    * @throws ExecutionException if the computation threw an exception
159    * @throws CancellationException if the computation was cancelled
160    * @throws TimeoutException if the wait timed out
161    */
getUninterruptibly( Future<V> future, long timeout, TimeUnit unit)162   public static <V> V getUninterruptibly(
163       Future<V> future, long timeout,  TimeUnit unit)
164           throws ExecutionException, TimeoutException {
165     boolean interrupted = false;
166     try {
167       long remainingNanos = unit.toNanos(timeout);
168       long end = System.nanoTime() + remainingNanos;
169 
170       while (true) {
171         try {
172           // Future treats negative timeouts just like zero.
173           return future.get(remainingNanos, NANOSECONDS);
174         } catch (InterruptedException e) {
175           interrupted = true;
176           remainingNanos = end - System.nanoTime();
177         }
178       }
179     } finally {
180       if (interrupted) {
181         Thread.currentThread().interrupt();
182       }
183     }
184   }
185 
186   /**
187    * Invokes
188    * {@code unit.}{@link TimeUnit#timedJoin(Thread, long)
189    * timedJoin(toJoin, timeout)} uninterruptibly.
190    */
joinUninterruptibly(Thread toJoin, long timeout, TimeUnit unit)191   public static void joinUninterruptibly(Thread toJoin,
192       long timeout, TimeUnit unit) {
193     Preconditions.checkNotNull(toJoin);
194     boolean interrupted = false;
195     try {
196       long remainingNanos = unit.toNanos(timeout);
197       long end = System.nanoTime() + remainingNanos;
198       while (true) {
199         try {
200           // TimeUnit.timedJoin() treats negative timeouts just like zero.
201           NANOSECONDS.timedJoin(toJoin, remainingNanos);
202           return;
203         } catch (InterruptedException e) {
204           interrupted = true;
205           remainingNanos = end - System.nanoTime();
206         }
207       }
208     } finally {
209       if (interrupted) {
210         Thread.currentThread().interrupt();
211       }
212     }
213   }
214 
215   /**
216    * Invokes {@code queue.}{@link BlockingQueue#take() take()} uninterruptibly.
217    */
takeUninterruptibly(BlockingQueue<E> queue)218   public static <E> E takeUninterruptibly(BlockingQueue<E> queue) {
219     boolean interrupted = false;
220     try {
221       while (true) {
222         try {
223           return queue.take();
224         } catch (InterruptedException e) {
225           interrupted = true;
226         }
227       }
228     } finally {
229       if (interrupted) {
230         Thread.currentThread().interrupt();
231       }
232     }
233   }
234 
235   /**
236    * Invokes {@code queue.}{@link BlockingQueue#put(Object) put(element)}
237    * uninterruptibly.
238    *
239    * @throws ClassCastException if the class of the specified element prevents
240    *     it from being added to the given queue
241    * @throws IllegalArgumentException if some property of the specified element
242    *     prevents it from being added to the given queue
243    */
putUninterruptibly(BlockingQueue<E> queue, E element)244   public static <E> void putUninterruptibly(BlockingQueue<E> queue, E element) {
245     boolean interrupted = false;
246     try {
247       while (true) {
248         try {
249           queue.put(element);
250           return;
251         } catch (InterruptedException e) {
252           interrupted = true;
253         }
254       }
255     } finally {
256       if (interrupted) {
257         Thread.currentThread().interrupt();
258       }
259     }
260   }
261 
262   // TODO(user): Support Sleeper somehow (wrapper or interface method)?
263   /**
264    * Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)}
265    * uninterruptibly.
266    */
sleepUninterruptibly(long sleepFor, TimeUnit unit)267   public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) {
268     boolean interrupted = false;
269     try {
270       long remainingNanos = unit.toNanos(sleepFor);
271       long end = System.nanoTime() + remainingNanos;
272       while (true) {
273         try {
274           // TimeUnit.sleep() treats negative timeouts just like zero.
275           NANOSECONDS.sleep(remainingNanos);
276           return;
277         } catch (InterruptedException e) {
278           interrupted = true;
279           remainingNanos = end - System.nanoTime();
280         }
281       }
282     } finally {
283       if (interrupted) {
284         Thread.currentThread().interrupt();
285       }
286     }
287   }
288 
289   /**
290    * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit)
291    * tryAcquire(1, timeout, unit)} uninterruptibly.
292    *
293    * @since 18.0
294    */
tryAcquireUninterruptibly( Semaphore semaphore, long timeout, TimeUnit unit)295   public static boolean tryAcquireUninterruptibly(
296       Semaphore semaphore, long timeout, TimeUnit unit) {
297     return tryAcquireUninterruptibly(semaphore, 1, timeout, unit);
298   }
299 
300   /**
301    * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit)
302    * tryAcquire(permits, timeout, unit)} uninterruptibly.
303    *
304    * @since 18.0
305    */
tryAcquireUninterruptibly( Semaphore semaphore, int permits, long timeout, TimeUnit unit)306   public static boolean tryAcquireUninterruptibly(
307       Semaphore semaphore, int permits, long timeout, TimeUnit unit) {
308     boolean interrupted = false;
309     try {
310       long remainingNanos = unit.toNanos(timeout);
311       long end = System.nanoTime() + remainingNanos;
312 
313       while (true) {
314         try {
315           // Semaphore treats negative timeouts just like zero.
316           return semaphore.tryAcquire(permits, remainingNanos, NANOSECONDS);
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   // TODO(user): Add support for waitUninterruptibly.
330 
Uninterruptibles()331   private Uninterruptibles() {}
332 }
333