Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 package org.mozilla.gecko.util;
8 import java.util.Map;
10 import android.os.Handler;
11 import android.os.MessageQueue;
12 import android.util.Log;
14 public final class ThreadUtils {
15 private static final String LOGTAG = "ThreadUtils";
17 /**
18 * Controls the action taken when a method like
19 * {@link ThreadUtils#assertOnUiThread(AssertBehavior)} detects a problem.
20 */
21 public static enum AssertBehavior {
22 NONE,
23 THROW,
24 }
26 private static volatile Thread sUiThread;
27 private static volatile Thread sBackgroundThread;
29 private static Handler sUiHandler;
31 // Referenced directly from GeckoAppShell in highly performance-sensitive code (The extra
32 // function call of the getter was harming performance. (Bug 897123))
33 // Once Bug 709230 is resolved we should reconsider this as ProGuard should be able to optimise
34 // this out at compile time.
35 public static Handler sGeckoHandler;
36 public static MessageQueue sGeckoQueue;
37 public static Thread sGeckoThread;
39 // Delayed Runnable that resets the Gecko thread priority.
40 private static final Runnable sPriorityResetRunnable = new Runnable() {
41 @Override
42 public void run() {
43 resetGeckoPriority();
44 }
45 };
47 private static boolean sIsGeckoPriorityReduced;
49 @SuppressWarnings("serial")
50 public static class UiThreadBlockedException extends RuntimeException {
51 public UiThreadBlockedException() {
52 super();
53 }
55 public UiThreadBlockedException(String msg) {
56 super(msg);
57 }
59 public UiThreadBlockedException(String msg, Throwable e) {
60 super(msg, e);
61 }
63 public UiThreadBlockedException(Throwable e) {
64 super(e);
65 }
66 }
68 public static void dumpAllStackTraces() {
69 Log.w(LOGTAG, "Dumping ALL the threads!");
70 Map<Thread, StackTraceElement[]> allStacks = Thread.getAllStackTraces();
71 for (Thread t : allStacks.keySet()) {
72 Log.w(LOGTAG, t.toString());
73 for (StackTraceElement ste : allStacks.get(t)) {
74 Log.w(LOGTAG, ste.toString());
75 }
76 Log.w(LOGTAG, "----");
77 }
78 }
80 public static void setUiThread(Thread thread, Handler handler) {
81 sUiThread = thread;
82 sUiHandler = handler;
83 }
85 public static void setBackgroundThread(Thread thread) {
86 sBackgroundThread = thread;
87 }
89 public static Thread getUiThread() {
90 return sUiThread;
91 }
93 public static Handler getUiHandler() {
94 return sUiHandler;
95 }
97 public static void postToUiThread(Runnable runnable) {
98 sUiHandler.post(runnable);
99 }
101 public static Thread getBackgroundThread() {
102 return sBackgroundThread;
103 }
105 public static Handler getBackgroundHandler() {
106 return GeckoBackgroundThread.getHandler();
107 }
109 public static void postToBackgroundThread(Runnable runnable) {
110 GeckoBackgroundThread.post(runnable);
111 }
113 public static void assertOnUiThread(final AssertBehavior assertBehavior) {
114 assertOnThread(getUiThread(), assertBehavior);
115 }
117 public static void assertOnUiThread() {
118 assertOnThread(getUiThread(), AssertBehavior.THROW);
119 }
121 public static void assertOnGeckoThread() {
122 assertOnThread(sGeckoThread, AssertBehavior.THROW);
123 }
125 public static void assertOnBackgroundThread() {
126 assertOnThread(getBackgroundThread(), AssertBehavior.THROW);
127 }
129 public static void assertOnThread(final Thread expectedThread) {
130 assertOnThread(expectedThread, AssertBehavior.THROW);
131 }
133 public static void assertOnThread(final Thread expectedThread, AssertBehavior behavior) {
134 final Thread currentThread = Thread.currentThread();
135 final long currentThreadId = currentThread.getId();
136 final long expectedThreadId = expectedThread.getId();
138 if (currentThreadId == expectedThreadId) {
139 return;
140 }
142 final String message = "Expected thread " +
143 expectedThreadId + " (\"" + expectedThread.getName() +
144 "\"), but running on thread " +
145 currentThreadId + " (\"" + currentThread.getName() + ")";
146 final IllegalThreadStateException e = new IllegalThreadStateException(message);
148 switch (behavior) {
149 case THROW:
150 throw e;
151 default:
152 Log.e(LOGTAG, "Method called on wrong thread!", e);
153 }
154 }
156 public static boolean isOnUiThread() {
157 return isOnThread(getUiThread());
158 }
160 public static boolean isOnBackgroundThread() {
161 if (sBackgroundThread == null) {
162 return false;
163 }
165 return isOnThread(sBackgroundThread);
166 }
168 public static boolean isOnThread(Thread thread) {
169 return (Thread.currentThread().getId() == thread.getId());
170 }
172 /**
173 * Reduces the priority of the Gecko thread, allowing other operations
174 * (such as those related to the UI and database) to take precedence.
175 *
176 * Note that there are no guards in place to prevent multiple calls
177 * to this method from conflicting with each other.
178 *
179 * @param timeout Timeout in ms after which the priority will be reset
180 */
181 public static void reduceGeckoPriority(long timeout) {
182 if (!sIsGeckoPriorityReduced) {
183 sIsGeckoPriorityReduced = true;
184 sGeckoThread.setPriority(Thread.MIN_PRIORITY);
185 getUiHandler().postDelayed(sPriorityResetRunnable, timeout);
186 }
187 }
189 /**
190 * Resets the priority of a thread whose priority has been reduced
191 * by reduceGeckoPriority.
192 */
193 public static void resetGeckoPriority() {
194 if (sIsGeckoPriorityReduced) {
195 sIsGeckoPriorityReduced = false;
196 sGeckoThread.setPriority(Thread.NORM_PRIORITY);
197 getUiHandler().removeCallbacks(sPriorityResetRunnable);
198 }
199 }
200 }