Bug 1049136 - Add native-code awareness of the Java UI thread so we can do thread assertions. r=snorp
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 08 Aug 2014 18:15:38 -0400
changeset 198689 6432653640cba93b61acdb754860dadafcb3b50e
parent 198688 4ffa223f053fae18fa2c940bbd2860ad34bb6841
child 198690 a5ce6752446f2e95c8f5990c25451b41501d83c2
push idunknown
push userunknown
push dateunknown
reviewerssnorp
bugs1049136
milestone34.0a1
Bug 1049136 - Add native-code awareness of the Java UI thread so we can do thread assertions. r=snorp
mobile/android/base/GeckoAppShell.java
mobile/android/base/GeckoThread.java
mozglue/android/jni-stubs.inc
widget/android/APZCCallbackHandler.cpp
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/AndroidJNI.cpp
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -178,16 +178,17 @@ public class GeckoAppShell
     static public final int LINK_TYPE_WIMAX = 4;
     static public final int LINK_TYPE_2G = 5;
     static public final int LINK_TYPE_3G = 6;
     static public final int LINK_TYPE_4G = 7;
 
     /* The Android-side API: API methods that Android calls */
 
     // Initialization methods
+    public static native void registerJavaUiThread();
     public static native void nativeInit();
 
     // helper methods
     //    public static native void setSurfaceView(GeckoSurfaceView sv);
     public static native void setLayerClient(Object client);
     public static native void onResume();
     public static void callObserver(String observerKey, String topic, String data) {
         sendEventToGecko(GeckoEvent.createCallObserverEvent(observerKey, topic, data));
--- a/mobile/android/base/GeckoThread.java
+++ b/mobile/android/base/GeckoThread.java
@@ -161,16 +161,24 @@ public class GeckoThread extends Thread 
     public void run() {
         Looper.prepare();
         ThreadUtils.sGeckoThread = this;
         ThreadUtils.sGeckoHandler = new Handler();
         ThreadUtils.sGeckoQueue = Looper.myQueue();
 
         String path = initGeckoEnvironment();
 
+        // This can only happen after the call to initGeckoEnvironment
+        // above, because otherwise the JNI code hasn't been loaded yet.
+        ThreadUtils.postToUiThread(new Runnable() {
+            @Override public void run() {
+                GeckoAppShell.registerJavaUiThread();
+            }
+        });
+
         Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - runGecko");
 
         String args = addCustomProfileArg(mArgs);
         String type = getTypeFromAction(mAction);
 
         if (!AppConstants.MOZILLA_OFFICIAL) {
             Log.i(LOGTAG, "RunGecko - args = " + args);
         }
--- a/mozglue/android/jni-stubs.inc
+++ b/mozglue/android/jni-stubs.inc
@@ -53,16 +53,35 @@ Java_org_mozilla_gecko_ANRReporter_relea
 #endif
 
 #ifdef JNI_BINDINGS
   xul_dlsym("Java_org_mozilla_gecko_ANRReporter_releaseNativeStack", &f_Java_org_mozilla_gecko_ANRReporter_releaseNativeStack);
 #endif
 
 #ifdef JNI_STUBS
 
+typedef void (*Java_org_mozilla_gecko_GeckoAppShell_registerJavaUiThread_t)(JNIEnv *, jclass);
+static Java_org_mozilla_gecko_GeckoAppShell_registerJavaUiThread_t f_Java_org_mozilla_gecko_GeckoAppShell_registerJavaUiThread;
+extern "C" NS_EXPORT void JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_registerJavaUiThread(JNIEnv * arg0, jclass arg1) {
+    if (!f_Java_org_mozilla_gecko_GeckoAppShell_registerJavaUiThread) {
+        arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
+                       "JNI Function called before it was loaded");
+        return ;
+    }
+     f_Java_org_mozilla_gecko_GeckoAppShell_registerJavaUiThread(arg0, arg1);
+}
+#endif
+
+#ifdef JNI_BINDINGS
+  xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_registerJavaUiThread", &f_Java_org_mozilla_gecko_GeckoAppShell_registerJavaUiThread);
+#endif
+
+#ifdef JNI_STUBS
+
 typedef void (*Java_org_mozilla_gecko_GeckoAppShell_nativeInit_t)(JNIEnv *, jclass);
 static Java_org_mozilla_gecko_GeckoAppShell_nativeInit_t f_Java_org_mozilla_gecko_GeckoAppShell_nativeInit;
 extern "C" NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv * arg0, jclass arg1) {
     if (!f_Java_org_mozilla_gecko_GeckoAppShell_nativeInit) {
         arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
                        "JNI Function called before it was loaded");
         return ;
--- a/widget/android/APZCCallbackHandler.cpp
+++ b/widget/android/APZCCallbackHandler.cpp
@@ -33,27 +33,27 @@ APZCCallbackHandler::SetNativePanZoomCon
     mNativePanZoomController = NativePanZoomController::Wrap(obj);
     return old;
 }
 
 void
 APZCCallbackHandler::NotifyDefaultPrevented(const ScrollableLayerGuid& aGuid,
                                             bool aDefaultPrevented)
 {
-    if (NS_IsMainThread()) {
+    if (!AndroidBridge::IsJavaUiThread()) {
         // The notification must reach the APZ on the Java UI thread (aka the
         // APZ "controller" thread) but we get it from the Gecko thread, so we
         // have to throw it onto the other thread.
         AndroidBridge::Bridge()->PostTaskToUiThread(NewRunnableMethod(
             this, &APZCCallbackHandler::NotifyDefaultPrevented,
             aGuid, aDefaultPrevented), 0);
         return;
     }
 
-    // This should be running on the Java UI thread
+    MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
     APZCTreeManager* controller = nsWindow::GetAPZCTreeManager();
     if (controller) {
         controller->ContentReceivedTouch(aGuid, aDefaultPrevented);
     }
 }
 
 nsIDOMWindowUtils*
 APZCCallbackHandler::GetDOMWindowUtils()
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -42,16 +42,17 @@
 #include "nsIScriptError.h"
 #include "nsIHttpChannel.h"
 
 using namespace mozilla;
 using namespace mozilla::widget::android;
 using namespace mozilla::gfx;
 
 AndroidBridge* AndroidBridge::sBridge;
+pthread_t AndroidBridge::sJavaUiThread = -1;
 static unsigned sJavaEnvThreadIndex = 0;
 static jobject sGlobalContext = nullptr;
 static void JavaThreadDetachFunc(void *arg);
 
 // This is a dummy class that can be used in the template for android::sp
 class AndroidRefable {
     void incStrong(void* thing) { }
     void decStrong(void* thing) { }
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -126,16 +126,24 @@ public:
         NOTIFY_IME_REPLY_EVENT = -1,
     };
 
     enum {
         LAYER_CLIENT_TYPE_NONE = 0,
         LAYER_CLIENT_TYPE_GL = 2            // AndroidGeckoGLLayerClient
     };
 
+    static void RegisterJavaUiThread() {
+        sJavaUiThread = pthread_self();
+    }
+
+    static bool IsJavaUiThread() {
+        return pthread_equal(pthread_self(), sJavaUiThread);
+    }
+
     static void ConstructBridge(JNIEnv *jEnv);
 
     static AndroidBridge *Bridge() {
         return sBridge;
     }
 
     static JavaVM *GetVM() {
         MOZ_ASSERT(sBridge);
@@ -348,16 +356,17 @@ public:
 
     static void InputStreamClose(jobject obj);
     static uint32_t InputStreamAvailable(jobject obj);
     static nsresult InputStreamRead(jobject obj, char *aBuf, uint32_t aCount, uint32_t *aRead);
 
     static nsresult GetExternalPublicDirectory(const nsAString& aType, nsAString& aPath);
 
 protected:
+    static pthread_t sJavaUiThread;
     static AndroidBridge* sBridge;
     nsTArray<nsCOMPtr<nsIMobileMessageCallback> > mSmsRequests;
 
     // the global JavaVM
     JavaVM *mJavaVM;
 
     // the JNIEnv for the main thread
     JNIEnv *mJNIEnv;
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -54,16 +54,22 @@ using namespace mozilla::widget::android
 /* Forward declare all the JNI methods as extern "C" */
 
 extern "C" {
 /*
  * Incoming JNI methods
  */
 
 NS_EXPORT void JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_registerJavaUiThread(JNIEnv *jenv, jclass jc)
+{
+    AndroidBridge::RegisterJavaUiThread();
+}
+
+NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv *jenv, jclass jc)
 {
     AndroidBridge::ConstructBridge(jenv);
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_notifyGeckoOfEvent(JNIEnv *jenv, jclass jc, jobject event)
 {