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