• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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.os.Handler;
8 
9 import org.chromium.base.Log;
10 import org.chromium.base.ThreadUtils;
11 import org.chromium.base.annotations.CalledByNative;
12 import org.chromium.base.annotations.JNINamespace;
13 
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.concurrent.Callable;
17 import java.util.concurrent.Executor;
18 import java.util.concurrent.FutureTask;
19 import java.util.concurrent.TimeUnit;
20 
21 import javax.annotation.concurrent.GuardedBy;
22 
23 /**
24  * Java interface to the native chromium scheduler.  Note tasks can be posted before native
25  * initialization, but task prioritization is extremely limited. Once the native scheduler
26  * is ready, tasks will be migrated over.
27  */
28 @JNINamespace("base")
29 public class PostTask {
30     private static final String TAG = "PostTask";
31     private static final Object sPreNativeTaskRunnerLock = new Object();
32     @GuardedBy("sPreNativeTaskRunnerLock")
33     private static List<TaskRunnerImpl> sPreNativeTaskRunners = new ArrayList<>();
34 
35     // Volatile is sufficient for synchronization here since we never need to read-write. This is a
36     // one-way switch (outside of testing) and volatile makes writes to it immediately visible to
37     // other threads.
38     private static volatile boolean sNativeInitialized;
39     private static ChromeThreadPoolExecutor sPrenativeThreadPoolExecutor =
40             new ChromeThreadPoolExecutor();
41     private static volatile Executor sPrenativeThreadPoolExecutorOverride;
42 
43     private static final ThreadPoolTaskExecutor sThreadPoolTaskExecutor =
44             new ThreadPoolTaskExecutor();
45     // Initialized on demand or when the UI thread is initialized to allow embedders (eg WebView) to
46     // override the UI thread.
47     private static UiThreadTaskExecutor sUiThreadTaskExecutor;
48 
49     // Used by AsyncTask / ChainedTask to auto-cancel tasks from prior tests.
50     static int sTestIterationForTesting;
51 
52     /**
53      * @param traits The TaskTraits that describe the desired TaskRunner.
54      * @return The TaskRunner for the specified TaskTraits.
55      */
createTaskRunner(@askTraits int taskTraits)56     public static TaskRunner createTaskRunner(@TaskTraits int taskTraits) {
57         return getTaskExecutorForTraits(taskTraits).createTaskRunner(taskTraits);
58     }
59 
60     /**
61      * Creates and returns a SequencedTaskRunner. SequencedTaskRunners automatically destroy
62      * themselves, so the destroy() function is not required to be called.
63      * @param traits The TaskTraits that describe the desired TaskRunner.
64      * @return The TaskRunner for the specified TaskTraits.
65      */
createSequencedTaskRunner(@askTraits int taskTraits)66     public static SequencedTaskRunner createSequencedTaskRunner(@TaskTraits int taskTraits) {
67         return getTaskExecutorForTraits(taskTraits).createSequencedTaskRunner(taskTraits);
68     }
69 
70     /**
71      *
72      * @param traits The TaskTraits that describe the desired TaskRunner.
73      * @return The TaskRunner for the specified TaskTraits.
74      */
createSingleThreadTaskRunner(@askTraits int taskTraits)75     public static SingleThreadTaskRunner createSingleThreadTaskRunner(@TaskTraits int taskTraits) {
76         return getTaskExecutorForTraits(taskTraits).createSingleThreadTaskRunner(taskTraits);
77     }
78 
79     /**
80      * @param taskTraits The TaskTraits that describe the desired TaskRunner.
81      * @param task The task to be run with the specified traits.
82      */
postTask(@askTraits int taskTraits, Runnable task)83     public static void postTask(@TaskTraits int taskTraits, Runnable task) {
84         postDelayedTask(taskTraits, task, 0);
85     }
86 
87     /**
88      * @param taskTraits The TaskTraits that describe the desired TaskRunner.
89      * @param task The task to be run with the specified traits.
90      * @param delay The delay in milliseconds before the task can be run.
91      */
postDelayedTask(@askTraits int taskTraits, Runnable task, long delay)92     public static void postDelayedTask(@TaskTraits int taskTraits, Runnable task, long delay) {
93         getTaskExecutorForTraits(taskTraits).postDelayedTask(taskTraits, task, delay);
94     }
95 
96     /**
97      * This function executes the task immediately if the current thread is the
98      * same as the one corresponding to the SingleThreadTaskRunner, otherwise it
99      * posts it.
100      *
101      * It should be executed only for tasks with traits corresponding to
102      * executors backed by a SingleThreadTaskRunner, like TaskTraits.UI_*.
103      *
104      * Use this only for trivial tasks as it ignores task priorities.
105      *
106      * @param taskTraits The TaskTraits that describe the desired TaskRunner.
107      * @param task The task to be run with the specified traits.
108      */
runOrPostTask(@askTraits int taskTraits, Runnable task)109     public static void runOrPostTask(@TaskTraits int taskTraits, Runnable task) {
110         if (getTaskExecutorForTraits(taskTraits).canRunTaskImmediately(taskTraits)) {
111             task.run();
112         } else {
113             postTask(taskTraits, task);
114         }
115     }
116 
117     /**
118      * Returns true if the task can be executed immediately (i.e. the current thread is the same as
119      * the one corresponding to the SingleThreadTaskRunner)
120      */
canRunTaskImmediately(@askTraits int taskTraits)121     public static boolean canRunTaskImmediately(@TaskTraits int taskTraits) {
122         return getTaskExecutorForTraits(taskTraits).canRunTaskImmediately(taskTraits);
123     }
124 
125     /**
126      * This function executes the task immediately if the current thread is the
127      * same as the one corresponding to the SingleThreadTaskRunner, otherwise it
128      * posts it and blocks until the task finishes.
129      *
130      * It should be executed only for tasks with traits corresponding to
131      * executors backed by a SingleThreadTaskRunner, like TaskTraits.UI_*.
132      *
133      * Use this only for trivial tasks as it ignores task priorities.
134      *
135      * Note that non-test usage of this function is heavily discouraged. For non-tests, use
136      * callbacks rather than blocking threads.
137      *
138      * @param taskTraits The TaskTraits that describe the desired TaskRunner.
139      * @param task The task to be run with the specified traits.
140      * @return The result of the callable
141      */
142     @Deprecated
runSynchronously(@askTraits int taskTraits, Callable<T> c)143     public static <T> T runSynchronously(@TaskTraits int taskTraits, Callable<T> c) {
144         return runSynchronouslyInternal(taskTraits, new FutureTask<T>(c));
145     }
146 
147     /**
148      * This function executes the task immediately if the current thread is the
149      * same as the one corresponding to the SingleThreadTaskRunner, otherwise it
150      * posts it and blocks until the task finishes.
151      *
152      * It should be executed only for tasks with traits corresponding to
153      * executors backed by a SingleThreadTaskRunner, like TaskTraits.UI_*.
154      *
155      * Use this only for trivial tasks as it ignores task priorities.
156      *
157      * Note that non-test usage of this function is heavily discouraged. For non-tests, use
158      * callbacks rather than blocking threads.
159      *
160      * @param taskTraits The TaskTraits that describe the desired TaskRunner.
161      * @param task The task to be run with the specified traits.
162      */
163     @Deprecated
runSynchronously(@askTraits int taskTraits, Runnable r)164     public static void runSynchronously(@TaskTraits int taskTraits, Runnable r) {
165         runSynchronouslyInternal(taskTraits, new FutureTask<Void>(r, null));
166     }
167 
runSynchronouslyInternal(@askTraits int taskTraits, FutureTask<T> task)168     private static <T> T runSynchronouslyInternal(@TaskTraits int taskTraits, FutureTask<T> task) {
169         runOrPostTask(taskTraits, task);
170         try {
171             return task.get();
172         } catch (Exception e) {
173             throw new RuntimeException(e);
174         }
175     }
176 
177     /**
178      * Lets a test override the pre-native thread pool executor.
179      *
180      * @param executor The Executor to use for pre-native thread pool tasks.
181      */
setPrenativeThreadPoolExecutorForTesting(Executor executor)182     public static void setPrenativeThreadPoolExecutorForTesting(Executor executor) {
183         sPrenativeThreadPoolExecutorOverride = executor;
184     }
185 
186     /**
187      * Clears an override set by setPrenativeThreadPoolExecutorOverrideForTesting.
188      */
resetPrenativeThreadPoolExecutorForTesting()189     public static void resetPrenativeThreadPoolExecutorForTesting() {
190         sPrenativeThreadPoolExecutorOverride = null;
191     }
192 
193     /**
194      * @return The current Executor that PrenativeThreadPool tasks should run on.
195      */
getPrenativeThreadPoolExecutor()196     static Executor getPrenativeThreadPoolExecutor() {
197         if (sPrenativeThreadPoolExecutorOverride != null) {
198             return sPrenativeThreadPoolExecutorOverride;
199         }
200         return sPrenativeThreadPoolExecutor;
201     }
202 
203     /**
204      * Called by every TaskRunnerImpl on its creation, attempts to register this TaskRunner as
205      * pre-native, unless the native scheduler has been initialized already, and informs the caller
206      * about the outcome.
207      *
208      * @param taskRunner The TaskRunnerImpl to be registered.
209      * @return If the taskRunner got registered as pre-native.
210      */
registerPreNativeTaskRunner(TaskRunnerImpl taskRunner)211     static boolean registerPreNativeTaskRunner(TaskRunnerImpl taskRunner) {
212         synchronized (sPreNativeTaskRunnerLock) {
213             if (sPreNativeTaskRunners == null) return false;
214             sPreNativeTaskRunners.add(taskRunner);
215             return true;
216         }
217     }
218 
getTaskExecutorForTraits(@askTraits int traits)219     private static TaskExecutor getTaskExecutorForTraits(@TaskTraits int traits) {
220         if (traits >= TaskTraits.UI_TRAITS_START) {
221             // UI thread may be posted to before initialized, so trigger the initialization.
222             if (sUiThreadTaskExecutor == null) ThreadUtils.getUiThreadHandler();
223             return sUiThreadTaskExecutor;
224         }
225         return sThreadPoolTaskExecutor;
226     }
227 
228     @CalledByNative
onNativeSchedulerReady()229     private static void onNativeSchedulerReady() {
230         // Unit tests call this multiple times.
231         if (sNativeInitialized) return;
232         sNativeInitialized = true;
233         List<TaskRunnerImpl> preNativeTaskRunners;
234         synchronized (sPreNativeTaskRunnerLock) {
235             preNativeTaskRunners = sPreNativeTaskRunners;
236             sPreNativeTaskRunners = null;
237         }
238         for (TaskRunnerImpl taskRunner : preNativeTaskRunners) {
239             taskRunner.initNativeTaskRunner();
240         }
241     }
242 
243     /** Drops all queued pre-native tasks. */
flushJobsAndResetForTesting()244     public static void flushJobsAndResetForTesting() throws InterruptedException {
245         ChromeThreadPoolExecutor executor = sPrenativeThreadPoolExecutor;
246         // Potential race condition, but by checking queue size first we overcount if anything.
247         int taskCount = executor.getQueue().size() + executor.getActiveCount();
248         if (taskCount > 0) {
249             executor.shutdownNow();
250             executor.awaitTermination(1, TimeUnit.SECONDS);
251             sPrenativeThreadPoolExecutor = new ChromeThreadPoolExecutor();
252         }
253         synchronized (sPreNativeTaskRunnerLock) {
254             // Clear rather than rely on sTestIterationForTesting in case there are task runners
255             // that are stored in static fields (re-used between tests).
256             if (sPreNativeTaskRunners != null) {
257                 for (TaskRunnerImpl taskRunner : sPreNativeTaskRunners) {
258                     // Clearing would not reliably work in non-robolectric environments since
259                     // a currently running background task could post a new task after the queue
260                     // is cleared. However, Robolectric controls executors to prevent actual
261                     // concurrency, so this approach should work fine.
262                     taskCount += taskRunner.clearTaskQueueForTesting();
263                 }
264             }
265             sTestIterationForTesting++;
266         }
267         resetPrenativeThreadPoolExecutorForTesting();
268         if (taskCount > 0) {
269             Log.w(TAG, "%d background task(s) existed after test finished.", taskCount);
270         }
271     }
272 
273     /** Called once when the UI thread has been initialized */
onUiThreadReady(Handler uiThreadHandler)274     public static void onUiThreadReady(Handler uiThreadHandler) {
275         assert sUiThreadTaskExecutor == null;
276         sUiThreadTaskExecutor = new UiThreadTaskExecutor(uiThreadHandler);
277     }
278 
resetUiThreadForTesting()279     public static void resetUiThreadForTesting() {
280         // UI Thread cannot be reset cleanly after native initialization.
281         assert !sNativeInitialized;
282 
283         sUiThreadTaskExecutor = null;
284     }
285 }
286