1 // Copyright 2012 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; 6 7 import android.os.Handler; 8 import android.os.Looper; 9 import android.os.Process; 10 11 import androidx.annotation.VisibleForTesting; 12 13 import org.chromium.base.annotations.CalledByNative; 14 import org.chromium.base.task.PostTask; 15 import org.chromium.base.task.TaskTraits; 16 17 import java.util.concurrent.Callable; 18 import java.util.concurrent.ExecutionException; 19 import java.util.concurrent.FutureTask; 20 21 /** 22 * Helper methods to deal with threading related tasks. 23 */ 24 public class ThreadUtils { 25 26 private static final Object sLock = new Object(); 27 28 private static volatile boolean sWillOverride; 29 30 private static volatile Handler sUiThreadHandler; 31 32 private static boolean sThreadAssertsDisabled; 33 34 /** 35 * A helper object to ensure that interactions with a particular object only happens on a 36 * particular thread. 37 * 38 * Example: 39 * <pre> 40 * {@code 41 * class Foo { 42 * // Valid thread is set during construction here. 43 * private final ThreadChecker mThreadChecker = new ThreadChecker(); 44 * 45 * public void doFoo() { 46 * mThreadChecker.assertOnValidThread(); 47 * } 48 * } 49 * } 50 * </pre> 51 * 52 * Another way to use this class is to also use the baked in support for destruction: 53 * <pre> 54 * {@code 55 * class Foo { 56 * // Valid thread is set during construction here. 57 * private final ThreadChecker mThreadChecker = new ThreadChecker(); 58 * 59 * public void doFoo() { 60 * mThreadChecker.assertOnValidThreadAndState(); 61 * } 62 * } 63 * } 64 * </pre> 65 */ 66 public static class ThreadChecker { 67 private final long mThreadId = Process.myTid(); 68 69 /** 70 * Asserts that the current thread is the same as the one the ThreadChecker was constructed 71 * on. 72 */ assertOnValidThread()73 public void assertOnValidThread() { 74 assert sThreadAssertsDisabled 75 || mThreadId == Process.myTid() : "Must only be used on a single thread."; 76 } 77 } 78 setWillOverrideUiThread()79 public static void setWillOverrideUiThread() { 80 sWillOverride = true; 81 assert sUiThreadHandler == null; 82 } 83 84 @VisibleForTesting clearUiThreadForTesting()85 public static void clearUiThreadForTesting() { 86 sWillOverride = false; 87 PostTask.resetUiThreadForTesting(); // IN-TEST 88 sUiThreadHandler = null; 89 } 90 setUiThread(Looper looper)91 public static void setUiThread(Looper looper) { 92 assert looper != null; 93 synchronized (sLock) { 94 if (sUiThreadHandler == null) { 95 Handler uiThreadHandler = new Handler(looper); 96 // Set up the UI Thread TaskExecutor before signaling readiness. 97 PostTask.onUiThreadReady(uiThreadHandler); 98 // volatile write signals readiness since other threads read it without acquiring 99 // sLock. 100 sUiThreadHandler = uiThreadHandler; 101 // Must come after PostTask is initialized since it uses PostTask. 102 TraceEvent.onUiThreadReady(); 103 } else if (sUiThreadHandler.getLooper() != looper) { 104 throw new RuntimeException("UI thread looper is already set to " 105 + sUiThreadHandler.getLooper() + " (Main thread looper is " 106 + Looper.getMainLooper() + "), cannot set to new looper " + looper); 107 } 108 } 109 } 110 getUiThreadHandler()111 public static Handler getUiThreadHandler() { 112 if (sUiThreadHandler != null) return sUiThreadHandler; 113 114 if (sWillOverride) { 115 throw new RuntimeException("Did not yet override the UI thread"); 116 } 117 setUiThread(Looper.getMainLooper()); 118 return sUiThreadHandler; 119 } 120 121 /** 122 * Run the supplied Runnable on the main thread. The method will block until the Runnable 123 * completes. 124 * 125 * Note that non-test usage of this function is heavily discouraged. For non-tests, use 126 * callbacks rather than blocking threads. 127 * 128 * @param r The Runnable to run. 129 */ runOnUiThreadBlocking(final Runnable r)130 public static void runOnUiThreadBlocking(final Runnable r) { 131 PostTask.runSynchronously(TaskTraits.UI_DEFAULT, r); 132 } 133 134 /** 135 * Run the supplied Callable on the main thread, wrapping any exceptions in a RuntimeException. 136 * The method will block until the Callable completes. 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 c The Callable to run 142 * @return The result of the callable 143 */ runOnUiThreadBlockingNoException(Callable<T> c)144 public static <T> T runOnUiThreadBlockingNoException(Callable<T> c) { 145 try { 146 return runOnUiThreadBlocking(c); 147 } catch (ExecutionException e) { 148 throw new RuntimeException("Error occurred waiting for callable", e); 149 } 150 } 151 152 /** 153 * Run the supplied Callable on the main thread, The method will block until the Callable 154 * completes. 155 * 156 * Note that non-test usage of this function is heavily discouraged. For non-tests, use 157 * callbacks rather than blocking threads. 158 * 159 * @param c The Callable to run 160 * @return The result of the callable 161 * @throws ExecutionException c's exception 162 */ runOnUiThreadBlocking(Callable<T> c)163 public static <T> T runOnUiThreadBlocking(Callable<T> c) throws ExecutionException { 164 return PostTask.runSynchronously(TaskTraits.UI_DEFAULT, c); 165 } 166 167 /** 168 * Run the supplied FutureTask on the main thread. The method will block only if the current 169 * thread is the main thread. 170 * 171 * @param task The FutureTask to run 172 * @return The queried task (to aid inline construction) 173 */ runOnUiThread(FutureTask<T> task)174 public static <T> FutureTask<T> runOnUiThread(FutureTask<T> task) { 175 PostTask.runOrPostTask(TaskTraits.UI_DEFAULT, task); 176 return task; 177 } 178 179 /** 180 * Run the supplied Callable on the main thread. The method will block only if the current 181 * thread is the main thread. 182 * 183 * @param c The Callable to run 184 * @return A FutureTask wrapping the callable to retrieve results 185 */ runOnUiThread(Callable<T> c)186 public static <T> FutureTask<T> runOnUiThread(Callable<T> c) { 187 return runOnUiThread(new FutureTask<T>(c)); 188 } 189 190 /** 191 * Run the supplied Runnable on the main thread. The method will block only if the current 192 * thread is the main thread. 193 * 194 * @param r The Runnable to run 195 */ runOnUiThread(Runnable r)196 public static void runOnUiThread(Runnable r) { 197 PostTask.runOrPostTask(TaskTraits.UI_DEFAULT, r); 198 } 199 200 /** 201 * Post the supplied FutureTask to run on the main thread. The method will not block, even if 202 * called on the UI thread. 203 * 204 * @param task The FutureTask to run 205 * @return The queried task (to aid inline construction) 206 */ postOnUiThread(FutureTask<T> task)207 public static <T> FutureTask<T> postOnUiThread(FutureTask<T> task) { 208 PostTask.postTask(TaskTraits.UI_DEFAULT, task); 209 return task; 210 } 211 212 /** 213 * Post the supplied Runnable to run on the main thread. The method will not block, even if 214 * called on the UI thread. 215 * 216 * @param r The Runnable to run 217 */ postOnUiThread(Runnable r)218 public static void postOnUiThread(Runnable r) { 219 PostTask.postTask(TaskTraits.UI_DEFAULT, r); 220 } 221 222 /** 223 * Post the supplied Runnable to run on the main thread after the given amount of time. The 224 * method will not block, even if called on the UI thread. 225 * 226 * @param r The Runnable to run 227 * @param delayMillis The delay in milliseconds until the Runnable will be run 228 */ postOnUiThreadDelayed(Runnable r, long delayMillis)229 public static void postOnUiThreadDelayed(Runnable r, long delayMillis) { 230 PostTask.postDelayedTask(TaskTraits.UI_DEFAULT, r, delayMillis); 231 } 232 233 /** 234 * Throw an exception (when DCHECKs are enabled) if currently not running on the UI thread. 235 * 236 * Can be disabled by setThreadAssertsDisabledForTesting(true). 237 */ assertOnUiThread()238 public static void assertOnUiThread() { 239 if (sThreadAssertsDisabled) return; 240 241 assert runningOnUiThread() : "Must be called on the UI thread."; 242 } 243 244 /** 245 * Throw an exception (regardless of build) if currently not running on the UI thread. 246 * 247 * Can be disabled by setThreadAssertsEnabledForTesting(false). 248 * 249 * @see #assertOnUiThread() 250 */ checkUiThread()251 public static void checkUiThread() { 252 if (!sThreadAssertsDisabled && !runningOnUiThread()) { 253 throw new IllegalStateException("Must be called on the UI thread."); 254 } 255 } 256 257 /** 258 * Throw an exception (when DCHECKs are enabled) if currently running on the UI thread. 259 * 260 * Can be disabled by setThreadAssertsDisabledForTesting(true). 261 */ assertOnBackgroundThread()262 public static void assertOnBackgroundThread() { 263 if (sThreadAssertsDisabled) return; 264 265 assert !runningOnUiThread() : "Must be called on a thread other than UI."; 266 } 267 268 /** 269 * Disables thread asserts. 270 * 271 * Can be used by tests where code that normally runs multi-threaded is going to run 272 * single-threaded for the test (otherwise asserts that are valid in production would fail in 273 * those tests). 274 */ setThreadAssertsDisabledForTesting(boolean disabled)275 public static void setThreadAssertsDisabledForTesting(boolean disabled) { 276 sThreadAssertsDisabled = disabled; 277 } 278 279 /** 280 * @return true iff the current thread is the main (UI) thread. 281 */ runningOnUiThread()282 public static boolean runningOnUiThread() { 283 return getUiThreadHandler().getLooper() == Looper.myLooper(); 284 } 285 getUiThreadLooper()286 public static Looper getUiThreadLooper() { 287 return getUiThreadHandler().getLooper(); 288 } 289 290 /** 291 * Set thread priority to audio. 292 */ 293 @CalledByNative setThreadPriorityAudio(int tid)294 public static void setThreadPriorityAudio(int tid) { 295 Process.setThreadPriority(tid, Process.THREAD_PRIORITY_AUDIO); 296 } 297 298 /** 299 * Checks whether Thread priority is THREAD_PRIORITY_AUDIO or not. 300 * @param tid Thread id. 301 * @return true for THREAD_PRIORITY_AUDIO and false otherwise. 302 */ 303 @CalledByNative isThreadPriorityAudio(int tid)304 private static boolean isThreadPriorityAudio(int tid) { 305 return Process.getThreadPriority(tid) == Process.THREAD_PRIORITY_AUDIO; 306 } 307 } 308