/*
|
* 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();
|
}
|
}
|