Home | History | Annotate | Download | only in concurrent
      1 // Copyright 2016 Google Inc. All Rights Reserved.
      2 package com.android.contacts.util.concurrent;
      3 
      4 import android.os.AsyncTask;
      5 import android.os.Handler;
      6 import android.os.Looper;
      7 import android.support.annotation.NonNull;
      8 
      9 import com.google.common.util.concurrent.ForwardingFuture;
     10 import com.google.common.util.concurrent.Futures;
     11 import com.google.common.util.concurrent.ListeningExecutorService;
     12 import com.google.common.util.concurrent.MoreExecutors;
     13 import com.google.common.util.concurrent.SettableFuture;
     14 
     15 import java.util.List;
     16 import java.util.concurrent.AbstractExecutorService;
     17 import java.util.concurrent.Callable;
     18 import java.util.concurrent.Delayed;
     19 import java.util.concurrent.ExecutorService;
     20 import java.util.concurrent.Executors;
     21 import java.util.concurrent.Future;
     22 import java.util.concurrent.LinkedBlockingQueue;
     23 import java.util.concurrent.RunnableScheduledFuture;
     24 import java.util.concurrent.ScheduledExecutorService;
     25 import java.util.concurrent.ScheduledFuture;
     26 import java.util.concurrent.ThreadPoolExecutor;
     27 import java.util.concurrent.TimeUnit;
     28 import java.util.concurrent.atomic.AtomicLong;
     29 
     30 /**
     31  * Provides some common executors for use with {@link Futures}
     32  */
     33 public class ContactsExecutors {
     34 
     35     private ContactsExecutors() {}
     36 
     37     private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
     38     private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
     39 
     40     // AsyncTask.THREAD_POOL_EXECUTOR is a ThreadPoolExecutor so we should end up always using that
     41     // but we have a fallback in case the platform implementation changes in some future release.
     42     private static final ListeningExecutorService DEFAULT_THREAD_POOL_EXECUTOR =
     43             (AsyncTask.THREAD_POOL_EXECUTOR instanceof ExecutorService) ?
     44                     MoreExecutors.listeningDecorator(
     45                             (ExecutorService) AsyncTask.THREAD_POOL_EXECUTOR) :
     46                     MoreExecutors.listeningDecorator(
     47                             Executors.newFixedThreadPool(CORE_POOL_SIZE));
     48 
     49     // We initialize this lazily since in some cases we may never even read from the SIM card
     50     private static ListeningExecutorService sSimExecutor;
     51 
     52     /**
     53      * Returns the default thread pool that can be used for background work.
     54      */
     55     public static ListeningExecutorService getDefaultThreadPoolExecutor() {
     56         return DEFAULT_THREAD_POOL_EXECUTOR;
     57     }
     58 
     59     /**
     60      * Creates an executor that runs commands on the application UI thread
     61      */
     62     public static ScheduledExecutorService newUiThreadExecutor() {
     63         return newHandlerExecutor(new Handler(Looper.getMainLooper()));
     64     }
     65 
     66     /**
     67      * Create an executor that posts commands to the provided handler
     68      */
     69     public static ScheduledExecutorService newHandlerExecutor(final Handler handler) {
     70         return new HandlerExecutorService(handler);
     71     }
     72 
     73     /**
     74      * Returns an ExecutorService that can be used to read from the SIM card.
     75      *
     76      * <p>See b/32831092</p>
     77      * <p>A different executor than {@link ContactsExecutors#getDefaultThreadPoolExecutor()} is
     78      * provided for this case because reads of the SIM card can block for long periods of time
     79      * and if they do we might exhaust our thread pool. Additionally it appears that reading from
     80      * the SIM provider from multiple threads concurrently can cause problems.
     81      * </p>
     82      */
     83     public synchronized static ListeningExecutorService getSimReadExecutor() {
     84         if (sSimExecutor == null) {
     85             final ThreadPoolExecutor executor = new ThreadPoolExecutor(
     86                     1, 1, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
     87             executor.allowCoreThreadTimeOut(true);
     88             sSimExecutor = MoreExecutors.listeningDecorator(executor);
     89         }
     90         return sSimExecutor;
     91     }
     92 
     93     /**
     94      * Wrapper around a handler that implements a subset of the ScheduledExecutorService
     95      *
     96      * <p>This class is useful for testability because Handler can't be mocked since it's
     97      * methods are final. It might be better to just use Executors.newSingleThreadScheduledExecutor
     98      * in the cases where we need to run some time based tasks.
     99      * </p>
    100      */
    101     private static class HandlerExecutorService extends AbstractExecutorService
    102             implements ScheduledExecutorService {
    103         private final Handler mHandler;
    104 
    105         private HandlerExecutorService(Handler handler) {
    106             mHandler = handler;
    107         }
    108 
    109         @NonNull
    110         @Override
    111         public ScheduledFuture<?> schedule(final Runnable command, long delay, TimeUnit unit) {
    112             final HandlerFuture<Void> future = HandlerFuture
    113                     .fromRunnable(mHandler, delay, unit, command);
    114             mHandler.postDelayed(future, unit.toMillis(delay));
    115             return future;
    116         }
    117 
    118         @NonNull
    119         @Override
    120         public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
    121             final HandlerFuture<V> future = new HandlerFuture<>(mHandler, delay, unit, callable);
    122             mHandler.postDelayed(future, unit.toMillis(delay));
    123             return future;
    124         }
    125 
    126         @NonNull
    127         @Override
    128         public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
    129                 long period, TimeUnit unit) {
    130             throw new UnsupportedOperationException();
    131         }
    132 
    133         @NonNull
    134         @Override
    135         public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
    136                 long delay, TimeUnit unit) {
    137             throw new UnsupportedOperationException();
    138         }
    139 
    140         @Override
    141         public void shutdown() {
    142         }
    143 
    144         @Override
    145         public List<Runnable> shutdownNow() {
    146             return null;
    147         }
    148 
    149         @Override
    150         public boolean isShutdown() {
    151             return false;
    152         }
    153 
    154         @Override
    155         public boolean isTerminated() {
    156             return false;
    157         }
    158 
    159         @Override
    160         public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
    161             throw new UnsupportedOperationException();
    162         }
    163 
    164         @Override
    165         public void execute(Runnable command) {
    166             mHandler.post(command);
    167         }
    168     }
    169 
    170     private static class HandlerFuture<T> extends ForwardingFuture<T> implements
    171             RunnableScheduledFuture<T> {
    172 
    173         private final Handler mHandler;
    174         private final long mDelayMillis;
    175         private final Callable<T> mTask;
    176         private final SettableFuture<T> mDelegate = SettableFuture.create();
    177 
    178         private final AtomicLong mStart = new AtomicLong(-1);
    179 
    180         private HandlerFuture(Handler handler, long delay, TimeUnit timeUnit, Callable<T> task) {
    181             mHandler = handler;
    182             mDelayMillis = timeUnit.toMillis(delay);
    183             mTask = task;
    184         }
    185 
    186         @Override
    187         public boolean isPeriodic() {
    188             return false;
    189         }
    190 
    191         @Override
    192         public long getDelay(TimeUnit unit) {
    193             long start = mStart.get();
    194             if (start < 0) {
    195                 return mDelayMillis;
    196             }
    197             long remaining = mDelayMillis - (System.currentTimeMillis() - start);
    198             return TimeUnit.MILLISECONDS.convert(remaining, unit);
    199         }
    200 
    201         @Override
    202         public int compareTo(Delayed o) {
    203             return Long.compare(getDelay(TimeUnit.MILLISECONDS),
    204                     o.getDelay(TimeUnit.MILLISECONDS));
    205         }
    206 
    207         @Override
    208         protected Future<T> delegate() {
    209             return mDelegate;
    210         }
    211 
    212         @Override
    213         public boolean cancel(boolean b) {
    214             mHandler.removeCallbacks(this);
    215             return super.cancel(b);
    216         }
    217 
    218         @Override
    219         public void run() {
    220             if (!mStart.compareAndSet(-1, System.currentTimeMillis())) {
    221                 // Already started
    222                 return;
    223             }
    224             try {
    225                 mDelegate.set(mTask.call());
    226             } catch (Exception e) {
    227                 mDelegate.setException(e);
    228             }
    229         }
    230 
    231         public static HandlerFuture<Void> fromRunnable(Handler handler, long delay, TimeUnit unit,
    232                 final Runnable command) {
    233             return new HandlerFuture<>(handler, delay, unit, new Callable<Void>() {
    234                 @Override
    235                 public Void call() throws Exception {
    236                     command.run();
    237                     return null;
    238                 }
    239             });
    240         }
    241     }
    242 }
    243