1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/util/ThreadUtils.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,200 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.util; 1.10 + 1.11 +import java.util.Map; 1.12 + 1.13 +import android.os.Handler; 1.14 +import android.os.MessageQueue; 1.15 +import android.util.Log; 1.16 + 1.17 +public final class ThreadUtils { 1.18 + private static final String LOGTAG = "ThreadUtils"; 1.19 + 1.20 + /** 1.21 + * Controls the action taken when a method like 1.22 + * {@link ThreadUtils#assertOnUiThread(AssertBehavior)} detects a problem. 1.23 + */ 1.24 + public static enum AssertBehavior { 1.25 + NONE, 1.26 + THROW, 1.27 + } 1.28 + 1.29 + private static volatile Thread sUiThread; 1.30 + private static volatile Thread sBackgroundThread; 1.31 + 1.32 + private static Handler sUiHandler; 1.33 + 1.34 + // Referenced directly from GeckoAppShell in highly performance-sensitive code (The extra 1.35 + // function call of the getter was harming performance. (Bug 897123)) 1.36 + // Once Bug 709230 is resolved we should reconsider this as ProGuard should be able to optimise 1.37 + // this out at compile time. 1.38 + public static Handler sGeckoHandler; 1.39 + public static MessageQueue sGeckoQueue; 1.40 + public static Thread sGeckoThread; 1.41 + 1.42 + // Delayed Runnable that resets the Gecko thread priority. 1.43 + private static final Runnable sPriorityResetRunnable = new Runnable() { 1.44 + @Override 1.45 + public void run() { 1.46 + resetGeckoPriority(); 1.47 + } 1.48 + }; 1.49 + 1.50 + private static boolean sIsGeckoPriorityReduced; 1.51 + 1.52 + @SuppressWarnings("serial") 1.53 + public static class UiThreadBlockedException extends RuntimeException { 1.54 + public UiThreadBlockedException() { 1.55 + super(); 1.56 + } 1.57 + 1.58 + public UiThreadBlockedException(String msg) { 1.59 + super(msg); 1.60 + } 1.61 + 1.62 + public UiThreadBlockedException(String msg, Throwable e) { 1.63 + super(msg, e); 1.64 + } 1.65 + 1.66 + public UiThreadBlockedException(Throwable e) { 1.67 + super(e); 1.68 + } 1.69 + } 1.70 + 1.71 + public static void dumpAllStackTraces() { 1.72 + Log.w(LOGTAG, "Dumping ALL the threads!"); 1.73 + Map<Thread, StackTraceElement[]> allStacks = Thread.getAllStackTraces(); 1.74 + for (Thread t : allStacks.keySet()) { 1.75 + Log.w(LOGTAG, t.toString()); 1.76 + for (StackTraceElement ste : allStacks.get(t)) { 1.77 + Log.w(LOGTAG, ste.toString()); 1.78 + } 1.79 + Log.w(LOGTAG, "----"); 1.80 + } 1.81 + } 1.82 + 1.83 + public static void setUiThread(Thread thread, Handler handler) { 1.84 + sUiThread = thread; 1.85 + sUiHandler = handler; 1.86 + } 1.87 + 1.88 + public static void setBackgroundThread(Thread thread) { 1.89 + sBackgroundThread = thread; 1.90 + } 1.91 + 1.92 + public static Thread getUiThread() { 1.93 + return sUiThread; 1.94 + } 1.95 + 1.96 + public static Handler getUiHandler() { 1.97 + return sUiHandler; 1.98 + } 1.99 + 1.100 + public static void postToUiThread(Runnable runnable) { 1.101 + sUiHandler.post(runnable); 1.102 + } 1.103 + 1.104 + public static Thread getBackgroundThread() { 1.105 + return sBackgroundThread; 1.106 + } 1.107 + 1.108 + public static Handler getBackgroundHandler() { 1.109 + return GeckoBackgroundThread.getHandler(); 1.110 + } 1.111 + 1.112 + public static void postToBackgroundThread(Runnable runnable) { 1.113 + GeckoBackgroundThread.post(runnable); 1.114 + } 1.115 + 1.116 + public static void assertOnUiThread(final AssertBehavior assertBehavior) { 1.117 + assertOnThread(getUiThread(), assertBehavior); 1.118 + } 1.119 + 1.120 + public static void assertOnUiThread() { 1.121 + assertOnThread(getUiThread(), AssertBehavior.THROW); 1.122 + } 1.123 + 1.124 + public static void assertOnGeckoThread() { 1.125 + assertOnThread(sGeckoThread, AssertBehavior.THROW); 1.126 + } 1.127 + 1.128 + public static void assertOnBackgroundThread() { 1.129 + assertOnThread(getBackgroundThread(), AssertBehavior.THROW); 1.130 + } 1.131 + 1.132 + public static void assertOnThread(final Thread expectedThread) { 1.133 + assertOnThread(expectedThread, AssertBehavior.THROW); 1.134 + } 1.135 + 1.136 + public static void assertOnThread(final Thread expectedThread, AssertBehavior behavior) { 1.137 + final Thread currentThread = Thread.currentThread(); 1.138 + final long currentThreadId = currentThread.getId(); 1.139 + final long expectedThreadId = expectedThread.getId(); 1.140 + 1.141 + if (currentThreadId == expectedThreadId) { 1.142 + return; 1.143 + } 1.144 + 1.145 + final String message = "Expected thread " + 1.146 + expectedThreadId + " (\"" + expectedThread.getName() + 1.147 + "\"), but running on thread " + 1.148 + currentThreadId + " (\"" + currentThread.getName() + ")"; 1.149 + final IllegalThreadStateException e = new IllegalThreadStateException(message); 1.150 + 1.151 + switch (behavior) { 1.152 + case THROW: 1.153 + throw e; 1.154 + default: 1.155 + Log.e(LOGTAG, "Method called on wrong thread!", e); 1.156 + } 1.157 + } 1.158 + 1.159 + public static boolean isOnUiThread() { 1.160 + return isOnThread(getUiThread()); 1.161 + } 1.162 + 1.163 + public static boolean isOnBackgroundThread() { 1.164 + if (sBackgroundThread == null) { 1.165 + return false; 1.166 + } 1.167 + 1.168 + return isOnThread(sBackgroundThread); 1.169 + } 1.170 + 1.171 + public static boolean isOnThread(Thread thread) { 1.172 + return (Thread.currentThread().getId() == thread.getId()); 1.173 + } 1.174 + 1.175 + /** 1.176 + * Reduces the priority of the Gecko thread, allowing other operations 1.177 + * (such as those related to the UI and database) to take precedence. 1.178 + * 1.179 + * Note that there are no guards in place to prevent multiple calls 1.180 + * to this method from conflicting with each other. 1.181 + * 1.182 + * @param timeout Timeout in ms after which the priority will be reset 1.183 + */ 1.184 + public static void reduceGeckoPriority(long timeout) { 1.185 + if (!sIsGeckoPriorityReduced) { 1.186 + sIsGeckoPriorityReduced = true; 1.187 + sGeckoThread.setPriority(Thread.MIN_PRIORITY); 1.188 + getUiHandler().postDelayed(sPriorityResetRunnable, timeout); 1.189 + } 1.190 + } 1.191 + 1.192 + /** 1.193 + * Resets the priority of a thread whose priority has been reduced 1.194 + * by reduceGeckoPriority. 1.195 + */ 1.196 + public static void resetGeckoPriority() { 1.197 + if (sIsGeckoPriorityReduced) { 1.198 + sIsGeckoPriorityReduced = false; 1.199 + sGeckoThread.setPriority(Thread.NORM_PRIORITY); 1.200 + getUiHandler().removeCallbacks(sPriorityResetRunnable); 1.201 + } 1.202 + } 1.203 +}