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