• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base.task;
6 
7 import android.util.Pair;
8 
9 import org.chromium.base.TraceEvent;
10 
11 import java.util.LinkedList;
12 
13 import javax.annotation.concurrent.GuardedBy;
14 
15 /**
16  * Allows chaining multiple tasks on arbitrary threads, with the next task posted when one
17  * completes.
18  *
19  * How this differs from SequencedTaskRunner:
20  * Deferred posting of subsequent tasks allows more time for Android framework tasks to run
21  * (e.g. input events). As such, this class really only makes sense when submitting tasks to
22  * SingleThreadTaskRunners.
23  *
24  * Threading:
25  * - This class is threadsafe and all methods may be called from any thread.
26  * - Tasks may run with arbitrary TaskTraits, unless tasks are coalesced, in which case all tasks
27  *   must run on the same thread.
28  */
29 public class ChainedTasks {
30     private final LinkedList<Pair<Integer, Runnable>> mTasks = new LinkedList<>();
31 
32     @GuardedBy("mTasks")
33     private boolean mFinalized;
34 
35     private volatile boolean mCanceled;
36     private int mIterationIdForTesting = PostTask.sTestIterationForTesting;
37 
38     private final Runnable mRunAndPost =
39             new Runnable() {
40                 @Override
41                 @SuppressWarnings("NoDynamicStringsInTraceEventCheck")
42                 public void run() {
43                     if (mIterationIdForTesting != PostTask.sTestIterationForTesting) {
44                         cancel();
45                     }
46                     if (mCanceled) return;
47 
48                     Pair<Integer, Runnable> pair = mTasks.pop();
49                     try (TraceEvent e =
50                             TraceEvent.scoped(
51                                     "ChainedTask.run: " + pair.second.getClass().getName())) {
52                         pair.second.run();
53                     }
54                     if (!mTasks.isEmpty()) PostTask.postTask(mTasks.peek().first, this);
55                 }
56             };
57 
58     /**
59      * Adds a task to the list of tasks to run. Cannot be called once {@link start()} has been
60      * called.
61      */
add(@askTraits int traits, Runnable task)62     public void add(@TaskTraits int traits, Runnable task) {
63         assert mIterationIdForTesting == PostTask.sTestIterationForTesting;
64 
65         synchronized (mTasks) {
66             assert !mFinalized : "Must not call add() after start()";
67             mTasks.add(new Pair<>(traits, task));
68         }
69     }
70 
71     /** Cancels the remaining tasks. */
cancel()72     public void cancel() {
73         synchronized (mTasks) {
74             mFinalized = true;
75             mCanceled = true;
76         }
77     }
78 
79     /**
80      * Posts or runs all the tasks, one by one.
81      *
82      * @param coalesceTasks if false, posts the tasks. Otherwise run them in a single task. If
83      * called on the thread matching the TaskTraits, will block and run all tasks synchronously.
84      */
start(final boolean coalesceTasks)85     public void start(final boolean coalesceTasks) {
86         synchronized (mTasks) {
87             assert !mFinalized : "Cannot call start() several times";
88             mFinalized = true;
89         }
90         if (mTasks.isEmpty()) return;
91         if (coalesceTasks) {
92             @TaskTraits int traits = mTasks.peek().first;
93             PostTask.runOrPostTask(
94                     traits,
95                     () -> {
96                         for (Pair<Integer, Runnable> pair : mTasks) {
97                             assert PostTask.canRunTaskImmediately(pair.first);
98                             pair.second.run();
99                             if (mCanceled) return;
100                         }
101                     });
102         } else {
103             PostTask.postTask(mTasks.peek().first, mRunAndPost);
104         }
105     }
106 }
107