001/*
002 * Copyright (C) 2006 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.util.concurrent.Internal.toNanosSaturated;
018
019import com.google.common.annotations.Beta;
020import com.google.common.annotations.GwtIncompatible;
021import com.google.errorprone.annotations.CanIgnoreReturnValue;
022import com.google.errorprone.annotations.DoNotMock;
023import java.time.Duration;
024import java.util.concurrent.Callable;
025import java.util.concurrent.ExecutionException;
026import java.util.concurrent.TimeUnit;
027import java.util.concurrent.TimeoutException;
028
029/**
030 * Imposes a time limit on method calls.
031 *
032 * @author Kevin Bourrillion
033 * @author Jens Nyman
034 * @since 1.0
035 */
036@Beta
037@DoNotMock("Use FakeTimeLimiter")
038@GwtIncompatible
039public interface TimeLimiter {
040
041  /**
042   * Returns an instance of {@code interfaceType} that delegates all method calls to the {@code
043   * target} object, enforcing the specified time limit on each call. This time-limited delegation
044   * is also performed for calls to {@link Object#equals}, {@link Object#hashCode}, and {@link
045   * Object#toString}.
046   *
047   * <p>If the target method call finishes before the limit is reached, the return value or
048   * exception is propagated to the caller exactly as-is. If, on the other hand, the time limit is
049   * reached, the proxy will attempt to abort the call to the target, and will throw an {@link
050   * UncheckedTimeoutException} to the caller.
051   *
052   * <p>It is important to note that the primary purpose of the proxy object is to return control to
053   * the caller when the timeout elapses; aborting the target method call is of secondary concern.
054   * The particular nature and strength of the guarantees made by the proxy is
055   * implementation-dependent. However, it is important that each of the methods on the target
056   * object behaves appropriately when its thread is interrupted.
057   *
058   * <p>For example, to return the value of {@code target.someMethod()}, but substitute {@code
059   * DEFAULT_VALUE} if this method call takes over 50 ms, you can use this code:
060   *
061   * <pre>
062   *   TimeLimiter limiter = . . .;
063   *   TargetType proxy = limiter.newProxy(
064   *       target, TargetType.class, 50, TimeUnit.MILLISECONDS);
065   *   try {
066   *     return proxy.someMethod();
067   *   } catch (UncheckedTimeoutException e) {
068   *     return DEFAULT_VALUE;
069   *   }
070   * </pre>
071   *
072   * @param target the object to proxy
073   * @param interfaceType the interface you wish the returned proxy to implement
074   * @param timeoutDuration with timeoutUnit, the maximum length of time that callers are willing to
075   *     wait on each method call to the proxy
076   * @param timeoutUnit with timeoutDuration, the maximum length of time that callers are willing to
077   *     wait on each method call to the proxy
078   * @return a time-limiting proxy
079   * @throws IllegalArgumentException if {@code interfaceType} is a regular class, enum, or
080   *     annotation type, rather than an interface
081   */
082  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
083  <T> T newProxy(T target, Class<T> interfaceType, long timeoutDuration, TimeUnit timeoutUnit);
084
085  /**
086   * Returns an instance of {@code interfaceType} that delegates all method calls to the {@code
087   * target} object, enforcing the specified time limit on each call. This time-limited delegation
088   * is also performed for calls to {@link Object#equals}, {@link Object#hashCode}, and {@link
089   * Object#toString}.
090   *
091   * <p>If the target method call finishes before the limit is reached, the return value or
092   * exception is propagated to the caller exactly as-is. If, on the other hand, the time limit is
093   * reached, the proxy will attempt to abort the call to the target, and will throw an {@link
094   * UncheckedTimeoutException} to the caller.
095   *
096   * <p>It is important to note that the primary purpose of the proxy object is to return control to
097   * the caller when the timeout elapses; aborting the target method call is of secondary concern.
098   * The particular nature and strength of the guarantees made by the proxy is
099   * implementation-dependent. However, it is important that each of the methods on the target
100   * object behaves appropriately when its thread is interrupted.
101   *
102   * <p>For example, to return the value of {@code target.someMethod()}, but substitute {@code
103   * DEFAULT_VALUE} if this method call takes over 50 ms, you can use this code:
104   *
105   * <pre>
106   *   TimeLimiter limiter = . . .;
107   *   TargetType proxy = limiter.newProxy(target, TargetType.class, Duration.ofMillis(50));
108   *   try {
109   *     return proxy.someMethod();
110   *   } catch (UncheckedTimeoutException e) {
111   *     return DEFAULT_VALUE;
112   *   }
113   * </pre>
114   *
115   * @param target the object to proxy
116   * @param interfaceType the interface you wish the returned proxy to implement
117   * @param timeout the maximum length of time that callers are willing to wait on each method call
118   *     to the proxy
119   * @return a time-limiting proxy
120   * @throws IllegalArgumentException if {@code interfaceType} is a regular class, enum, or
121   *     annotation type, rather than an interface
122   * @since 28.0
123   */
124  default <T> T newProxy(T target, Class<T> interfaceType, Duration timeout) {
125    return newProxy(target, interfaceType, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
126  }
127
128  /**
129   * Invokes a specified Callable, timing out after the specified time limit. If the target method
130   * call finishes before the limit is reached, the return value or a wrapped exception is
131   * propagated. If, on the other hand, the time limit is reached, we attempt to abort the call to
132   * the target, and throw a {@link TimeoutException} to the caller.
133   *
134   * @param callable the Callable to execute
135   * @param timeoutDuration with timeoutUnit, the maximum length of time to wait
136   * @param timeoutUnit with timeoutDuration, the maximum length of time to wait
137   * @return the result returned by the Callable
138   * @throws TimeoutException if the time limit is reached
139   * @throws InterruptedException if the current thread was interrupted during execution
140   * @throws ExecutionException if {@code callable} throws a checked exception
141   * @throws UncheckedExecutionException if {@code callable} throws a {@code RuntimeException}
142   * @throws ExecutionError if {@code callable} throws an {@code Error}
143   * @since 22.0
144   */
145  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
146  @CanIgnoreReturnValue
147  <T> T callWithTimeout(Callable<T> callable, long timeoutDuration, TimeUnit timeoutUnit)
148      throws TimeoutException, InterruptedException, ExecutionException;
149
150  /**
151   * Invokes a specified Callable, timing out after the specified time limit. If the target method
152   * call finishes before the limit is reached, the return value or a wrapped exception is
153   * propagated. If, on the other hand, the time limit is reached, we attempt to abort the call to
154   * the target, and throw a {@link TimeoutException} to the caller.
155   *
156   * @param callable the Callable to execute
157   * @param timeout the maximum length of time to wait
158   * @return the result returned by the Callable
159   * @throws TimeoutException if the time limit is reached
160   * @throws InterruptedException if the current thread was interrupted during execution
161   * @throws ExecutionException if {@code callable} throws a checked exception
162   * @throws UncheckedExecutionException if {@code callable} throws a {@code RuntimeException}
163   * @throws ExecutionError if {@code callable} throws an {@code Error}
164   * @since 28.0
165   */
166  @CanIgnoreReturnValue
167  default <T> T callWithTimeout(Callable<T> callable, Duration timeout)
168      throws TimeoutException, InterruptedException, ExecutionException {
169    return callWithTimeout(callable, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
170  }
171
172  /**
173   * Invokes a specified Callable, timing out after the specified time limit. If the target method
174   * call finishes before the limit is reached, the return value or a wrapped exception is
175   * propagated. If, on the other hand, the time limit is reached, we attempt to abort the call to
176   * the target, and throw a {@link TimeoutException} to the caller.
177   *
178   * <p>The difference with {@link #callWithTimeout(Callable, long, TimeUnit)} is that this method
179   * will ignore interrupts on the current thread.
180   *
181   * @param callable the Callable to execute
182   * @param timeoutDuration with timeoutUnit, the maximum length of time to wait
183   * @param timeoutUnit with timeoutDuration, the maximum length of time to wait
184   * @return the result returned by the Callable
185   * @throws TimeoutException if the time limit is reached
186   * @throws ExecutionException if {@code callable} throws a checked exception
187   * @throws UncheckedExecutionException if {@code callable} throws a {@code RuntimeException}
188   * @throws ExecutionError if {@code callable} throws an {@code Error}
189   * @since 22.0
190   */
191  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
192  @CanIgnoreReturnValue
193  <T> T callUninterruptiblyWithTimeout(
194      Callable<T> callable, long timeoutDuration, TimeUnit timeoutUnit)
195      throws TimeoutException, ExecutionException;
196
197  /**
198   * Invokes a specified Callable, timing out after the specified time limit. If the target method
199   * call finishes before the limit is reached, the return value or a wrapped exception is
200   * propagated. If, on the other hand, the time limit is reached, we attempt to abort the call to
201   * the target, and throw a {@link TimeoutException} to the caller.
202   *
203   * <p>The difference with {@link #callWithTimeout(Callable, Duration)} is that this method will
204   * ignore interrupts on the current thread.
205   *
206   * @param callable the Callable to execute
207   * @param timeout the maximum length of time to wait
208   * @return the result returned by the Callable
209   * @throws TimeoutException if the time limit is reached
210   * @throws ExecutionException if {@code callable} throws a checked exception
211   * @throws UncheckedExecutionException if {@code callable} throws a {@code RuntimeException}
212   * @throws ExecutionError if {@code callable} throws an {@code Error}
213   * @since 28.0
214   */
215  @CanIgnoreReturnValue
216  default <T> T callUninterruptiblyWithTimeout(Callable<T> callable, Duration timeout)
217      throws TimeoutException, ExecutionException {
218    return callUninterruptiblyWithTimeout(
219        callable, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
220  }
221
222  /**
223   * Invokes a specified Runnable, timing out after the specified time limit. If the target method
224   * run finishes before the limit is reached, this method returns or a wrapped exception is
225   * propagated. If, on the other hand, the time limit is reached, we attempt to abort the run, and
226   * throw a {@link TimeoutException} to the caller.
227   *
228   * @param runnable the Runnable to execute
229   * @param timeoutDuration with timeoutUnit, the maximum length of time to wait
230   * @param timeoutUnit with timeoutDuration, the maximum length of time to wait
231   * @throws TimeoutException if the time limit is reached
232   * @throws InterruptedException if the current thread was interrupted during execution
233   * @throws UncheckedExecutionException if {@code runnable} throws a {@code RuntimeException}
234   * @throws ExecutionError if {@code runnable} throws an {@code Error}
235   * @since 22.0
236   */
237  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
238  void runWithTimeout(Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit)
239      throws TimeoutException, InterruptedException;
240
241  /**
242   * Invokes a specified Runnable, timing out after the specified time limit. If the target method
243   * run finishes before the limit is reached, this method returns or a wrapped exception is
244   * propagated. If, on the other hand, the time limit is reached, we attempt to abort the run, and
245   * throw a {@link TimeoutException} to the caller.
246   *
247   * @param runnable the Runnable to execute
248   * @param timeout the maximum length of time to wait
249   * @throws TimeoutException if the time limit is reached
250   * @throws InterruptedException if the current thread was interrupted during execution
251   * @throws UncheckedExecutionException if {@code runnable} throws a {@code RuntimeException}
252   * @throws ExecutionError if {@code runnable} throws an {@code Error}
253   * @since 28.0
254   */
255  default void runWithTimeout(Runnable runnable, Duration timeout)
256      throws TimeoutException, InterruptedException {
257    runWithTimeout(runnable, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
258  }
259
260  /**
261   * Invokes a specified Runnable, timing out after the specified time limit. If the target method
262   * run finishes before the limit is reached, this method returns or a wrapped exception is
263   * propagated. If, on the other hand, the time limit is reached, we attempt to abort the run, and
264   * throw a {@link TimeoutException} to the caller.
265   *
266   * <p>The difference with {@link #runWithTimeout(Runnable, long, TimeUnit)} is that this method
267   * will ignore interrupts on the current thread.
268   *
269   * @param runnable the Runnable to execute
270   * @param timeoutDuration with timeoutUnit, the maximum length of time to wait
271   * @param timeoutUnit with timeoutDuration, the maximum length of time to wait
272   * @throws TimeoutException if the time limit is reached
273   * @throws UncheckedExecutionException if {@code runnable} throws a {@code RuntimeException}
274   * @throws ExecutionError if {@code runnable} throws an {@code Error}
275   * @since 22.0
276   */
277  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
278  void runUninterruptiblyWithTimeout(Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit)
279      throws TimeoutException;
280
281  /**
282   * Invokes a specified Runnable, timing out after the specified time limit. If the target method
283   * run finishes before the limit is reached, this method returns or a wrapped exception is
284   * propagated. If, on the other hand, the time limit is reached, we attempt to abort the run, and
285   * throw a {@link TimeoutException} to the caller.
286   *
287   * <p>The difference with {@link #runWithTimeout(Runnable, Duration)} is that this method will
288   * ignore interrupts on the current thread.
289   *
290   * @param runnable the Runnable to execute
291   * @param timeout the maximum length of time to wait
292   * @throws TimeoutException if the time limit is reached
293   * @throws UncheckedExecutionException if {@code runnable} throws a {@code RuntimeException}
294   * @throws ExecutionError if {@code runnable} throws an {@code Error}
295   * @since 28.0
296   */
297  default void runUninterruptiblyWithTimeout(Runnable runnable, Duration timeout)
298      throws TimeoutException {
299    runUninterruptiblyWithTimeout(runnable, toNanosSaturated(timeout), TimeUnit.NANOSECONDS);
300  }
301}