1 // Copyright 2012 The Chromium Authors. All rights reserved. 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.chromium.base.annotations.CalledByNative; 12 13 import java.util.concurrent.Callable; 14 import java.util.concurrent.ExecutionException; 15 import java.util.concurrent.FutureTask; 16 17 /** 18 * Helper methods to deal with threading related tasks. 19 */ 20 public class ThreadUtils { 21 22 private static final Object sLock = new Object(); 23 24 private static boolean sWillOverride; 25 26 private static Handler sUiThreadHandler; 27 28 private static boolean sThreadAssertsDisabled; 29 setWillOverrideUiThread()30 public static void setWillOverrideUiThread() { 31 synchronized (sLock) { 32 sWillOverride = true; 33 } 34 } 35 setUiThread(Looper looper)36 public static void setUiThread(Looper looper) { 37 synchronized (sLock) { 38 if (looper == null) { 39 // Used to reset the looper after tests. 40 sUiThreadHandler = null; 41 return; 42 } 43 if (sUiThreadHandler != null && sUiThreadHandler.getLooper() != looper) { 44 throw new RuntimeException("UI thread looper is already set to " 45 + sUiThreadHandler.getLooper() + " (Main thread looper is " 46 + Looper.getMainLooper() + "), cannot set to new looper " + looper); 47 } else { 48 sUiThreadHandler = new Handler(looper); 49 } 50 } 51 } 52 getUiThreadHandler()53 public static Handler getUiThreadHandler() { 54 synchronized (sLock) { 55 if (sUiThreadHandler == null) { 56 if (sWillOverride) { 57 throw new RuntimeException("Did not yet override the UI thread"); 58 } 59 sUiThreadHandler = new Handler(Looper.getMainLooper()); 60 } 61 return sUiThreadHandler; 62 } 63 } 64 65 /** 66 * Run the supplied Runnable on the main thread. The method will block until the Runnable 67 * completes. 68 * 69 * @param r The Runnable to run. 70 */ runOnUiThreadBlocking(final Runnable r)71 public static void runOnUiThreadBlocking(final Runnable r) { 72 if (runningOnUiThread()) { 73 r.run(); 74 } else { 75 FutureTask<Void> task = new FutureTask<Void>(r, null); 76 postOnUiThread(task); 77 try { 78 task.get(); 79 } catch (Exception e) { 80 throw new RuntimeException("Exception occurred while waiting for runnable", e); 81 } 82 } 83 } 84 85 /** 86 * Run the supplied Callable on the main thread, wrapping any exceptions in a RuntimeException. 87 * The method will block until the Callable completes. 88 * 89 * @param c The Callable to run 90 * @return The result of the callable 91 */ 92 @VisibleForTesting runOnUiThreadBlockingNoException(Callable<T> c)93 public static <T> T runOnUiThreadBlockingNoException(Callable<T> c) { 94 try { 95 return runOnUiThreadBlocking(c); 96 } catch (ExecutionException e) { 97 throw new RuntimeException("Error occurred waiting for callable", e); 98 } 99 } 100 101 /** 102 * Run the supplied Callable on the main thread, The method will block until the Callable 103 * completes. 104 * 105 * @param c The Callable to run 106 * @return The result of the callable 107 * @throws ExecutionException c's exception 108 */ runOnUiThreadBlocking(Callable<T> c)109 public static <T> T runOnUiThreadBlocking(Callable<T> c) throws ExecutionException { 110 FutureTask<T> task = new FutureTask<T>(c); 111 runOnUiThread(task); 112 try { 113 return task.get(); 114 } catch (InterruptedException e) { 115 throw new RuntimeException("Interrupted waiting for callable", e); 116 } 117 } 118 119 /** 120 * Run the supplied FutureTask on the main thread. The method will block only if the current 121 * thread is the main thread. 122 * 123 * @param task The FutureTask to run 124 * @return The queried task (to aid inline construction) 125 */ runOnUiThread(FutureTask<T> task)126 public static <T> FutureTask<T> runOnUiThread(FutureTask<T> task) { 127 if (runningOnUiThread()) { 128 task.run(); 129 } else { 130 postOnUiThread(task); 131 } 132 return task; 133 } 134 135 /** 136 * Run the supplied Callable on the main thread. The method will block only if the current 137 * thread is the main thread. 138 * 139 * @param c The Callable to run 140 * @return A FutureTask wrapping the callable to retrieve results 141 */ runOnUiThread(Callable<T> c)142 public static <T> FutureTask<T> runOnUiThread(Callable<T> c) { 143 return runOnUiThread(new FutureTask<T>(c)); 144 } 145 146 /** 147 * Run the supplied Runnable on the main thread. The method will block only if the current 148 * thread is the main thread. 149 * 150 * @param r The Runnable to run 151 */ runOnUiThread(Runnable r)152 public static void runOnUiThread(Runnable r) { 153 if (runningOnUiThread()) { 154 r.run(); 155 } else { 156 getUiThreadHandler().post(r); 157 } 158 } 159 160 /** 161 * Post the supplied FutureTask to run on the main thread. The method will not block, even if 162 * called on the UI thread. 163 * 164 * @param task The FutureTask to run 165 * @return The queried task (to aid inline construction) 166 */ postOnUiThread(FutureTask<T> task)167 public static <T> FutureTask<T> postOnUiThread(FutureTask<T> task) { 168 getUiThreadHandler().post(task); 169 return task; 170 } 171 172 /** 173 * Post the supplied Runnable to run on the main thread. The method will not block, even if 174 * called on the UI thread. 175 * 176 * @param task The Runnable to run 177 */ postOnUiThread(Runnable task)178 public static void postOnUiThread(Runnable task) { 179 getUiThreadHandler().post(task); 180 } 181 182 /** 183 * Post the supplied Runnable to run on the main thread after the given amount of time. The 184 * method will not block, even if called on the UI thread. 185 * 186 * @param task The Runnable to run 187 * @param delayMillis The delay in milliseconds until the Runnable will be run 188 */ 189 @VisibleForTesting postOnUiThreadDelayed(Runnable task, long delayMillis)190 public static void postOnUiThreadDelayed(Runnable task, long delayMillis) { 191 getUiThreadHandler().postDelayed(task, delayMillis); 192 } 193 194 /** 195 * Throw an exception (when DCHECKs are enabled) if currently not running on the UI thread. 196 * 197 * Can be disabled by setThreadAssertsDisabledForTesting(true). 198 */ assertOnUiThread()199 public static void assertOnUiThread() { 200 if (sThreadAssertsDisabled) return; 201 202 assert runningOnUiThread() : "Must be called on the UI thread."; 203 } 204 205 /** 206 * Throw an exception (regardless of build) if currently not running on the UI thread. 207 * 208 * Can be disabled by setThreadAssertsEnabledForTesting(false). 209 * 210 * @see #assertOnUiThread() 211 */ checkUiThread()212 public static void checkUiThread() { 213 if (!sThreadAssertsDisabled && !runningOnUiThread()) { 214 throw new IllegalStateException("Must be called on the UI thread."); 215 } 216 } 217 218 /** 219 * Throw an exception (when DCHECKs are enabled) if currently running on the UI thread. 220 * 221 * Can be disabled by setThreadAssertsDisabledForTesting(true). 222 */ assertOnBackgroundThread()223 public static void assertOnBackgroundThread() { 224 if (sThreadAssertsDisabled) return; 225 226 assert !runningOnUiThread() : "Must be called on a thread other than UI."; 227 } 228 229 /** 230 * Disables thread asserts. 231 * 232 * Can be used by tests where code that normally runs multi-threaded is going to run 233 * single-threaded for the test (otherwise asserts that are valid in production would fail in 234 * those tests). 235 */ setThreadAssertsDisabledForTesting(boolean disabled)236 public static void setThreadAssertsDisabledForTesting(boolean disabled) { 237 sThreadAssertsDisabled = disabled; 238 } 239 240 /** 241 * @return true iff the current thread is the main (UI) thread. 242 */ runningOnUiThread()243 public static boolean runningOnUiThread() { 244 return getUiThreadHandler().getLooper() == Looper.myLooper(); 245 } 246 getUiThreadLooper()247 public static Looper getUiThreadLooper() { 248 return getUiThreadHandler().getLooper(); 249 } 250 251 /** 252 * Set thread priority to audio. 253 */ 254 @CalledByNative setThreadPriorityAudio(int tid)255 public static void setThreadPriorityAudio(int tid) { 256 Process.setThreadPriority(tid, Process.THREAD_PRIORITY_AUDIO); 257 } 258 259 /** 260 * Checks whether Thread priority is THREAD_PRIORITY_AUDIO or not. 261 * @param tid Thread id. 262 * @return true for THREAD_PRIORITY_AUDIO and false otherwise. 263 */ 264 @CalledByNative isThreadPriorityAudio(int tid)265 private static boolean isThreadPriorityAudio(int tid) { 266 return Process.getThreadPriority(tid) == Process.THREAD_PRIORITY_AUDIO; 267 } 268 } 269