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