1 // Copyright 2016 Google Inc. All Rights Reserved.
2 package com.android.contacts.util.concurrent;
3 
4 import android.os.Handler;
5 
6 import com.google.common.util.concurrent.AsyncFunction;
7 import com.google.common.util.concurrent.Futures;
8 import com.google.common.util.concurrent.ListenableFuture;
9 import com.google.common.util.concurrent.MoreExecutors;
10 
11 import java.util.concurrent.CancellationException;
12 import java.util.concurrent.ScheduledExecutorService;
13 import java.util.concurrent.TimeUnit;
14 import java.util.concurrent.TimeoutException;
15 import java.util.concurrent.atomic.AtomicBoolean;
16 
17 /**
18  * Has utility methods for operating on ListenableFutures
19  */
20 public class FuturesUtil {
21 
22     /**
23      * See
24      * {@link FuturesUtil#withTimeout(ListenableFuture, long, TimeUnit, ScheduledExecutorService)}
25      */
withTimeout(final ListenableFuture<V> future, long time, TimeUnit unit, Handler handler)26     public static <V> ListenableFuture<V> withTimeout(final ListenableFuture<V> future, long time,
27             TimeUnit unit, Handler handler) {
28         return withTimeout(future, time, unit, ContactsExecutors.newHandlerExecutor(handler));
29     }
30 
31     /**
32      * Returns a future that completes with the result from the input future unless the specified
33      * time elapses before it finishes in which case the result will contain a TimeoutException and
34      * the input future will be canceled.
35      *
36      * <p>Guava has Futures.withTimeout but it isn't available until v19.0 and we depend on v14.0
37      * right now. Replace usages of this method if we upgrade our dependency.</p>
38      */
withTimeout(final ListenableFuture<V> future, long time, TimeUnit unit, ScheduledExecutorService executor)39     public static <V> ListenableFuture<V> withTimeout(final ListenableFuture<V> future, long time,
40             TimeUnit unit, ScheduledExecutorService executor) {
41         final AtomicBoolean didTimeout = new AtomicBoolean(false);
42         executor.schedule(new Runnable() {
43             @Override
44             public void run() {
45                 didTimeout.set(!future.isDone() && !future.isCancelled());
46                 future.cancel(true);
47             }
48         }, time, unit);
49 
50         return Futures.catchingAsync(future, Throwable.class, new AsyncFunction<Throwable, V>() {
51             @Override
52             public ListenableFuture<V> apply(Throwable t) throws Exception {
53                 if ((t instanceof CancellationException) && didTimeout.get()) {
54                     return Futures.immediateFailedFuture(new TimeoutException("Timeout expired"));
55                 }
56                 return Futures.immediateFailedFuture(t);
57             }
58         }, MoreExecutors.directExecutor());
59     }
60 }
61