Home | History | Annotate | Download | only in portability
      1 /*
      2  * Copyright (C) 2014 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.ex.camera2.portability;
     18 
     19 import android.os.Handler;
     20 import android.os.HandlerThread;
     21 import android.os.SystemClock;
     22 
     23 import com.android.ex.camera2.portability.debug.Log;
     24 
     25 import java.util.LinkedList;
     26 import java.util.Queue;
     27 
     28 public class DispatchThread extends Thread {
     29     private static final Log.Tag TAG = new Log.Tag("DispatchThread");
     30     private static final long MAX_MESSAGE_QUEUE_LENGTH = 256;
     31 
     32     private final Queue<Runnable> mJobQueue;
     33     private Boolean mIsEnded;
     34     private Handler mCameraHandler;
     35     private HandlerThread mCameraHandlerThread;
     36 
     37     public DispatchThread(Handler cameraHandler, HandlerThread cameraHandlerThread) {
     38         super("Camera Job Dispatch Thread");
     39         mJobQueue = new LinkedList<Runnable>();
     40         mIsEnded = new Boolean(false);
     41         mCameraHandler = cameraHandler;
     42         mCameraHandlerThread = cameraHandlerThread;
     43     }
     44 
     45     /**
     46      * Queues up the job.
     47      *
     48      * @param job The job to run.
     49      */
     50     public void runJob(Runnable job) {
     51         if (isEnded()) {
     52             throw new IllegalStateException(
     53                     "Trying to run job on interrupted dispatcher thread");
     54         }
     55         synchronized (mJobQueue) {
     56             if (mJobQueue.size() == MAX_MESSAGE_QUEUE_LENGTH) {
     57                 throw new RuntimeException("Camera master thread job queue full");
     58             }
     59 
     60             mJobQueue.add(job);
     61             mJobQueue.notifyAll();
     62         }
     63     }
     64 
     65     /**
     66      * Queues up the job and wait for it to be done.
     67      *
     68      * @param job The job to run.
     69      * @param timeoutMs Timeout limit in milliseconds.
     70      * @param jobMsg The message to log when the job runs timeout.
     71      * @return Whether the job finishes before timeout.
     72      */
     73     public void runJobSync(final Runnable job, Object waitLock, long timeoutMs, String jobMsg) {
     74         String timeoutMsg = "Timeout waiting " + timeoutMs + "ms for " + jobMsg;
     75         synchronized (waitLock) {
     76             long timeoutBound = SystemClock.uptimeMillis() + timeoutMs;
     77             try {
     78                 runJob(job);
     79                 waitLock.wait(timeoutMs);
     80                 if (SystemClock.uptimeMillis() > timeoutBound) {
     81                     throw new IllegalStateException(timeoutMsg);
     82                 }
     83             } catch (InterruptedException ex) {
     84                 if (SystemClock.uptimeMillis() > timeoutBound) {
     85                     throw new IllegalStateException(timeoutMsg);
     86                 }
     87             }
     88         }
     89     }
     90 
     91     /**
     92      * Gracefully ends this thread. Will stop after all jobs are processed.
     93      */
     94     public void end() {
     95         synchronized (mIsEnded) {
     96             mIsEnded = true;
     97         }
     98         synchronized(mJobQueue) {
     99             mJobQueue.notifyAll();
    100         }
    101     }
    102 
    103     private boolean isEnded() {
    104         synchronized (mIsEnded) {
    105             return mIsEnded;
    106         }
    107     }
    108 
    109     @Override
    110     public void run() {
    111         while(true) {
    112             Runnable job = null;
    113             synchronized (mJobQueue) {
    114                 while (mJobQueue.size() == 0 && !isEnded()) {
    115                     try {
    116                         mJobQueue.wait();
    117                     } catch (InterruptedException ex) {
    118                         Log.w(TAG, "Dispatcher thread wait() interrupted, exiting");
    119                         break;
    120                     }
    121                 }
    122 
    123                 job = mJobQueue.poll();
    124             }
    125 
    126             if (job == null) {
    127                 // mJobQueue.poll() returning null means wait() is
    128                 // interrupted and the queue is empty.
    129                 if (isEnded()) {
    130                     break;
    131                 }
    132                 continue;
    133             }
    134 
    135             job.run();
    136 
    137             synchronized (DispatchThread.this) {
    138                 mCameraHandler.post(new Runnable() {
    139                     @Override
    140                     public void run() {
    141                         synchronized (DispatchThread.this) {
    142                             DispatchThread.this.notifyAll();
    143                         }
    144                     }
    145                 });
    146                 try {
    147                     DispatchThread.this.wait();
    148                 } catch (InterruptedException ex) {
    149                     // TODO: do something here.
    150                 }
    151             }
    152         }
    153         mCameraHandlerThread.quitSafely();
    154     }
    155 }
    156