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