|
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/. */ |
|
5 |
|
6 package org.mozilla.gecko.util; |
|
7 |
|
8 import java.util.Map; |
|
9 |
|
10 import android.os.Handler; |
|
11 import android.os.MessageQueue; |
|
12 import android.util.Log; |
|
13 |
|
14 public final class ThreadUtils { |
|
15 private static final String LOGTAG = "ThreadUtils"; |
|
16 |
|
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 } |
|
25 |
|
26 private static volatile Thread sUiThread; |
|
27 private static volatile Thread sBackgroundThread; |
|
28 |
|
29 private static Handler sUiHandler; |
|
30 |
|
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; |
|
38 |
|
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 }; |
|
46 |
|
47 private static boolean sIsGeckoPriorityReduced; |
|
48 |
|
49 @SuppressWarnings("serial") |
|
50 public static class UiThreadBlockedException extends RuntimeException { |
|
51 public UiThreadBlockedException() { |
|
52 super(); |
|
53 } |
|
54 |
|
55 public UiThreadBlockedException(String msg) { |
|
56 super(msg); |
|
57 } |
|
58 |
|
59 public UiThreadBlockedException(String msg, Throwable e) { |
|
60 super(msg, e); |
|
61 } |
|
62 |
|
63 public UiThreadBlockedException(Throwable e) { |
|
64 super(e); |
|
65 } |
|
66 } |
|
67 |
|
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 } |
|
79 |
|
80 public static void setUiThread(Thread thread, Handler handler) { |
|
81 sUiThread = thread; |
|
82 sUiHandler = handler; |
|
83 } |
|
84 |
|
85 public static void setBackgroundThread(Thread thread) { |
|
86 sBackgroundThread = thread; |
|
87 } |
|
88 |
|
89 public static Thread getUiThread() { |
|
90 return sUiThread; |
|
91 } |
|
92 |
|
93 public static Handler getUiHandler() { |
|
94 return sUiHandler; |
|
95 } |
|
96 |
|
97 public static void postToUiThread(Runnable runnable) { |
|
98 sUiHandler.post(runnable); |
|
99 } |
|
100 |
|
101 public static Thread getBackgroundThread() { |
|
102 return sBackgroundThread; |
|
103 } |
|
104 |
|
105 public static Handler getBackgroundHandler() { |
|
106 return GeckoBackgroundThread.getHandler(); |
|
107 } |
|
108 |
|
109 public static void postToBackgroundThread(Runnable runnable) { |
|
110 GeckoBackgroundThread.post(runnable); |
|
111 } |
|
112 |
|
113 public static void assertOnUiThread(final AssertBehavior assertBehavior) { |
|
114 assertOnThread(getUiThread(), assertBehavior); |
|
115 } |
|
116 |
|
117 public static void assertOnUiThread() { |
|
118 assertOnThread(getUiThread(), AssertBehavior.THROW); |
|
119 } |
|
120 |
|
121 public static void assertOnGeckoThread() { |
|
122 assertOnThread(sGeckoThread, AssertBehavior.THROW); |
|
123 } |
|
124 |
|
125 public static void assertOnBackgroundThread() { |
|
126 assertOnThread(getBackgroundThread(), AssertBehavior.THROW); |
|
127 } |
|
128 |
|
129 public static void assertOnThread(final Thread expectedThread) { |
|
130 assertOnThread(expectedThread, AssertBehavior.THROW); |
|
131 } |
|
132 |
|
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(); |
|
137 |
|
138 if (currentThreadId == expectedThreadId) { |
|
139 return; |
|
140 } |
|
141 |
|
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); |
|
147 |
|
148 switch (behavior) { |
|
149 case THROW: |
|
150 throw e; |
|
151 default: |
|
152 Log.e(LOGTAG, "Method called on wrong thread!", e); |
|
153 } |
|
154 } |
|
155 |
|
156 public static boolean isOnUiThread() { |
|
157 return isOnThread(getUiThread()); |
|
158 } |
|
159 |
|
160 public static boolean isOnBackgroundThread() { |
|
161 if (sBackgroundThread == null) { |
|
162 return false; |
|
163 } |
|
164 |
|
165 return isOnThread(sBackgroundThread); |
|
166 } |
|
167 |
|
168 public static boolean isOnThread(Thread thread) { |
|
169 return (Thread.currentThread().getId() == thread.getId()); |
|
170 } |
|
171 |
|
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 } |
|
188 |
|
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 } |