1cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher/* 2cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * Copyright (C) 2014 The Android Open Source Project 3cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * 4cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * Licensed under the Apache License, Version 2.0 (the "License"); 5cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * you may not use this file except in compliance with the License. 6cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * You may obtain a copy of the License at 7cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * 8cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * http://www.apache.org/licenses/LICENSE-2.0 9cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * 10cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * Unless required by applicable law or agreed to in writing, software 11cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * distributed under the License is distributed on an "AS IS" BASIS, 12cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * See the License for the specific language governing permissions and 14cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * limitations under the License. 15cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher */ 16cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 17cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucherpackage com.android.ex.camera2.portability; 18cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 19cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucherimport android.os.Handler; 20cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucherimport android.os.HandlerThread; 21cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucherimport android.os.SystemClock; 22cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 23cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucherimport com.android.ex.camera2.portability.debug.Log; 24cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 25cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucherimport java.util.LinkedList; 26cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucherimport java.util.Queue; 27cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 28a0842b40441db5332a5290f941021636b1182761Sol Boucherpublic class DispatchThread extends Thread { 29cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher private static final Log.Tag TAG = new Log.Tag("DispatchThread"); 30cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher private static final long MAX_MESSAGE_QUEUE_LENGTH = 256; 31cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 32cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher private final Queue<Runnable> mJobQueue; 33cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher private Boolean mIsEnded; 34cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher private Handler mCameraHandler; 35cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher private HandlerThread mCameraHandlerThread; 36cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 37cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher public DispatchThread(Handler cameraHandler, HandlerThread cameraHandlerThread) { 38cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher super("Camera Job Dispatch Thread"); 39cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher mJobQueue = new LinkedList<Runnable>(); 40cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher mIsEnded = new Boolean(false); 41cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher mCameraHandler = cameraHandler; 42cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher mCameraHandlerThread = cameraHandlerThread; 43cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 44cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 45cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher /** 46cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * Queues up the job. 47cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * 48cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * @param job The job to run. 49cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher */ 50cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher public void runJob(Runnable job) { 51cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher if (isEnded()) { 52cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher throw new IllegalStateException( 53cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher "Trying to run job on interrupted dispatcher thread"); 54cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 55cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher synchronized (mJobQueue) { 56cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher if (mJobQueue.size() == MAX_MESSAGE_QUEUE_LENGTH) { 57cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher throw new RuntimeException("Camera master thread job queue full"); 58cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 59cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 60cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher mJobQueue.add(job); 61cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher mJobQueue.notifyAll(); 62cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 63cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 64cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 65cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher /** 66cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * Queues up the job and wait for it to be done. 67cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * 68cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * @param job The job to run. 69cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * @param timeoutMs Timeout limit in milliseconds. 70cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * @param jobMsg The message to log when the job runs timeout. 71cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * @return Whether the job finishes before timeout. 72cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher */ 73cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher public void runJobSync(final Runnable job, Object waitLock, long timeoutMs, String jobMsg) { 74cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher String timeoutMsg = "Timeout waiting " + timeoutMs + "ms for " + jobMsg; 75cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher synchronized (waitLock) { 76cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher long timeoutBound = SystemClock.uptimeMillis() + timeoutMs; 77cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher try { 78cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher runJob(job); 79cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher waitLock.wait(timeoutMs); 80cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher if (SystemClock.uptimeMillis() > timeoutBound) { 81cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher throw new IllegalStateException(timeoutMsg); 82cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 83cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } catch (InterruptedException ex) { 84cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher if (SystemClock.uptimeMillis() > timeoutBound) { 85cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher throw new IllegalStateException(timeoutMsg); 86cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 87cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 88cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 89cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 90cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 91cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher /** 92cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher * Gracefully ends this thread. Will stop after all jobs are processed. 93cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher */ 94cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher public void end() { 95cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher synchronized (mIsEnded) { 96cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher mIsEnded = true; 97cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 98cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher synchronized(mJobQueue) { 99cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher mJobQueue.notifyAll(); 100cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 101cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 102cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 103cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher private boolean isEnded() { 104cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher synchronized (mIsEnded) { 105cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher return mIsEnded; 106cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 107cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 108cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 109cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher @Override 110cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher public void run() { 111cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher while(true) { 112cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher Runnable job = null; 113cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher synchronized (mJobQueue) { 114cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher while (mJobQueue.size() == 0 && !isEnded()) { 115cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher try { 116cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher mJobQueue.wait(); 117cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } catch (InterruptedException ex) { 118cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher Log.w(TAG, "Dispatcher thread wait() interrupted, exiting"); 119cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher break; 120cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 121cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 122cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 123cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher job = mJobQueue.poll(); 124cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 125cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 126cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher if (job == null) { 127cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher // mJobQueue.poll() returning null means wait() is 128cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher // interrupted and the queue is empty. 129cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher if (isEnded()) { 130cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher break; 131cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 132cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher continue; 133cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 134cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 135cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher job.run(); 136cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher 137cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher synchronized (DispatchThread.this) { 138cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher mCameraHandler.post(new Runnable() { 139cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher @Override 140cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher public void run() { 141cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher synchronized (DispatchThread.this) { 142cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher DispatchThread.this.notifyAll(); 143cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 144cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 145cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher }); 146cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher try { 147cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher DispatchThread.this.wait(); 148cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } catch (InterruptedException ex) { 149cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher // TODO: do something here. 150cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 151cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 152cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 153cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher mCameraHandlerThread.quitSafely(); 154cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher } 155cef46862d6937bc98bf1a6b087c5daa22b5239f3Sol Boucher} 156