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