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