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