From 82468ce27bca8e36b5bd30e26873bfa5f19a8f85 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 15 Mar 2013 11:52:52 +0100 Subject: [PATCH] Bug 802130 - Move assertOnThread functions to a new ThreadUtils class. r=mfinkle --- mobile/android/base/GeckoApp.java | 26 ++------ mobile/android/base/GeckoEditable.java | 15 ++--- mobile/android/base/GeckoInputConnection.java | 17 +++--- mobile/android/base/Makefile.in | 1 + mobile/android/base/PromptService.java | 9 +-- mobile/android/base/gfx/GLController.java | 8 +-- .../base/util/GeckoBackgroundThread.java | 11 ++-- mobile/android/base/util/ThreadUtils.java | 61 +++++++++++++++++++ 8 files changed, 98 insertions(+), 50 deletions(-) create mode 100644 mobile/android/base/util/ThreadUtils.java diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 0273ab593b40..e54edf6c0323 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -18,6 +18,7 @@ import org.mozilla.gecko.updater.UpdateServiceHelper; import org.mozilla.gecko.util.GeckoBackgroundThread; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.GeckoEventResponder; +import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.UiAsyncTask; import org.json.JSONArray; @@ -1372,6 +1373,8 @@ abstract public class GeckoApp ((GeckoApplication)getApplication()).initialize(); mAppContext = this; + ThreadUtils.setUiThread(Thread.currentThread()); + Tabs.getInstance().attachToActivity(this); Favicons.getInstance().attachToContext(this); @@ -1632,6 +1635,7 @@ abstract public class GeckoApp if (!mIsRestoringActivity) { sGeckoThread = new GeckoThread(intent, passedUri); + ThreadUtils.setGeckoThread(sGeckoThread); } if (!ACTION_DEBUG.equals(action) && GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.Launched)) { @@ -2638,26 +2642,4 @@ abstract public class GeckoApp } return false; } - - public static void assertOnUiThread() { - Thread uiThread = mAppContext.getMainLooper().getThread(); - assertOnThread(uiThread); - } - - public static void assertOnGeckoThread() { - assertOnThread(sGeckoThread); - } - - public static void assertOnThread(Thread expectedThread) { - Thread currentThread = Thread.currentThread(); - long currentThreadId = currentThread.getId(); - long expectedThreadId = expectedThread.getId(); - - if (currentThreadId != expectedThreadId) { - throw new IllegalThreadStateException("Expected thread " + expectedThreadId + " (\"" - + expectedThread.getName() - + "\"), but running on thread " + currentThreadId - + " (\"" + currentThread.getName() + ")"); - } - } } diff --git a/mobile/android/base/GeckoEditable.java b/mobile/android/base/GeckoEditable.java index c9dbaacea60b..2bb6b8e8e10f 100644 --- a/mobile/android/base/GeckoEditable.java +++ b/mobile/android/base/GeckoEditable.java @@ -7,6 +7,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.gfx.InputConnectionHandler; import org.mozilla.gecko.gfx.LayerView; +import org.mozilla.gecko.util.ThreadUtils; import android.os.Build; import android.os.Handler; @@ -233,7 +234,7 @@ final class GeckoEditable void poll() { if (DEBUG) { - GeckoApp.assertOnGeckoThread(); + ThreadUtils.assertOnGeckoThread(); } if (mActions.isEmpty()) { throw new IllegalStateException("empty actions queue"); @@ -251,7 +252,7 @@ final class GeckoEditable Action peek() { if (DEBUG) { - GeckoApp.assertOnGeckoThread(); + ThreadUtils.assertOnGeckoThread(); } if (mActions.isEmpty()) { throw new IllegalStateException("empty actions queue"); @@ -304,7 +305,7 @@ final class GeckoEditable } private void assertOnIcThread() { - GeckoApp.assertOnThread(mIcRunHandler.getLooper().getThread()); + ThreadUtils.assertOnThread(mIcRunHandler.getLooper().getThread()); } private void geckoPostToIc(Runnable runnable) { @@ -598,7 +599,7 @@ final class GeckoEditable private void geckoActionReply() { if (DEBUG) { // GeckoEditableListener methods should all be called from the Gecko thread - GeckoApp.assertOnGeckoThread(); + ThreadUtils.assertOnGeckoThread(); } final Action action = mActionQueue.peek(); @@ -650,7 +651,7 @@ final class GeckoEditable public void notifyIME(final int type, final int state) { if (DEBUG) { // GeckoEditableListener methods should all be called from the Gecko thread - GeckoApp.assertOnGeckoThread(); + ThreadUtils.assertOnGeckoThread(); // NOTIFY_IME_REPLY_EVENT is logged separately, inside geckoActionReply() if (type != NOTIFY_IME_REPLY_EVENT) { Log.d(LOGTAG, "notifyIME(" + @@ -727,7 +728,7 @@ final class GeckoEditable public void onSelectionChange(final int start, final int end) { if (DEBUG) { // GeckoEditableListener methods should all be called from the Gecko thread - GeckoApp.assertOnGeckoThread(); + ThreadUtils.assertOnGeckoThread(); Log.d(LOGTAG, "onSelectionChange(" + start + ", " + end + ")"); } if (start < 0 || start > mText.length() || end < 0 || end > mText.length()) { @@ -777,7 +778,7 @@ final class GeckoEditable final int unboundedOldEnd, final int unboundedNewEnd) { if (DEBUG) { // GeckoEditableListener methods should all be called from the Gecko thread - GeckoApp.assertOnGeckoThread(); + ThreadUtils.assertOnGeckoThread(); Log.d(LOGTAG, "onTextChange(\"" + text + "\", " + start + ", " + unboundedOldEnd + ", " + unboundedNewEnd + ")"); } diff --git a/mobile/android/base/GeckoInputConnection.java b/mobile/android/base/GeckoInputConnection.java index d2a001eca0fb..06407d1630ac 100644 --- a/mobile/android/base/GeckoInputConnection.java +++ b/mobile/android/base/GeckoInputConnection.java @@ -6,6 +6,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.gfx.InputConnectionHandler; +import org.mozilla.gecko.util.ThreadUtils; import android.R; import android.content.Context; @@ -45,14 +46,14 @@ class GeckoInputConnection private static Handler sBackgroundHandler; - private class ThreadUtils { + private class InputThreadUtils { private Editable mUiEditable; private Object mUiEditableReturn; private Exception mUiEditableException; private final SynchronousQueue mIcRunnableSync; private final Runnable mIcSignalRunnable; - public ThreadUtils() { + public InputThreadUtils() { mIcRunnableSync = new SynchronousQueue(); mIcSignalRunnable = new Runnable() { @Override public void run() { @@ -62,7 +63,7 @@ class GeckoInputConnection private void runOnIcThread(Handler icHandler, final Runnable runnable) { if (DEBUG) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); Log.d(LOGTAG, "runOnIcThread() on thread " + icHandler.getLooper().getThread().getName()); } @@ -92,7 +93,7 @@ class GeckoInputConnection public void endWaitForUiThread() { if (DEBUG) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); Log.d(LOGTAG, "endWaitForUiThread()"); } try { @@ -103,7 +104,7 @@ class GeckoInputConnection public void waitForUiThread(Handler icHandler) { if (DEBUG) { - GeckoApp.assertOnThread(icHandler.getLooper().getThread()); + ThreadUtils.assertOnThread(icHandler.getLooper().getThread()); Log.d(LOGTAG, "waitForUiThread() blocking on thread " + icHandler.getLooper().getThread().getName()); } @@ -120,7 +121,7 @@ class GeckoInputConnection public Editable getEditableForUiThread(final Handler uiHandler, final Handler icHandler) { if (DEBUG) { - GeckoApp.assertOnThread(uiHandler.getLooper().getThread()); + ThreadUtils.assertOnThread(uiHandler.getLooper().getThread()); } if (icHandler.getLooper() == uiHandler.getLooper()) { // IC thread is UI thread; safe to use Editable directly @@ -136,7 +137,7 @@ class GeckoInputConnection final Method method, final Object[] args) throws Throwable { if (DEBUG) { - GeckoApp.assertOnThread(uiHandler.getLooper().getThread()); + ThreadUtils.assertOnThread(uiHandler.getLooper().getThread()); Log.d(LOGTAG, "UiEditable." + method.getName() + "() blocking"); } synchronized (icHandler) { @@ -177,7 +178,7 @@ class GeckoInputConnection } } - private final ThreadUtils mThreadUtils = new ThreadUtils(); + private final InputThreadUtils mThreadUtils = new InputThreadUtils(); // Managed only by notifyIMEEnabled; see comments in notifyIMEEnabled private int mIMEState; diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index bf4d2f693a04..be391d7f377f 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -37,6 +37,7 @@ UTIL_JAVA_FILES := \ util/INIParser.java \ util/INISection.java \ util/StringUtils.java \ + util/ThreadUtils.java \ util/UiAsyncTask.java \ $(NULL) diff --git a/mobile/android/base/PromptService.java b/mobile/android/base/PromptService.java index 908f58fb9f92..d7ad5fedd4ac 100644 --- a/mobile/android/base/PromptService.java +++ b/mobile/android/base/PromptService.java @@ -6,6 +6,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.util.GeckoEventResponder; +import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.widget.DateTimePicker; import org.json.JSONArray; @@ -298,7 +299,7 @@ public class PromptService implements OnClickListener, OnCancelListener, OnItemC } public void show(String aTitle, String aText, PromptListItem[] aMenuList, boolean aMultipleSelection) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); // treat actions that show a dialog as if preventDefault by content to prevent panning GeckoApp.mAppContext.getLayerView().abortPanning(); @@ -394,7 +395,7 @@ public class PromptService implements OnClickListener, OnCancelListener, OnItemC @Override public void onClick(DialogInterface aDialog, int aWhich) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); JSONObject ret = new JSONObject(); try { int button = -1; @@ -436,13 +437,13 @@ public class PromptService implements OnClickListener, OnCancelListener, OnItemC @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); mSelected[position] = !mSelected[position]; } @Override public void onCancel(DialogInterface aDialog) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); JSONObject ret = new JSONObject(); try { ret.put("button", -1); diff --git a/mobile/android/base/gfx/GLController.java b/mobile/android/base/gfx/GLController.java index f96d588304cb..0360c240f63c 100644 --- a/mobile/android/base/gfx/GLController.java +++ b/mobile/android/base/gfx/GLController.java @@ -5,10 +5,10 @@ package org.mozilla.gecko.gfx; -import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoEvent; import org.mozilla.gecko.GeckoThread; +import org.mozilla.gecko.util.ThreadUtils; import android.util.Log; @@ -75,7 +75,7 @@ public class GLController { } synchronized void surfaceDestroyed() { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); mSurfaceValid = false; mEGLSurface = null; @@ -94,7 +94,7 @@ public class GLController { } synchronized void surfaceChanged(int newWidth, int newHeight) { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); mWidth = newWidth; mHeight = newHeight; @@ -161,7 +161,7 @@ public class GLController { } void createCompositor() { - GeckoApp.assertOnUiThread(); + ThreadUtils.assertOnUiThread(); if (mCompositorCreated) { // If the compositor has already been created, just resume it instead. We don't need diff --git a/mobile/android/base/util/GeckoBackgroundThread.java b/mobile/android/base/util/GeckoBackgroundThread.java index 739d84337dc4..f08f6cb96052 100644 --- a/mobile/android/base/util/GeckoBackgroundThread.java +++ b/mobile/android/base/util/GeckoBackgroundThread.java @@ -35,11 +35,12 @@ public final class GeckoBackgroundThread extends Thread { // Get a Handler for a looper thread, or create one if it doesn't yet exist. public static synchronized Handler getHandler() { if (sHandler == null) { - GeckoBackgroundThread lt = new GeckoBackgroundThread(); - lt.start(); - try { - sHandler = lt.mHandlerQueue.take(); - } catch (InterruptedException ie) {} + GeckoBackgroundThread lt = new GeckoBackgroundThread(); + ThreadUtils.setBackgroundThread(lt); + lt.start(); + try { + sHandler = lt.mHandlerQueue.take(); + } catch (InterruptedException ie) {} } return sHandler; } diff --git a/mobile/android/base/util/ThreadUtils.java b/mobile/android/base/util/ThreadUtils.java new file mode 100644 index 000000000000..76a6b92c0639 --- /dev/null +++ b/mobile/android/base/util/ThreadUtils.java @@ -0,0 +1,61 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.util; + +public final class ThreadUtils { + private static Thread sUiThread; + private static Thread sGeckoThread; + private static Thread sBackgroundThread; + + public static void setUiThread(Thread thread) { + sUiThread = thread; + } + + public static void setGeckoThread(Thread thread) { + sGeckoThread = thread; + } + + public static void setBackgroundThread(Thread thread) { + sBackgroundThread = thread; + } + + public static Thread getUiThread() { + return sUiThread; + } + + public static Thread getGeckoThread() { + return sGeckoThread; + } + + public static Thread getBackgroundThread() { + return sBackgroundThread; + } + + public static void assertOnUiThread() { + assertOnThread(getUiThread()); + } + + public static void assertOnGeckoThread() { + assertOnThread(getGeckoThread()); + } + + public static void assertOnBackgroundThread() { + assertOnThread(getBackgroundThread()); + } + + public static void assertOnThread(Thread expectedThread) { + Thread currentThread = Thread.currentThread(); + long currentThreadId = currentThread.getId(); + long expectedThreadId = expectedThread.getId(); + + if (currentThreadId != expectedThreadId) { + throw new IllegalThreadStateException("Expected thread " + expectedThreadId + " (\"" + + expectedThread.getName() + + "\"), but running on thread " + currentThreadId + + " (\"" + currentThread.getName() + ")"); + } + } +}