• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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