• 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 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