FileDocCategorySizeDatePackage
DispatchThread.javaAPI DocAndroid 5.1 API4953Thu Mar 12 22:22:48 GMT 2015com.android.ex.camera2.portability

DispatchThread.java

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