| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ex.camera2.portability; |
| |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.SystemClock; |
| |
| import com.android.ex.camera2.portability.debug.Log; |
| |
| import java.util.LinkedList; |
| import java.util.Queue; |
| |
| public class DispatchThread extends Thread { |
| private static final Log.Tag TAG = new Log.Tag("DispatchThread"); |
| private static final long MAX_MESSAGE_QUEUE_LENGTH = 256; |
| |
| private final Queue<Runnable> mJobQueue; |
| private Boolean mIsEnded; |
| private Handler mCameraHandler; |
| private HandlerThread mCameraHandlerThread; |
| |
| public DispatchThread(Handler cameraHandler, HandlerThread cameraHandlerThread) { |
| super("Camera Job Dispatch Thread"); |
| mJobQueue = new LinkedList<Runnable>(); |
| mIsEnded = new Boolean(false); |
| mCameraHandler = cameraHandler; |
| mCameraHandlerThread = cameraHandlerThread; |
| } |
| |
| /** |
| * Queues up the job. |
| * |
| * @param job The job to run. |
| */ |
| public void runJob(Runnable job) { |
| if (isEnded()) { |
| throw new IllegalStateException( |
| "Trying to run job on interrupted dispatcher thread"); |
| } |
| synchronized (mJobQueue) { |
| if (mJobQueue.size() == MAX_MESSAGE_QUEUE_LENGTH) { |
| throw new RuntimeException("Camera master thread job queue full"); |
| } |
| |
| mJobQueue.add(job); |
| mJobQueue.notifyAll(); |
| } |
| } |
| |
| /** |
| * Queues up the job and wait for it to be done. |
| * |
| * @param job The job to run. |
| * @param timeoutMs Timeout limit in milliseconds. |
| * @param jobMsg The message to log when the job runs timeout. |
| * @return Whether the job finishes before timeout. |
| */ |
| public void runJobSync(final Runnable job, Object waitLock, long timeoutMs, String jobMsg) { |
| String timeoutMsg = "Timeout waiting " + timeoutMs + "ms for " + jobMsg; |
| synchronized (waitLock) { |
| long timeoutBound = SystemClock.uptimeMillis() + timeoutMs; |
| try { |
| runJob(job); |
| waitLock.wait(timeoutMs); |
| if (SystemClock.uptimeMillis() > timeoutBound) { |
| throw new IllegalStateException(timeoutMsg); |
| } |
| } catch (InterruptedException ex) { |
| if (SystemClock.uptimeMillis() > timeoutBound) { |
| throw new IllegalStateException(timeoutMsg); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Gracefully ends this thread. Will stop after all jobs are processed. |
| */ |
| public void end() { |
| synchronized (mIsEnded) { |
| mIsEnded = true; |
| } |
| synchronized(mJobQueue) { |
| mJobQueue.notifyAll(); |
| } |
| } |
| |
| private boolean isEnded() { |
| synchronized (mIsEnded) { |
| return mIsEnded; |
| } |
| } |
| |
| @Override |
| public void run() { |
| while(true) { |
| Runnable job = null; |
| synchronized (mJobQueue) { |
| while (mJobQueue.size() == 0 && !isEnded()) { |
| try { |
| mJobQueue.wait(); |
| } catch (InterruptedException ex) { |
| Log.w(TAG, "Dispatcher thread wait() interrupted, exiting"); |
| break; |
| } |
| } |
| |
| job = mJobQueue.poll(); |
| } |
| |
| if (job == null) { |
| // mJobQueue.poll() returning null means wait() is |
| // interrupted and the queue is empty. |
| if (isEnded()) { |
| break; |
| } |
| continue; |
| } |
| |
| job.run(); |
| |
| synchronized (DispatchThread.this) { |
| mCameraHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| synchronized (DispatchThread.this) { |
| DispatchThread.this.notifyAll(); |
| } |
| } |
| }); |
| try { |
| DispatchThread.this.wait(); |
| } catch (InterruptedException ex) { |
| // TODO: do something here. |
| } |
| } |
| } |
| mCameraHandlerThread.quitSafely(); |
| } |
| } |