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