package org.corfudb.util; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Function; /** * Created by mwei on 9/15/15. */ public class CFUtils { private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool( 1, new ThreadFactoryBuilder() .setDaemon(true) .setNameFormat("failAfter-%d") .build()); @SuppressWarnings("unchecked") public static <T, A extends Throwable, B extends Throwable, C extends Throwable, D extends Throwable> T getUninterruptibly(Future<T> future, Class<A> throwableA, Class<B> throwableB, Class<C> throwableC, Class<D> throwableD) throws A, B, C, D { while (true) { try { return future.get(); } catch (InterruptedException e) { //retry } catch (ExecutionException ee) { if (throwableA.isInstance(ee.getCause())) { throw (A) ee.getCause(); } if (throwableB.isInstance(ee.getCause())) { throw (B) ee.getCause(); } if (throwableC.isInstance(ee.getCause())) { throw (C) ee.getCause(); } if (throwableD.isInstance(ee.getCause())) { throw (D) ee.getCause(); } throw new RuntimeException(ee.getCause()); } } } public static <T, A extends Throwable, B extends Throwable, C extends Throwable> T getUninterruptibly(Future<T> future, Class<A> throwableA, Class<B> throwableB, Class<C> throwableC) throws A, B, C { return getUninterruptibly(future, throwableA, throwableB, throwableC, RuntimeException.class); } public static <T, A extends Throwable, B extends Throwable> T getUninterruptibly(Future<T> future, Class<A> throwableA, Class<B> throwableB) throws A, B { return getUninterruptibly(future, throwableA, throwableB, RuntimeException.class, RuntimeException.class); } public static <T, A extends Throwable> T getUninterruptibly(Future<T> future, Class<A> throwableA) throws A { return getUninterruptibly(future, throwableA, RuntimeException.class, RuntimeException.class, RuntimeException.class); } public static <T> T getUninterruptibly(Future<T> future) { return getUninterruptibly(future, RuntimeException.class, RuntimeException.class, RuntimeException.class, RuntimeException.class); } /** * Generates a completable future which times out. * inspired by NoBlogDefFound: http://www.nurkiewicz.com/2014/12/asynchronous-timeouts-with.html * * @param duration The duration to timeout after. * @param <T> Ignored, since the future will always timeout. * @return A completable future that will time out. */ public static <T> CompletableFuture<T> failAfter(Duration duration) { final CompletableFuture<T> promise = new CompletableFuture<>(); scheduler.schedule(() -> { final TimeoutException ex = new TimeoutException("Timeout after " + duration.toMillis() + " ms"); return promise.completeExceptionally(ex); }, duration.toMillis(), TimeUnit.MILLISECONDS); return promise; } /** * Schedules a runnable after a given time * * @param duration The duration to timeout after. * @return A completable future that will time out. */ public static void runAfter(Duration duration, Runnable toRun) { final CompletableFuture<Void> promise = new CompletableFuture<>(); scheduler.schedule(toRun::run, duration.toMillis(), TimeUnit.MILLISECONDS); } /** * Takes a completable future, and ensures that it completes within a certain duration. If it does * not, it is cancelled and completes exceptionally with TimeoutException. * inspired by NoBlogDefFound: http://www.nurkiewicz.com/2014/12/asynchronous-timeouts-with.html * * @param future The completable future that must be completed within duration. * @param duration The duration the future must be completed in. * @param <T> The return type of the future. * @return A completable future which completes with the original value if completed within * duration, otherwise completes exceptionally with TimeoutException. */ public static <T> CompletableFuture<T> within(CompletableFuture<T> future, Duration duration) { final CompletableFuture<T> timeout = failAfter(duration); return future.applyToEither(timeout, Function.identity()); } }