Coverage Summary for Class: SimpleTimeLimiter (com.google.common.util.concurrent)

Class Method, % Line, %
SimpleTimeLimiter 0% (0/16) 0% (0/107)
SimpleTimeLimiter$1 0% (0/2) 0% (0/4)
SimpleTimeLimiter$1$1 0% (0/2) 0% (0/4)
Total 0% (0/20) 0% (0/115)


1 /* 2  * Copyright (C) 2006 The Guava Authors 3  * 4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5  * in compliance with the License. You may obtain a copy of the License at 6  * 7  * http://www.apache.org/licenses/LICENSE-2.0 8  * 9  * Unless required by applicable law or agreed to in writing, software distributed under the License 10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11  * or implied. See the License for the specific language governing permissions and limitations under 12  * the License. 13  */ 14  15 package com.google.common.util.concurrent; 16  17 import static com.google.common.base.Preconditions.checkArgument; 18 import static com.google.common.base.Preconditions.checkNotNull; 19  20 import com.google.common.annotations.Beta; 21 import com.google.common.annotations.GwtIncompatible; 22 import com.google.common.collect.ObjectArrays; 23 import com.google.common.collect.Sets; 24 import com.google.errorprone.annotations.CanIgnoreReturnValue; 25 import java.lang.reflect.InvocationHandler; 26 import java.lang.reflect.InvocationTargetException; 27 import java.lang.reflect.Method; 28 import java.lang.reflect.Proxy; 29 import java.util.Set; 30 import java.util.concurrent.Callable; 31 import java.util.concurrent.ExecutionException; 32 import java.util.concurrent.ExecutorService; 33 import java.util.concurrent.Executors; 34 import java.util.concurrent.Future; 35 import java.util.concurrent.TimeUnit; 36 import java.util.concurrent.TimeoutException; 37 import javax.annotation.CheckForNull; 38 import org.checkerframework.checker.nullness.qual.Nullable; 39  40 /** 41  * A TimeLimiter that runs method calls in the background using an {@link ExecutorService}. If the 42  * time limit expires for a given method call, the thread running the call will be interrupted. 43  * 44  * @author Kevin Bourrillion 45  * @author Jens Nyman 46  * @since 1.0 47  */ 48 @Beta 49 @GwtIncompatible 50 @ElementTypesAreNonnullByDefault 51 public final class SimpleTimeLimiter implements TimeLimiter { 52  53  private final ExecutorService executor; 54  55  private SimpleTimeLimiter(ExecutorService executor) { 56  this.executor = checkNotNull(executor); 57  } 58  59  /** 60  * Creates a TimeLimiter instance using the given executor service to execute method calls. 61  * 62  * <p><b>Warning:</b> using a bounded executor may be counterproductive! If the thread pool fills 63  * up, any time callers spend waiting for a thread may count toward their time limit, and in this 64  * case the call may even time out before the target method is ever invoked. 65  * 66  * @param executor the ExecutorService that will execute the method calls on the target objects; 67  * for example, a {@link Executors#newCachedThreadPool()}. 68  * @since 22.0 69  */ 70  public static SimpleTimeLimiter create(ExecutorService executor) { 71  return new SimpleTimeLimiter(executor); 72  } 73  74  @Override 75  public <T> T newProxy( 76  final T target, 77  Class<T> interfaceType, 78  final long timeoutDuration, 79  final TimeUnit timeoutUnit) { 80  checkNotNull(target); 81  checkNotNull(interfaceType); 82  checkNotNull(timeoutUnit); 83  checkPositiveTimeout(timeoutDuration); 84  checkArgument(interfaceType.isInterface(), "interfaceType must be an interface type"); 85  86  final Set<Method> interruptibleMethods = findInterruptibleMethods(interfaceType); 87  88  InvocationHandler handler = 89  new InvocationHandler() { 90  @Override 91  @CheckForNull 92  public Object invoke( 93  Object obj, final Method method, @CheckForNull final @Nullable Object[] args) 94  throws Throwable { 95  Callable<@Nullable Object> callable = 96  new Callable<@Nullable Object>() { 97  @Override 98  @CheckForNull 99  public Object call() throws Exception { 100  try { 101  return method.invoke(target, args); 102  } catch (InvocationTargetException e) { 103  throw throwCause(e, false /* combineStackTraces */); 104  } 105  } 106  }; 107  return callWithTimeout( 108  callable, timeoutDuration, timeoutUnit, interruptibleMethods.contains(method)); 109  } 110  }; 111  return newProxy(interfaceType, handler); 112  } 113  114  // TODO: replace with version in common.reflect if and when it's open-sourced 115  private static <T> T newProxy(Class<T> interfaceType, InvocationHandler handler) { 116  Object object = 117  Proxy.newProxyInstance( 118  interfaceType.getClassLoader(), new Class<?>[] {interfaceType}, handler); 119  return interfaceType.cast(object); 120  } 121  122  private <T extends @Nullable Object> T callWithTimeout( 123  Callable<T> callable, long timeoutDuration, TimeUnit timeoutUnit, boolean amInterruptible) 124  throws Exception { 125  checkNotNull(callable); 126  checkNotNull(timeoutUnit); 127  checkPositiveTimeout(timeoutDuration); 128  129  Future<T> future = executor.submit(callable); 130  131  try { 132  if (amInterruptible) { 133  try { 134  return future.get(timeoutDuration, timeoutUnit); 135  } catch (InterruptedException e) { 136  future.cancel(true); 137  throw e; 138  } 139  } else { 140  return Uninterruptibles.getUninterruptibly(future, timeoutDuration, timeoutUnit); 141  } 142  } catch (ExecutionException e) { 143  throw throwCause(e, true /* combineStackTraces */); 144  } catch (TimeoutException e) { 145  future.cancel(true); 146  throw new UncheckedTimeoutException(e); 147  } 148  } 149  150  @CanIgnoreReturnValue 151  @Override 152  public <T extends @Nullable Object> T callWithTimeout( 153  Callable<T> callable, long timeoutDuration, TimeUnit timeoutUnit) 154  throws TimeoutException, InterruptedException, ExecutionException { 155  checkNotNull(callable); 156  checkNotNull(timeoutUnit); 157  checkPositiveTimeout(timeoutDuration); 158  159  Future<T> future = executor.submit(callable); 160  161  try { 162  return future.get(timeoutDuration, timeoutUnit); 163  } catch (InterruptedException | TimeoutException e) { 164  future.cancel(true /* mayInterruptIfRunning */); 165  throw e; 166  } catch (ExecutionException e) { 167  wrapAndThrowExecutionExceptionOrError(e.getCause()); 168  throw new AssertionError(); 169  } 170  } 171  172  @CanIgnoreReturnValue 173  @Override 174  public <T extends @Nullable Object> T callUninterruptiblyWithTimeout( 175  Callable<T> callable, long timeoutDuration, TimeUnit timeoutUnit) 176  throws TimeoutException, ExecutionException { 177  checkNotNull(callable); 178  checkNotNull(timeoutUnit); 179  checkPositiveTimeout(timeoutDuration); 180  181  Future<T> future = executor.submit(callable); 182  183  try { 184  return Uninterruptibles.getUninterruptibly(future, timeoutDuration, timeoutUnit); 185  } catch (TimeoutException e) { 186  future.cancel(true /* mayInterruptIfRunning */); 187  throw e; 188  } catch (ExecutionException e) { 189  wrapAndThrowExecutionExceptionOrError(e.getCause()); 190  throw new AssertionError(); 191  } 192  } 193  194  @Override 195  public void runWithTimeout(Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit) 196  throws TimeoutException, InterruptedException { 197  checkNotNull(runnable); 198  checkNotNull(timeoutUnit); 199  checkPositiveTimeout(timeoutDuration); 200  201  Future<?> future = executor.submit(runnable); 202  203  try { 204  future.get(timeoutDuration, timeoutUnit); 205  } catch (InterruptedException | TimeoutException e) { 206  future.cancel(true /* mayInterruptIfRunning */); 207  throw e; 208  } catch (ExecutionException e) { 209  wrapAndThrowRuntimeExecutionExceptionOrError(e.getCause()); 210  throw new AssertionError(); 211  } 212  } 213  214  @Override 215  public void runUninterruptiblyWithTimeout( 216  Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit) throws TimeoutException { 217  checkNotNull(runnable); 218  checkNotNull(timeoutUnit); 219  checkPositiveTimeout(timeoutDuration); 220  221  Future<?> future = executor.submit(runnable); 222  223  try { 224  Uninterruptibles.getUninterruptibly(future, timeoutDuration, timeoutUnit); 225  } catch (TimeoutException e) { 226  future.cancel(true /* mayInterruptIfRunning */); 227  throw e; 228  } catch (ExecutionException e) { 229  wrapAndThrowRuntimeExecutionExceptionOrError(e.getCause()); 230  throw new AssertionError(); 231  } 232  } 233  234  private static Exception throwCause(Exception e, boolean combineStackTraces) throws Exception { 235  Throwable cause = e.getCause(); 236  if (cause == null) { 237  throw e; 238  } 239  if (combineStackTraces) { 240  StackTraceElement[] combined = 241  ObjectArrays.concat(cause.getStackTrace(), e.getStackTrace(), StackTraceElement.class); 242  cause.setStackTrace(combined); 243  } 244  if (cause instanceof Exception) { 245  throw (Exception) cause; 246  } 247  if (cause instanceof Error) { 248  throw (Error) cause; 249  } 250  // The cause is a weird kind of Throwable, so throw the outer exception. 251  throw e; 252  } 253  254  private static Set<Method> findInterruptibleMethods(Class<?> interfaceType) { 255  Set<Method> set = Sets.newHashSet(); 256  for (Method m : interfaceType.getMethods()) { 257  if (declaresInterruptedEx(m)) { 258  set.add(m); 259  } 260  } 261  return set; 262  } 263  264  private static boolean declaresInterruptedEx(Method method) { 265  for (Class<?> exType : method.getExceptionTypes()) { 266  // debate: == or isAssignableFrom? 267  if (exType == InterruptedException.class) { 268  return true; 269  } 270  } 271  return false; 272  } 273  274  private void wrapAndThrowExecutionExceptionOrError(Throwable cause) throws ExecutionException { 275  if (cause instanceof Error) { 276  throw new ExecutionError((Error) cause); 277  } else if (cause instanceof RuntimeException) { 278  throw new UncheckedExecutionException(cause); 279  } else { 280  throw new ExecutionException(cause); 281  } 282  } 283  284  private void wrapAndThrowRuntimeExecutionExceptionOrError(Throwable cause) { 285  if (cause instanceof Error) { 286  throw new ExecutionError((Error) cause); 287  } else { 288  throw new UncheckedExecutionException(cause); 289  } 290  } 291  292  private static void checkPositiveTimeout(long timeoutDuration) { 293  checkArgument(timeoutDuration > 0, "timeout must be positive: %s", timeoutDuration); 294  } 295 }