michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.util; michael@0: michael@0: import java.util.Map; michael@0: michael@0: import android.os.Handler; michael@0: import android.os.MessageQueue; michael@0: import android.util.Log; michael@0: michael@0: public final class ThreadUtils { michael@0: private static final String LOGTAG = "ThreadUtils"; michael@0: michael@0: /** michael@0: * Controls the action taken when a method like michael@0: * {@link ThreadUtils#assertOnUiThread(AssertBehavior)} detects a problem. michael@0: */ michael@0: public static enum AssertBehavior { michael@0: NONE, michael@0: THROW, michael@0: } michael@0: michael@0: private static volatile Thread sUiThread; michael@0: private static volatile Thread sBackgroundThread; michael@0: michael@0: private static Handler sUiHandler; michael@0: michael@0: // Referenced directly from GeckoAppShell in highly performance-sensitive code (The extra michael@0: // function call of the getter was harming performance. (Bug 897123)) michael@0: // Once Bug 709230 is resolved we should reconsider this as ProGuard should be able to optimise michael@0: // this out at compile time. michael@0: public static Handler sGeckoHandler; michael@0: public static MessageQueue sGeckoQueue; michael@0: public static Thread sGeckoThread; michael@0: michael@0: // Delayed Runnable that resets the Gecko thread priority. michael@0: private static final Runnable sPriorityResetRunnable = new Runnable() { michael@0: @Override michael@0: public void run() { michael@0: resetGeckoPriority(); michael@0: } michael@0: }; michael@0: michael@0: private static boolean sIsGeckoPriorityReduced; michael@0: michael@0: @SuppressWarnings("serial") michael@0: public static class UiThreadBlockedException extends RuntimeException { michael@0: public UiThreadBlockedException() { michael@0: super(); michael@0: } michael@0: michael@0: public UiThreadBlockedException(String msg) { michael@0: super(msg); michael@0: } michael@0: michael@0: public UiThreadBlockedException(String msg, Throwable e) { michael@0: super(msg, e); michael@0: } michael@0: michael@0: public UiThreadBlockedException(Throwable e) { michael@0: super(e); michael@0: } michael@0: } michael@0: michael@0: public static void dumpAllStackTraces() { michael@0: Log.w(LOGTAG, "Dumping ALL the threads!"); michael@0: Map allStacks = Thread.getAllStackTraces(); michael@0: for (Thread t : allStacks.keySet()) { michael@0: Log.w(LOGTAG, t.toString()); michael@0: for (StackTraceElement ste : allStacks.get(t)) { michael@0: Log.w(LOGTAG, ste.toString()); michael@0: } michael@0: Log.w(LOGTAG, "----"); michael@0: } michael@0: } michael@0: michael@0: public static void setUiThread(Thread thread, Handler handler) { michael@0: sUiThread = thread; michael@0: sUiHandler = handler; michael@0: } michael@0: michael@0: public static void setBackgroundThread(Thread thread) { michael@0: sBackgroundThread = thread; michael@0: } michael@0: michael@0: public static Thread getUiThread() { michael@0: return sUiThread; michael@0: } michael@0: michael@0: public static Handler getUiHandler() { michael@0: return sUiHandler; michael@0: } michael@0: michael@0: public static void postToUiThread(Runnable runnable) { michael@0: sUiHandler.post(runnable); michael@0: } michael@0: michael@0: public static Thread getBackgroundThread() { michael@0: return sBackgroundThread; michael@0: } michael@0: michael@0: public static Handler getBackgroundHandler() { michael@0: return GeckoBackgroundThread.getHandler(); michael@0: } michael@0: michael@0: public static void postToBackgroundThread(Runnable runnable) { michael@0: GeckoBackgroundThread.post(runnable); michael@0: } michael@0: michael@0: public static void assertOnUiThread(final AssertBehavior assertBehavior) { michael@0: assertOnThread(getUiThread(), assertBehavior); michael@0: } michael@0: michael@0: public static void assertOnUiThread() { michael@0: assertOnThread(getUiThread(), AssertBehavior.THROW); michael@0: } michael@0: michael@0: public static void assertOnGeckoThread() { michael@0: assertOnThread(sGeckoThread, AssertBehavior.THROW); michael@0: } michael@0: michael@0: public static void assertOnBackgroundThread() { michael@0: assertOnThread(getBackgroundThread(), AssertBehavior.THROW); michael@0: } michael@0: michael@0: public static void assertOnThread(final Thread expectedThread) { michael@0: assertOnThread(expectedThread, AssertBehavior.THROW); michael@0: } michael@0: michael@0: public static void assertOnThread(final Thread expectedThread, AssertBehavior behavior) { michael@0: final Thread currentThread = Thread.currentThread(); michael@0: final long currentThreadId = currentThread.getId(); michael@0: final long expectedThreadId = expectedThread.getId(); michael@0: michael@0: if (currentThreadId == expectedThreadId) { michael@0: return; michael@0: } michael@0: michael@0: final String message = "Expected thread " + michael@0: expectedThreadId + " (\"" + expectedThread.getName() + michael@0: "\"), but running on thread " + michael@0: currentThreadId + " (\"" + currentThread.getName() + ")"; michael@0: final IllegalThreadStateException e = new IllegalThreadStateException(message); michael@0: michael@0: switch (behavior) { michael@0: case THROW: michael@0: throw e; michael@0: default: michael@0: Log.e(LOGTAG, "Method called on wrong thread!", e); michael@0: } michael@0: } michael@0: michael@0: public static boolean isOnUiThread() { michael@0: return isOnThread(getUiThread()); michael@0: } michael@0: michael@0: public static boolean isOnBackgroundThread() { michael@0: if (sBackgroundThread == null) { michael@0: return false; michael@0: } michael@0: michael@0: return isOnThread(sBackgroundThread); michael@0: } michael@0: michael@0: public static boolean isOnThread(Thread thread) { michael@0: return (Thread.currentThread().getId() == thread.getId()); michael@0: } michael@0: michael@0: /** michael@0: * Reduces the priority of the Gecko thread, allowing other operations michael@0: * (such as those related to the UI and database) to take precedence. michael@0: * michael@0: * Note that there are no guards in place to prevent multiple calls michael@0: * to this method from conflicting with each other. michael@0: * michael@0: * @param timeout Timeout in ms after which the priority will be reset michael@0: */ michael@0: public static void reduceGeckoPriority(long timeout) { michael@0: if (!sIsGeckoPriorityReduced) { michael@0: sIsGeckoPriorityReduced = true; michael@0: sGeckoThread.setPriority(Thread.MIN_PRIORITY); michael@0: getUiHandler().postDelayed(sPriorityResetRunnable, timeout); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Resets the priority of a thread whose priority has been reduced michael@0: * by reduceGeckoPriority. michael@0: */ michael@0: public static void resetGeckoPriority() { michael@0: if (sIsGeckoPriorityReduced) { michael@0: sIsGeckoPriorityReduced = false; michael@0: sGeckoThread.setPriority(Thread.NORM_PRIORITY); michael@0: getUiHandler().removeCallbacks(sPriorityResetRunnable); michael@0: } michael@0: } michael@0: }