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