1bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor/* 21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2006 The Guava Authors 3bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * 4bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Licensed under the Apache License, Version 2.0 (the "License"); 5bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * you may not use this file except in compliance with the License. 6bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * You may obtain a copy of the License at 7bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * 8bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * http://www.apache.org/licenses/LICENSE-2.0 9bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * 10bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Unless required by applicable law or agreed to in writing, software 11bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * distributed under the License is distributed on an "AS IS" BASIS, 12bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * See the License for the specific language governing permissions and 14bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * limitations under the License. 15bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 16bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 17bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorpackage com.google.common.util.concurrent; 18bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 19bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport static com.google.common.base.Preconditions.checkArgument; 20bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport static com.google.common.base.Preconditions.checkNotNull; 211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.annotations.Beta; 231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.ObjectArrays; 24bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport com.google.common.collect.Sets; 25bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 26bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.lang.reflect.InvocationHandler; 27bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.lang.reflect.InvocationTargetException; 28bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.lang.reflect.Method; 29bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.lang.reflect.Proxy; 30bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.util.Set; 31bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.util.concurrent.Callable; 32bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.util.concurrent.ExecutionException; 33bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.util.concurrent.ExecutorService; 34bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.util.concurrent.Executors; 35bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.util.concurrent.Future; 36bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.util.concurrent.TimeUnit; 37bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.util.concurrent.TimeoutException; 38bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 39bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor/** 40bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * A TimeLimiter that runs method calls in the background using an 41bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * {@link ExecutorService}. If the time limit expires for a given method call, 42bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * the thread running the call will be interrupted. 43bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * 44bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * @author Kevin Bourrillion 451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @since 1.0 46bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert@Beta 481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpublic final class SimpleTimeLimiter implements TimeLimiter { 49bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 50bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor private final ExecutorService executor; 51bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 52bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor /** 53bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Constructs a TimeLimiter instance using the given executor service to 54bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * execute proxied method calls. 55bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * <p> 56bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * <b>Warning:</b> using a bounded executor 57bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * may be counterproductive! If the thread pool fills up, any time callers 58bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * spend waiting for a thread may count toward their time limit, and in 59bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * this case the call may even time out before the target method is ever 60bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * invoked. 61bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * 62bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * @param executor the ExecutorService that will execute the method calls on 63bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * the target objects; for example, a {@link 641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Executors#newCachedThreadPool()}. 65bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 66bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor public SimpleTimeLimiter(ExecutorService executor) { 671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this.executor = checkNotNull(executor); 68bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 69bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 70bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor /** 71bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Constructs a TimeLimiter instance using a {@link 721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Executors#newCachedThreadPool()} to execute proxied method calls. 73bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * 74bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * <p><b>Warning:</b> using a bounded executor may be counterproductive! If 75bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * the thread pool fills up, any time callers spend waiting for a thread may 76bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * count toward their time limit, and in this case the call may even time out 77bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * before the target method is ever invoked. 78bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 79bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor public SimpleTimeLimiter() { 80bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor this(Executors.newCachedThreadPool()); 81bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 82bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 84bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor public <T> T newProxy(final T target, Class<T> interfaceType, 85bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor final long timeoutDuration, final TimeUnit timeoutUnit) { 86bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor checkNotNull(target); 87bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor checkNotNull(interfaceType); 88bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor checkNotNull(timeoutUnit); 89bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor checkArgument(timeoutDuration > 0, "bad timeout: " + timeoutDuration); 90bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor checkArgument(interfaceType.isInterface(), 91bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor "interfaceType must be an interface type"); 92bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 93bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor final Set<Method> interruptibleMethods 94bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor = findInterruptibleMethods(interfaceType); 95bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 96bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor InvocationHandler handler = new InvocationHandler() { 971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 98bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor public Object invoke(Object obj, final Method method, final Object[] args) 99bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor throws Throwable { 100bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor Callable<Object> callable = new Callable<Object>() { 1011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 102bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor public Object call() throws Exception { 103bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor try { 104bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor return method.invoke(target, args); 105bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } catch (InvocationTargetException e) { 1061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throwCause(e, false); 107bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor throw new AssertionError("can't get here"); 108bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 109bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 110bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor }; 111bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor return callWithTimeout(callable, timeoutDuration, timeoutUnit, 112bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor interruptibleMethods.contains(method)); 113bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 114bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor }; 115bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor return newProxy(interfaceType, handler); 116bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 117bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 118bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor // TODO: should this actually throw only ExecutionException? 1191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 120bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor public <T> T callWithTimeout(Callable<T> callable, long timeoutDuration, 121bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor TimeUnit timeoutUnit, boolean amInterruptible) throws Exception { 122bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor checkNotNull(callable); 123bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor checkNotNull(timeoutUnit); 1241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkArgument(timeoutDuration > 0, "timeout must be positive: %s", 1251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert timeoutDuration); 126bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor Future<T> future = executor.submit(callable); 127bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor try { 128bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor if (amInterruptible) { 129bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor try { 130bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor return future.get(timeoutDuration, timeoutUnit); 131bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } catch (InterruptedException e) { 132bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor future.cancel(true); 133bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor throw e; 134bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 135bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } else { 1361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return Uninterruptibles.getUninterruptibly(future, 1371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert timeoutDuration, timeoutUnit); 138bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 139bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } catch (ExecutionException e) { 1401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw throwCause(e, true); 141bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } catch (TimeoutException e) { 142bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor future.cancel(true); 143bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor throw new UncheckedTimeoutException(e); 144bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 145bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 146bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 1471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private static Exception throwCause(Exception e, boolean combineStackTraces) 1481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throws Exception { 1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert Throwable cause = e.getCause(); 1501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (cause == null) { 1511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw e; 1521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (combineStackTraces) { 1541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert StackTraceElement[] combined = ObjectArrays.concat(cause.getStackTrace(), 1551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert e.getStackTrace(), StackTraceElement.class); 1561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert cause.setStackTrace(combined); 1571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (cause instanceof Exception) { 1591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw (Exception) cause; 1601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (cause instanceof Error) { 1621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw (Error) cause; 1631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // The cause is a weird kind of Throwable, so throw the outer exception. 1651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw e; 1661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 168bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor private static Set<Method> findInterruptibleMethods(Class<?> interfaceType) { 169bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor Set<Method> set = Sets.newHashSet(); 170bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor for (Method m : interfaceType.getMethods()) { 171bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor if (declaresInterruptedEx(m)) { 172bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor set.add(m); 173bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 174bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 175bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor return set; 176bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 177bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 178bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor private static boolean declaresInterruptedEx(Method method) { 179bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor for (Class<?> exType : method.getExceptionTypes()) { 180bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor // debate: == or isAssignableFrom? 181bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor if (exType == InterruptedException.class) { 182bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor return true; 183bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 184bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 185bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor return false; 186bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 187bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 1881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // TODO: replace with version in common.reflect if and when it's open-sourced 189bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor private static <T> T newProxy( 190bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor Class<T> interfaceType, InvocationHandler handler) { 1911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert Object object = Proxy.newProxyInstance(interfaceType.getClassLoader(), 1921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert new Class<?>[] { interfaceType }, handler); 193bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor return interfaceType.cast(object); 194bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 195bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor} 196