• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.gallery3d.util;
18 
19 import java.util.concurrent.Executor;
20 import java.util.concurrent.LinkedBlockingQueue;
21 import java.util.concurrent.ThreadPoolExecutor;
22 import java.util.concurrent.TimeUnit;
23 
24 public class ThreadPool {
25     private static final String TAG = "ThreadPool";
26     private static final int CORE_POOL_SIZE = 4;
27     private static final int MAX_POOL_SIZE = 8;
28     private static final int KEEP_ALIVE_TIME = 10; // 10 seconds
29 
30     // Resource type
31     public static final int MODE_NONE = 0;
32     public static final int MODE_CPU = 1;
33     public static final int MODE_NETWORK = 2;
34 
35     public static final JobContext JOB_CONTEXT_STUB = new JobContextStub();
36 
37     ResourceCounter mCpuCounter = new ResourceCounter(2);
38     ResourceCounter mNetworkCounter = new ResourceCounter(2);
39 
40     // A Job is like a Callable, but it has an addition JobContext parameter.
41     public interface Job<T> {
run(JobContext jc)42         public T run(JobContext jc);
43     }
44 
45     public interface JobContext {
isCancelled()46         boolean isCancelled();
setCancelListener(CancelListener listener)47         void setCancelListener(CancelListener listener);
setMode(int mode)48         boolean setMode(int mode);
49     }
50 
51     private static class JobContextStub implements JobContext {
52         @Override
isCancelled()53         public boolean isCancelled() {
54             return false;
55         }
56 
57         @Override
setCancelListener(CancelListener listener)58         public void setCancelListener(CancelListener listener) {
59         }
60 
61         @Override
setMode(int mode)62         public boolean setMode(int mode) {
63             return true;
64         }
65     }
66 
67     public interface CancelListener {
onCancel()68         public void onCancel();
69     }
70 
71     private static class ResourceCounter {
72         public int value;
ResourceCounter(int v)73         public ResourceCounter(int v) {
74             value = v;
75         }
76     }
77 
78     private final Executor mExecutor;
79 
ThreadPool()80     public ThreadPool() {
81         mExecutor = new ThreadPoolExecutor(
82                 CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME,
83                 TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
84                 new PriorityThreadFactory("thread-pool",
85                 android.os.Process.THREAD_PRIORITY_BACKGROUND));
86     }
87 
88     // Submit a job to the thread pool. The listener will be called when the
89     // job is finished (or cancelled).
submit(Job<T> job, FutureListener<T> listener)90     public <T> Future<T> submit(Job<T> job, FutureListener<T> listener) {
91         Worker<T> w = new Worker<T>(job, listener);
92         mExecutor.execute(w);
93         return w;
94     }
95 
submit(Job<T> job)96     public <T> Future<T> submit(Job<T> job) {
97         return submit(job, null);
98     }
99 
100     private class Worker<T> implements Runnable, Future<T>, JobContext {
101         private static final String TAG = "Worker";
102         private Job<T> mJob;
103         private FutureListener<T> mListener;
104         private CancelListener mCancelListener;
105         private ResourceCounter mWaitOnResource;
106         private volatile boolean mIsCancelled;
107         private boolean mIsDone;
108         private T mResult;
109         private int mMode;
110 
Worker(Job<T> job, FutureListener<T> listener)111         public Worker(Job<T> job, FutureListener<T> listener) {
112             mJob = job;
113             mListener = listener;
114         }
115 
116         // This is called by a thread in the thread pool.
run()117         public void run() {
118             T result = null;
119 
120             // A job is in CPU mode by default. setMode returns false
121             // if the job is cancelled.
122             if (setMode(MODE_CPU)) {
123                 try {
124                     result = mJob.run(this);
125                 } catch (Throwable ex) {
126                     Log.w(TAG, "Exception in running a job", ex);
127                 }
128             }
129 
130             synchronized(this) {
131                 setMode(MODE_NONE);
132                 mResult = result;
133                 mIsDone = true;
134                 notifyAll();
135             }
136             if (mListener != null) mListener.onFutureDone(this);
137         }
138 
139         // Below are the methods for Future.
cancel()140         public synchronized void cancel() {
141             if (mIsCancelled) return;
142             mIsCancelled = true;
143             if (mWaitOnResource != null) {
144                 synchronized (mWaitOnResource) {
145                     mWaitOnResource.notifyAll();
146                 }
147             }
148             if (mCancelListener != null) {
149                 mCancelListener.onCancel();
150             }
151         }
152 
isCancelled()153         public boolean isCancelled() {
154             return mIsCancelled;
155         }
156 
isDone()157         public synchronized boolean isDone() {
158             return mIsDone;
159         }
160 
get()161         public synchronized T get() {
162             while (!mIsDone) {
163                 try {
164                     wait();
165                 } catch (Exception ex) {
166                     Log.w(TAG, "ingore exception", ex);
167                     // ignore.
168                 }
169             }
170             return mResult;
171         }
172 
waitDone()173         public void waitDone() {
174             get();
175         }
176 
177         // Below are the methods for JobContext (only called from the
178         // thread running the job)
setCancelListener(CancelListener listener)179         public synchronized void setCancelListener(CancelListener listener) {
180             mCancelListener = listener;
181             if (mIsCancelled && mCancelListener != null) {
182                 mCancelListener.onCancel();
183             }
184         }
185 
setMode(int mode)186         public boolean setMode(int mode) {
187             // Release old resource
188             ResourceCounter rc = modeToCounter(mMode);
189             if (rc != null) releaseResource(rc);
190             mMode = MODE_NONE;
191 
192             // Acquire new resource
193             rc = modeToCounter(mode);
194             if (rc != null) {
195                 if (!acquireResource(rc)) {
196                     return false;
197                 }
198                 mMode = mode;
199             }
200 
201             return true;
202         }
203 
modeToCounter(int mode)204         private ResourceCounter modeToCounter(int mode) {
205             if (mode == MODE_CPU) {
206                 return mCpuCounter;
207             } else if (mode == MODE_NETWORK) {
208                 return mNetworkCounter;
209             } else {
210                 return null;
211             }
212         }
213 
acquireResource(ResourceCounter counter)214         private boolean acquireResource(ResourceCounter counter) {
215             while (true) {
216                 synchronized (this) {
217                     if (mIsCancelled) {
218                         mWaitOnResource = null;
219                         return false;
220                     }
221                     mWaitOnResource = counter;
222                 }
223 
224                 synchronized (counter) {
225                     if (counter.value > 0) {
226                         counter.value--;
227                         break;
228                     } else {
229                         try {
230                             counter.wait();
231                         } catch (InterruptedException ex) {
232                             // ignore.
233                         }
234                     }
235                 }
236             }
237 
238             synchronized (this) {
239                 mWaitOnResource = null;
240             }
241 
242             return true;
243         }
244 
releaseResource(ResourceCounter counter)245         private void releaseResource(ResourceCounter counter) {
246             synchronized (counter) {
247                 counter.value++;
248                 counter.notifyAll();
249             }
250         }
251     }
252 }
253