193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein/*
293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Copyright (C) 2013 The Android Open Source Project
393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Licensed under the Apache License, Version 2.0 (the "License");
593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * you may not use this file except in compliance with the License.
693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * You may obtain a copy of the License at
793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *      http://www.apache.org/licenses/LICENSE-2.0
993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
1093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Unless required by applicable law or agreed to in writing, software
1193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * distributed under the License is distributed on an "AS IS" BASIS,
1293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * See the License for the specific language governing permissions and
1493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * limitations under the License.
1593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein */
1693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinpackage com.android.bitmap;
1893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.util.Log;
2093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.util.SparseArray;
2193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
2293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.util.Trace;
2393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
2493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport java.util.ArrayDeque;
2593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport java.util.Iterator;
2693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport java.util.Queue;
2793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
2893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein/**
2993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * An implementation of a task aggregator that executes tasks in the order that they are expected
3093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * . All tasks that is given to {@link #execute(Object, Runnable)} must correspond to a key. This
3193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * key must have been previously declared with {@link #expect(Object, Callback)}.
3293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * The task will be scheduled to run when its corresponding key becomes the first expected key
3393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * amongst the other keys in this aggregator.
3493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <p/>
3593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * If on {@link #execute(Object, Runnable)} the key is not expected, the task will be executed
3693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * immediately as an edge case.
3793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <p/>
3893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * A characteristic scenario is as follows:
3993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <ol>
4093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <li>Expect keys <b>A</b>, <b>B</b>, <b>C</b>, and <b>Z</b>, in that order. Key <b>A</b> is now
4193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * the first expected key.</li>
4293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <li>Execute task <b>2</b> for key <b>B</b>. The first expected key is <b>A</b>,
4393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * which has no task associated with it, so we store task <b>2</b>. </li>
4493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <li>Execute task <b>4</b> for key <b>Z</b>. We store task <b>4</b>. </li>
4593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <li>Execute task <b>1</b> for key <b>A</b>. We run task <b>1</b> because its key <b>A</b> is
4693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * the first expected, then we remove key <b>A</b> from the list of keys that we expect.</li>
4793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <li>This causes key <b>B</b> to be the first expected key, and we see that we have previously
4893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * stored task <b>2</b> for that key. We run task <b>2</b> and remove key <b>B</b>. </li>
4993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <li>Key <b>C</b> is now the first expected key, but it has no task,
5093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * so nothing happens. Task <b>4</b>, which was previously stored,
5193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * cannot run until its corresponding key <b>Z</b> becomes the first expected key. This can
5293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * happen if a task comes in for key <b>C</b> or if forget is called on key <b>C</b>.</li>
5393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * </ol>
5493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <p/>
5593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * ContiguousFIFOAggregator is not thread safe.
5693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein */
5793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinpublic class ContiguousFIFOAggregator<T> {
5893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private final Queue<T> mExpected;
5993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private final SparseArray<Value> mTasks;
6093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
6193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static final String TAG = ContiguousFIFOAggregator.class.getSimpleName();
6293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static final boolean DEBUG = false;
6393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
6493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
6593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * Create a new ContiguousFIFOAggregator.
6693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * <p/>
6793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * The nature of the prioritization means that the number of stored keys and tasks may grow
6893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * unbounded. This may happen, for instance, if the top priority key is never given a task to
6993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * {@link #execute(Object, Runnable)}. However, in practice, if you are generating tasks in
7093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * response to UI elements appearing on the screen, you will only have a bounded set of keys.
7193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * UI elements that scroll off the screen will call {@link #forget(Object)} while new elements
7293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * will call {@link #expect(Object, Callback)}. This means that the expected
7393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * number of keys and tasks is
7493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * the maximum number of UI elements that you expect to show on the screen at any time.
7593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
7693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public ContiguousFIFOAggregator() {
7793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mExpected = new ArrayDeque<T>();
7893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mTasks = new SparseArray<Value>();
7993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
8093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
8193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
8293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * Declare a key to be expected in the future. The order in which you expect keys is very
8393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * important. Keys that are declared first are guaranteed to have their tasks run first. You
8493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * must call either {@link #forget(Object)} or {@link #execute(Object, Runnable)}
8593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * with this key in the future, or you will leak the key.
8693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     *
8793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * If you call expect with a previously expected key, the key will be placed at the back of
8893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * the expected queue and its callback will be replaced with this one.
8993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     *
9093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * @param key      the key to expect a task for. Use the same key when setting the task later
9193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     *                 with {@link #execute (Object, Runnable)}.
9293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * @param callback the callback to notify when the key becomes the first expected key, or null.
9393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
9493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void expect(final T key, final Callback<T> callback) {
9593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (key == null) {
9693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            throw new IllegalArgumentException("Do not use null keys.");
9793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
9893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
9993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.beginSection("pool expect");
10093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final int hash = key.hashCode();
10193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (contains(key)) {
10293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mExpected.remove(key);
10393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mTasks.remove(hash);
10493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
10593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final boolean isFirst = mExpected.isEmpty();
10693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mExpected.offer(key);
10793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mTasks.put(hash, new Value(callback, null));
10893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (DEBUG) {
10993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Log.d(TAG, String.format("ContiguousFIFOAggregator >> tasks: %s", prettyPrint()));
11093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
11193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
11293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (isFirst) {
11393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            onFirstExpectedChanged(key);
11493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
11593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.endSection();
11693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
11793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
11893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
11993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * Remove a previously declared key that we no longer expect to execute a task for. This
12093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * potentially allows another key to now become the first expected key,
12193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * and so this may trigger one or more tasks to be executed.
12293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     *
12393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * @param key the key previously declared in {@link #expect(Object, Callback)}.
12493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     *
12593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
12693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void forget(final T key) {
12793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (key == null) {
12893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            throw new IllegalArgumentException("Do not use null keys.");
12993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
13093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
13193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (!contains(key)) {
13293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return;
13393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
13493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
13593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.beginSection("pool forget");
13693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final boolean removedFirst = key.equals(mExpected.peek());
13793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mExpected.remove(key);
13893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mTasks.delete(key.hashCode());
13993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (DEBUG) {
14093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Log.d(TAG, String.format("ContiguousFIFOAggregator  < tasks: %s", prettyPrint()));
14193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
14293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
14393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final T second;
14493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (removedFirst && (second = mExpected.peek()) != null) {
14593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // We removed the first key. The second key is now first.
14693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            onFirstExpectedChanged(second);
14793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
14893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
14993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        maybeExecuteNow();
15093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.endSection();
15193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
15293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
15393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
15493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * Attempt to execute a task corresponding to a previously declared key. If the key is the
15593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * first expected key, the task will be executed immediately. Otherwise, the task is stored
15693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * until its key becomes the first expected key. Execution of a task results in the removal
15793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * of that key, which potentially allows another key to now become the first expected key,
15893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * and may cause one or more other tasks to be executed.
15993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * <p/>
16093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * If the key is not expected, the task will be executed immediately as an edge case.
16193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     *
16293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * @param key  the key previously declared in {@link #expect(Object, Callback)}.
16393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * @param task the task to execute or store, depending on its corresponding key.
16493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
16593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void execute(final T key, final Runnable task) {
16693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.beginSection("pool execute");
16793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final int hash = key.hashCode();
16893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final Value value = mTasks.get(hash);
16993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (value == null || task == null) {
17093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (task != null) {
17193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                task.run();
17293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
17393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Trace.endSection();
17493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return;
17593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
17693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        value.task = task;
17793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (DEBUG) {
17893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Log.d(TAG, String.format("ContiguousFIFOAggregator ++ tasks: %s", prettyPrint()));
17993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
18093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        maybeExecuteNow();
18193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.endSection();
18293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
18393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
18493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
18593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * Triggered by {@link #execute(Object, Runnable)} and {@link #forget(Object)}. The keys or
18693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * tasks have changed, which may cause one or more tasks to be executed. This method will
18793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * continue to execute tasks associated with the first expected key to the last expected key,
18893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * stopping when it finds that the first expected key has not yet been assigned a task.
18993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
19093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private void maybeExecuteNow() {
19193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        T first;
19293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        int count = 0;
19393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        while (!mExpected.isEmpty()) {
19493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Trace.beginSection("pool maybeExecuteNow loop");
19593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            first = mExpected.peek();
19693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (count > 0) {
19793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                // When count == 0, the key is already first.
19893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                onFirstExpectedChanged(first);
19993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
20093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
20193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final int hash = first.hashCode();
20293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final Value value = mTasks.get(hash);
20393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (value.task == null) {
20493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                Trace.endSection();
20593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                break;
20693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
20793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
20893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mExpected.poll();
20993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mTasks.delete(hash);
21093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (DEBUG) {
21193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                Log.d(TAG, String.format("ContiguousFIFOAggregator  - tasks: %s", prettyPrint()));
21293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
21393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            value.task.run();
21493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            count++;
21593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Trace.endSection();
21693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
21793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
21893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
21993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
22093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * This method should only be called once for any key.
22193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * @param key The key that has become the new first expected key.
22293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
22393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private void onFirstExpectedChanged(final T key) {
22493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final int hash = key.hashCode();
22593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final Value value = mTasks.get(hash);
22693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (value == null) {
22793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return;
22893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
22993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final Callback<T> callback = value.callback;
23093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (callback == null) {
23193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return;
23293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
23393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (DEBUG) {
23493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Log.d(TAG, String.format("ContiguousFIFOAggregator    first: %d", hash));
23593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
23693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        callback.onBecomeFirstExpected(key);
23793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
23893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
23993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private boolean contains(final T key) {
24093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        return mTasks.get(key.hashCode()) != null;
24193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
24293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
24393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
24493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * Get a pretty string representing the internal data.
24593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * @return  a String for the internal data.
24693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
24793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private String prettyPrint() {
24893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (mExpected.isEmpty()) {
24993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return "{}";
25093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
25193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
25293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        StringBuilder buffer = new StringBuilder(mExpected.size() * 28);
25393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        buffer.append('{');
25493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Iterator<T> it = mExpected.iterator();
25593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        while (it.hasNext()) {
25693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final T key = it.next();
25793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final int hash = key.hashCode();
25893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            buffer.append(hash);
25993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            buffer.append('=');
26093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final Value value = mTasks.get(hash);
26193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            buffer.append(value);
26293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (it.hasNext()) {
26393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                buffer.append(", ");
26493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
26593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
26693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        buffer.append('}');
26793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (mExpected.size() != mTasks.size()) {
26893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            buffer.append(" error");
26993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
27093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        return buffer.toString();
27193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
27293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
27393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
27493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * Implement this interface if you want to be notified when the key becomes the first
27593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * expected key.
27693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * @param <T> The type of the key used for the aggregator.
27793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
27893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public interface Callback<T> {
27993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
28093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        /**
28193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         * The key you declared as expected has become the first expected key in this aggregator.
28293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         *
28393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         * We don't need a noLongerFirstExpected() method because this aggregator strictly adds
28493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         * additional to the end of the queue. For a first expected key to no longer be the first
28593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         * expected, it would either have to be forgotten with {@link #forget(Object)} or a task
28693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         * assigned and executed with {@link #execute(Object, Runnable)}.
28793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         *
28893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         * @param key The key that became first. We provide the key so the callback can either not
28993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         *            keep state, or it can keep state which may have changed so the callback can do
29093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         *            a comparison.
29193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         */
29293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        void onBecomeFirstExpected(final T key);
29393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
29493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
29593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
29693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * Holds the callback and task for when a key later becomes the first expected key.
29793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
29893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private class Value {
29993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
30093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final Callback<T> callback;
30193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Runnable task;
30293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
30393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Value(final Callback<T> callback, final Runnable task) {
30493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            this.callback = callback;
30593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            this.task = task;
30693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
30793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
30893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        @Override
30993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        public String toString() {
31093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return String.valueOf(task);
31193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
31293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
31393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein}
314