concurrent/src/main/java/io/airlift/concurrent/MoreFutures.java
public static <T> ListenableFuture<T> addTimeout(ListenableFuture<T> future, Callable<T> onTimeout, Duration timeout, ScheduledExecutorService executorService)
    {
        AsyncFunction<TimeoutException, T> timeoutHandler = timeoutException -> {
            try {
                return immediateFuture(onTimeout.call());
            }
            catch (Throwable throwable) {
                return immediateFailedFuture(throwable);
            }
        };
        return FluentFuture.from(future)
                .withTimeout(timeout.toMillis(), MILLISECONDS, executorService)
                .catchingAsync(TimeoutException.class, timeoutHandler, directExecutor());
    }
public static <T> ListenableFuture<T> addTimeout(ListenableFuture<T> future, Callable<T> onTimeout, Duration timeout, ScheduledExecutorService executorService)
    {
        AsyncFunction<TimeoutException, T> timeoutHandler = timeoutException -> {
            try {
                return immediateFuture(onTimeout.call());
            }
            catch (Throwable throwable) {
                return immediateFailedFuture(throwable);
            }
        };
        return FluentFuture.from(future)
                .withTimeout(timeout.toMillis(), MILLISECONDS, executorService)
                .catchingAsync(TimeoutException.class, timeoutHandler, directExecutor());
    }
Returns a new future that is completed when the supplied future completes or
when the timeout expires.  If the timeout occurs or the returned CompletableFuture
is canceled, the supplied future will be canceled.