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