Home | History | Annotate | Download | only in util
      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> {
     42         public T run(JobContext jc);
     43     }
     44 
     45     public interface JobContext {
     46         boolean isCancelled();
     47         void setCancelListener(CancelListener listener);
     48         boolean setMode(int mode);
     49     }
     50 
     51     private static class JobContextStub implements JobContext {
     52         @Override
     53         public boolean isCancelled() {
     54             return false;
     55         }
     56 
     57         @Override
     58         public void setCancelListener(CancelListener listener) {
     59         }
     60 
     61         @Override
     62         public boolean setMode(int mode) {
     63             return true;
     64         }
     65     }
     66 
     67     public interface CancelListener {
     68         public void onCancel();
     69     }
     70 
     71     private static class ResourceCounter {
     72         public int value;
     73         public ResourceCounter(int v) {
     74             value = v;
     75         }
     76     }
     77 
     78     private final Executor mExecutor;
     79 
     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).
     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 
     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 
    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.
    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.
    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 
    153         public boolean isCancelled() {
    154             return mIsCancelled;
    155         }
    156 
    157         public synchronized boolean isDone() {
    158             return mIsDone;
    159         }
    160 
    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 
    173         public void waitDone() {
    174             get();
    175         }
    176 
    177         // Below are the methods for JobContext (only called from the
    178         // thread running the job)
    179         public synchronized void setCancelListener(CancelListener listener) {
    180             mCancelListener = listener;
    181             if (mIsCancelled && mCancelListener != null) {
    182                 mCancelListener.onCancel();
    183             }
    184         }
    185 
    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 
    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 
    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 
    245         private void releaseResource(ResourceCounter counter) {
    246             synchronized (counter) {
    247                 counter.value++;
    248                 counter.notifyAll();
    249             }
    250         }
    251     }
    252 }
    253