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