10b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin/*
20b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * Copyright (C) 2014 The Android Open Source Project
30b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin *
40b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * Licensed under the Apache License, Version 2.0 (the "License");
50b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * you may not use this file except in compliance with the License.
60b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * You may obtain a copy of the License at
70b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin *
80b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin *      http://www.apache.org/licenses/LICENSE-2.0
90b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin *
100b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * Unless required by applicable law or agreed to in writing, software
110b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * distributed under the License is distributed on an "AS IS" BASIS,
120b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * See the License for the specific language governing permissions and
140b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * limitations under the License.
150b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin */
160b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkinpackage android.hardware.camera2.utils;
170b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
180b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkinimport android.os.Handler;
190b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkinimport android.util.Log;
200b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
210b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkinimport java.util.HashSet;
220b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkinimport java.util.Set;
230b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
240b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkinimport static com.android.internal.util.Preconditions.*;
250b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
260b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin/**
270b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * Keep track of multiple concurrent tasks starting and finishing by their key;
280b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * allow draining existing tasks and figuring out when all tasks have finished
290b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * (and new ones won't begin).
300b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin *
310b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * <p>The initial state is to allow all tasks to be started and finished. A task may only be started
320b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * once, after which it must be finished before starting again. Likewise, finishing a task
330b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * that hasn't been started is also not allowed.</p>
340b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin *
350b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * <p>When draining begins, no more new tasks can be started. This guarantees that at some
360b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * point when all the tasks are finished there will be no more collective new tasks,
370b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * at which point the {@link DrainListener#onDrained} callback will be invoked.</p>
380b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin *
390b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin *
400b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin * @param <T>
410b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin *          a type for the key that will represent tracked tasks;
420b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin *          must implement {@code Object#equals}
430b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin */
440b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkinpublic class TaskDrainer<T> {
450b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    /**
460b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * Fired asynchronously after draining has begun with {@link TaskDrainer#beginDrain}
470b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * <em>and</em> all tasks that were started have finished.
480b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     */
490b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    public interface DrainListener {
500b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        /** All tasks have fully finished draining; there will be no more pending tasks. */
510b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        public void onDrained();
520b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    }
530b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
540b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    private static final String TAG = "TaskDrainer";
55e1442204c1a2e8c6697d0640df06dc6314ed7113Igor Murashkin    private final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
560b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
570b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    private final Handler mHandler;
580b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    private final DrainListener mListener;
590b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    private final String mName;
600b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
610b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    /** Set of tasks which have been started but not yet finished with #taskFinished */
620b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    private final Set<T> mTaskSet = new HashSet<T>();
630b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    private final Object mLock = new Object();
640b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
650b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    private boolean mDraining = false;
660b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    private boolean mDrainFinished = false;
670b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
680b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    /**
690b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
700b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * via the {@code handler}.
710b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *
720b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @param handler a non-{@code null} handler to use to post runnables to
730b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @param listener a non-{@code null} listener where {@code onDrained} will be called
740b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     */
750b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    public TaskDrainer(Handler handler, DrainListener listener) {
760b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        mHandler = checkNotNull(handler, "handler must not be null");
770b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        mListener = checkNotNull(listener, "listener must not be null");
780b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        mName = null;
790b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    }
800b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
810b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    /**
820b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
830b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * via the {@code handler}.
840b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *
850b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @param handler a non-{@code null} handler to use to post runnables to
860b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @param listener a non-{@code null} listener where {@code onDrained} will be called
870b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @param name an optional name used for debug logging
880b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     */
890b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    public TaskDrainer(Handler handler, DrainListener listener, String name) {
900b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        // XX: Probably don't need a handler at all here
910b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        mHandler = checkNotNull(handler, "handler must not be null");
920b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        mListener = checkNotNull(listener, "listener must not be null");
930b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        mName = name;
940b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    }
950b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
960b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    /**
970b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * Mark an asynchronous task as having started.
980b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *
990b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * <p>A task cannot be started more than once without first having finished. Once
1000b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * draining begins with {@link #beginDrain}, no new tasks can be started.</p>
1010b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *
1020b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @param task a key to identify a task
1030b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *
1040b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @see #taskFinished
1050b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @see #beginDrain
1060b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *
1070b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @throws IllegalStateException
1080b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *          If attempting to start a task which is already started (and not finished),
1090b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *          or if attempting to start a task after draining has begun.
1100b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     */
1110b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    public void taskStarted(T task) {
1120b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        synchronized (mLock) {
1130b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            if (VERBOSE) {
1140b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                Log.v(TAG + "[" + mName + "]", "taskStarted " + task);
1150b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            }
1160b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
1170b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            if (mDraining) {
1180b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                throw new IllegalStateException("Can't start more tasks after draining has begun");
1190b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            }
1200b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
1210b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            if (!mTaskSet.add(task)) {
1220b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                throw new IllegalStateException("Task " + task + " was already started");
1230b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            }
1240b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        }
1250b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    }
1260b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
1270b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
1280b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    /**
1290b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * Mark an asynchronous task as having finished.
1300b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *
1310b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * <p>A task cannot be finished if it hasn't started. Once finished, a task
1320b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * cannot be finished again (unless it's started again).</p>
1330b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *
1340b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @param task a key to identify a task
1350b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *
1360b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @see #taskStarted
1370b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @see #beginDrain
1380b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *
1390b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * @throws IllegalStateException
1400b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *          If attempting to start a task which is already finished (and not re-started),
1410b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     */
1420b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    public void taskFinished(T task) {
1430b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        synchronized (mLock) {
1440b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            if (VERBOSE) {
1450b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                Log.v(TAG + "[" + mName + "]", "taskFinished " + task);
1460b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            }
1470b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
1480b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            if (!mTaskSet.remove(task)) {
1490b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                throw new IllegalStateException("Task " + task + " was already finished");
1500b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            }
1510b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
1520b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            // If this is the last finished task and draining has already begun, fire #onDrained
1530b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            checkIfDrainFinished();
1540b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        }
1550b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    }
1560b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
1570b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    /**
1580b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * Do not allow any more tasks to be started; once all existing started tasks are finished,
1590b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * fire the {@link DrainListener#onDrained} callback asynchronously.
1600b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     *
1610b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     * <p>This operation is idempotent; calling it more than once has no effect.</p>
1620b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin     */
1630b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    public void beginDrain() {
1640b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        synchronized (mLock) {
1650b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            if (!mDraining) {
1660b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                if (VERBOSE) {
1670b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                    Log.v(TAG + "[" + mName + "]", "beginDrain started");
1680b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                }
1690b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
1700b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                mDraining = true;
1710b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
1720b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                // If all tasks that had started had already finished by now, fire #onDrained
1730b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                checkIfDrainFinished();
1740b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            } else {
1750b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                if (VERBOSE) {
1760b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                    Log.v(TAG + "[" + mName + "]", "beginDrain ignored");
1770b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                }
1780b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            }
1790b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        }
1800b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    }
1810b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
1820b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    private void checkIfDrainFinished() {
1830b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        if (mTaskSet.isEmpty() && mDraining && !mDrainFinished) {
1840b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            mDrainFinished = true;
1850b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            postDrained();
1860b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        }
1870b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    }
1880b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
1890b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    private void postDrained() {
1900b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        mHandler.post(new Runnable() {
1910b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            @Override
1920b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            public void run() {
1930b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                if (VERBOSE) {
1940b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                    Log.v(TAG + "[" + mName + "]", "onDrained");
1950b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                }
1960b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin
1970b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin                mListener.onDrained();
1980b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin            }
1990b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin        });
2000b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin    }
2010b27d3453d5e257594792e9177c5fedb1bc6f9e9Igor Murashkin}
202