001    /*
002     * Copyright (C) 2011 The Guava Authors
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package com.google.common.util.concurrent;
018    
019    import static java.util.concurrent.TimeUnit.NANOSECONDS;
020    
021    import com.google.common.annotations.Beta;
022    import com.google.common.base.Preconditions;
023    
024    import java.util.concurrent.CountDownLatch;
025    import java.util.concurrent.ExecutionException;
026    import java.util.concurrent.Future;
027    import java.util.concurrent.TimeUnit;
028    import java.util.concurrent.TimeoutException;
029    
030    /**
031     * Utilities for treating interruptible operations as uninterruptible.
032     * In all cases, if a thread is interrupted during such a call, the call
033     * continues to block until the result is available or the timeout elapses,
034     * and only then re-interrupts the thread.
035     *
036     * @author Anthony Zana
037     * @since 10.0
038     */
039    @Beta
040    public final class Uninterruptibles {
041    
042      // Implementation Note: As of 3-7-11, the logic for each blocking/timeout
043      // methods is identical, save for method being invoked.
044    
045      /**
046       * Invokes {@code latch.}{@link CountDownLatch#await() await()}
047       * uninterruptibly.
048       */
049      public static void awaitUninterruptibly(CountDownLatch latch) {
050        boolean interrupted = false;
051        try {
052          while (true) {
053            try {
054              latch.await();
055              return;
056            } catch (InterruptedException e) {
057              interrupted = true;
058            }
059          }
060        } finally {
061          if (interrupted) {
062            Thread.currentThread().interrupt();
063          }
064        }
065      }
066    
067      /**
068       * Invokes
069       * {@code latch.}{@link CountDownLatch#await(long, TimeUnit)
070       * await(timeout, unit)} uninterruptibly.
071       */
072      public static boolean awaitUninterruptibly(CountDownLatch latch,
073          long timeout, TimeUnit unit) {
074        boolean interrupted = false;
075        try {
076          long remainingNanos = unit.toNanos(timeout);
077          long end = System.nanoTime() + remainingNanos;
078    
079          while (true) {
080            try {
081              // CountDownLatch treats negative timeouts just like zero.
082              return latch.await(remainingNanos, NANOSECONDS);
083            } catch (InterruptedException e) {
084              interrupted = true;
085              remainingNanos = end - System.nanoTime();
086            }
087          }
088        } finally {
089          if (interrupted) {
090            Thread.currentThread().interrupt();
091          }
092        }
093      }
094    
095      /**
096       * Invokes {@code toJoin.}{@link Thread#join() join()} uninterruptibly.
097       */
098      public static void joinUninterruptibly(Thread toJoin) {
099        boolean interrupted = false;
100        try {
101          while (true) {
102            try {
103              toJoin.join();
104              return;
105            } catch (InterruptedException e) {
106              interrupted = true;
107            }
108          }
109        } finally {
110          if (interrupted) {
111            Thread.currentThread().interrupt();
112          }
113        }
114      }
115    
116      /**
117       * Invokes {@code future.}{@link Future#get() get()} uninterruptibly.
118       * To get uninterruptibility and remove checked exceptions, see
119       * {@link Futures#getUnchecked}.
120       *
121       * <p>If instead, you wish to treat {@link InterruptedException} uniformly
122       * with other exceptions, see {@link Futures#get(Future, Class) Futures.get}
123       * or {@link Futures#makeChecked}.
124       */
125      public static <V> V getUninterruptibly(Future<V> future)
126          throws ExecutionException {
127        boolean interrupted = false;
128        try {
129          while (true) {
130            try {
131              return future.get();
132            } catch (InterruptedException ignored) {
133              interrupted = true;
134            }
135          }
136        } finally {
137          if (interrupted) {
138            Thread.currentThread().interrupt();
139          }
140        }
141      }
142    
143      /**
144       * Invokes
145       * {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)}
146       * uninterruptibly.
147       *
148       * <p>If instead, you wish to treat {@link InterruptedException} uniformly
149       * with other exceptions, see {@link Futures#get(Future, Class) Futures.get}
150       * or {@link Futures#makeChecked}.
151       */
152      public static <V> V getUninterruptibly(
153          Future<V> future, long timeout,  TimeUnit unit)
154              throws ExecutionException, TimeoutException {
155        boolean interrupted = false;
156        try {
157          long remainingNanos = unit.toNanos(timeout);
158          long end = System.nanoTime() + remainingNanos;
159    
160          while (true) {
161            try {
162              // Future treats negative timeouts just like zero.
163              return future.get(remainingNanos, NANOSECONDS);
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
178       * {@code unit.}{@link TimeUnit#timedJoin(Thread, long)
179       * timedJoin(toJoin, timeout)} uninterruptibly.
180       */
181      public static void joinUninterruptibly(Thread toJoin,
182          long timeout, TimeUnit unit) {
183        Preconditions.checkNotNull(toJoin);
184        boolean interrupted = false;
185        try {
186          long remainingNanos = unit.toNanos(timeout);
187          long end = System.nanoTime() + remainingNanos;
188          while (true) {
189            try {
190              // TimeUnit.timedJoin() treats negative timeouts just like zero.
191              NANOSECONDS.timedJoin(toJoin, remainingNanos);
192              return;
193            } catch (InterruptedException e) {
194              interrupted = true;
195              remainingNanos = end - System.nanoTime();
196            }
197          }
198        } finally {
199          if (interrupted) {
200            Thread.currentThread().interrupt();
201          }
202        }
203      }
204      // TODO(user): Support Sleeper somehow (wrapper or interface method)?
205      /**
206       * Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)}
207       * uninterruptibly.
208       */
209      public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) {
210        boolean interrupted = false;
211        try {
212          long remainingNanos = unit.toNanos(sleepFor);
213          long end = System.nanoTime() + remainingNanos;
214          while (true) {
215            try {
216              // TimeUnit.sleep() treats negative timeouts just like zero.
217              NANOSECONDS.sleep(remainingNanos);
218              return;
219            } catch (InterruptedException e) {
220              interrupted = true;
221              remainingNanos = end - System.nanoTime();
222            }
223          }
224        } finally {
225          if (interrupted) {
226            Thread.currentThread().interrupt();
227          }
228        }
229      }
230    
231      // TODO(user): Add support for waitUninterruptibly.
232    
233      private Uninterruptibles() {}
234    }