Bug 1562761 - Add support for dispatching java runnables to Gecko event targets. r=snorp
☠☠ backed out by 0e3ae20248b9 ☠ ☠
authorBobby Holley <bobbyholley@gmail.com>
Fri, 08 Nov 2019 14:32:59 +0000
changeset 501311 efc82f89a7c74dc8efe498342f78a4b379acabd4
parent 501310 093ad83d0ab40951603cb3df46dc29877d51ca75
child 501312 bfa48920ac65d80927aa7f56324d94a574098e46
push id114168
push userdluca@mozilla.com
push dateSun, 10 Nov 2019 03:08:55 +0000
treeherdermozilla-inbound@33f64c1ef3e4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1562761
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1562761 - Add support for dispatching java runnables to Gecko event targets. r=snorp Differential Revision: https://phabricator.services.mozilla.com/D52197
ipc/glue/GeckoChildProcessHost.cpp
ipc/glue/GeckoChildProcessHost.h
mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XPCOMEventTarget.java
widget/android/nsAppShell.cpp
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -174,18 +174,16 @@ class BaseProcessLauncher {
   static BinPathType GetPathToBinary(FilePath&, GeckoProcessType);
 
   void GetChildLogName(const char* origLogName, nsACString& buffer);
 
   const char* ChildProcessType() {
     return XRE_ChildProcessTypeToString(mProcessType);
   }
 
-  nsCOMPtr<nsIEventTarget> GetIPCLauncher();
-
   nsCOMPtr<nsISerialEventTarget> mLaunchThread;
   GeckoProcessType mProcessType;
   UniquePtr<base::LaunchOptions> mLaunchOptions;
   std::vector<std::string> mExtraOpts;
 #ifdef XP_WIN
   nsString mGroupId;
 #endif
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
@@ -835,17 +833,17 @@ IPCLaunchThreadObserver::Observe(nsISupp
   if (gIPCLaunchThread) {
     rv = gIPCLaunchThread->Shutdown();
     gIPCLaunchThread = nullptr;
   }
   mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
   return rv;
 }
 
-nsCOMPtr<nsIEventTarget> BaseProcessLauncher::GetIPCLauncher() {
+nsCOMPtr<nsIEventTarget> GetIPCLauncher() {
   StaticMutexAutoLock lock(gIPCLaunchThreadMutex);
   if (!gIPCLaunchThread) {
     nsCOMPtr<nsIThread> thread;
     nsresult rv = NS_NewNamedThread(NS_LITERAL_CSTRING("IPC Launch"),
                                     getter_AddRefs(thread));
     if (!NS_WARN_IF(NS_FAILED(rv))) {
       NS_DispatchToMainThread(
           NS_NewRunnableFunction("GeckoChildProcessHost::GetIPCLauncher", [] {
@@ -862,17 +860,17 @@ nsCOMPtr<nsIEventTarget> BaseProcessLaun
   MOZ_DIAGNOSTIC_ASSERT(thread);
   return thread;
 }
 
 #else  // defined(XP_WIN) || defined(MOZ_WIDGET_ANDROID)
 
 // Other platforms use an on-demand thread pool.
 
-nsCOMPtr<nsIEventTarget> BaseProcessLauncher::GetIPCLauncher() {
+nsCOMPtr<nsIEventTarget> GetIPCLauncher() {
   nsCOMPtr<nsIEventTarget> pool =
       mozilla::SharedThreadPool::Get(NS_LITERAL_CSTRING("IPC Launch"));
   MOZ_DIAGNOSTIC_ASSERT(pool);
   return pool;
 }
 
 #endif  // XP_WIN
 
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -266,12 +266,14 @@ class GeckoChildProcessHost : public Chi
   mozilla::Atomic<bool> mDestroying;
 
   static uint32_t sNextUniqueID;
   static StaticAutoPtr<LinkedList<GeckoChildProcessHost>>
       sGeckoChildProcessHosts;
   static StaticMutex sMutex;
 };
 
+nsCOMPtr<nsIEventTarget> GetIPCLauncher();
+
 } /* namespace ipc */
 } /* namespace mozilla */
 
 #endif /* __IPC_GLUE_GECKOCHILDPROCESSHOST_H__ */
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XPCOMEventTarget.java
@@ -0,0 +1,58 @@
+/* -*- 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;
+
+import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.mozglue.JNIObject;
+
+/**
+ * Wrapper for nsIEventTarget, enabling seamless dispatch of java runnables to
+ * Gecko event queues.
+ */
+@WrapForJNI
+public final class XPCOMEventTarget extends JNIObject {
+    public void dispatch(Runnable runnable) {
+        dispatchNative(new JNIRunnable(runnable));
+    }
+
+    public static synchronized XPCOMEventTarget mainThread() {
+        if (mMainThread == null) {
+            mMainThread = createWrapper("main");
+        }
+        return mMainThread;
+    }
+    private static XPCOMEventTarget mMainThread = null;
+
+    public static synchronized XPCOMEventTarget launcherThread() {
+        if (mLauncherThread == null) {
+            mLauncherThread = createWrapper("launcher");
+        }
+        return mLauncherThread;
+    }
+    private static XPCOMEventTarget mLauncherThread = null;
+
+    public native boolean isOnCurrentThread();
+    private native void dispatchNative(JNIRunnable runnable);
+    private static native XPCOMEventTarget createWrapper(String name);
+
+    @Override
+    protected native void disposeNative();
+
+    @WrapForJNI
+    final class JNIRunnable {
+        JNIRunnable(Runnable inner) {
+            mInner = inner;
+        }
+
+        @WrapForJNI
+        void run() {
+            mInner.run();
+        }
+
+        private Runnable mInner;
+    }
+}
+
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -29,16 +29,17 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Components.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Hal.h"
 #include "mozilla/dom/BrowserChild.h"
 #include "mozilla/intl/OSPreferences.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/widget/ScreenManager.h"
 #include "prenv.h"
 
 #include "AndroidBridge.h"
 #include "AndroidBridgeUtilities.h"
 #include "AndroidSurfaceTexture.h"
 #include "GeneratedJNINatives.h"
 #include <android/log.h>
@@ -345,31 +346,73 @@ class GeckoAppShellSupport final
     }
 
     widget::AndroidAlerts::NotifyListener(aName->ToString(),
                                           aTopic->ToCString().get(),
                                           aCookie->ToString().get());
   }
 };
 
+class XPCOMEventTargetWrapper final
+    : public java::XPCOMEventTarget::Natives<XPCOMEventTargetWrapper> {
+ public:
+  // Wraps a java runnable into an XPCOM runnable and dispatches it to mTarget.
+  void DispatchNative(mozilla::jni::Object::Param aJavaRunnable) {
+    java::XPCOMEventTarget::JNIRunnable::GlobalRef r =
+        java::XPCOMEventTarget::JNIRunnable::Ref::From(aJavaRunnable);
+    mTarget->Dispatch(NS_NewRunnableFunction(
+        "XPCOMEventTargetWrapper::DispatchNative",
+        [runnable = std::move(r)]() { runnable->Run(); }));
+  }
+
+  bool IsOnCurrentThread() { return mTarget->IsOnCurrentThread(); }
+
+  // Instantiates a wrapper. The Java code calls this only once per wrapped
+  // thread, and caches the result.
+  static java::XPCOMEventTarget::LocalRef CreateWrapper(
+      mozilla::jni::String::Param aName) {
+    nsString name(aName->ToString());
+    nsCOMPtr<nsIEventTarget> target;
+    if (name.EqualsLiteral("main")) {
+      target = do_GetMainThread();
+    } else if (name.EqualsLiteral("launcher")) {
+      target = ipc::GetIPCLauncher();
+    } else {
+      MOZ_CRASH("Trying to create JNI wrapper for unknown XPCOM thread");
+    }
+
+    auto java = java::XPCOMEventTarget::New();
+    auto native = MakeUnique<XPCOMEventTargetWrapper>(target.forget());
+    AttachNative(java, std::move(native));
+    return java;
+  }
+
+  explicit XPCOMEventTargetWrapper(already_AddRefed<nsIEventTarget> aTarget)
+      : mTarget(aTarget) {}
+
+ private:
+  nsCOMPtr<nsIEventTarget> mTarget;
+};
+
 nsAppShell::nsAppShell()
     : mSyncRunFinished(*(sAppShellLock = new Mutex("nsAppShell")),
                        "nsAppShell.SyncRun"),
       mSyncRunQuit(false) {
   {
     MutexAutoLock lock(*sAppShellLock);
     sAppShell = this;
   }
 
   hal::Init();
 
   if (!XRE_IsParentProcess()) {
     if (jni::IsAvailable()) {
       GeckoThreadSupport::Init();
       GeckoAppShellSupport::Init();
+      XPCOMEventTargetWrapper::Init();
       mozilla::GeckoSystemStateListener::Init();
       mozilla::widget::Telemetry::Init();
       mozilla::widget::GeckoTelemetryDelegate::Init();
 
       // Set the corresponding state in GeckoThread.
       java::GeckoThread::SetState(java::GeckoThread::State::RUNNING());
     }
     return;
@@ -378,16 +421,17 @@ nsAppShell::nsAppShell()
   if (jni::IsAvailable()) {
     ScreenManager& screenManager = ScreenManager::GetSingleton();
     screenManager.SetHelper(mozilla::MakeUnique<ScreenHelperAndroid>());
 
     // Initialize JNI and Set the corresponding state in GeckoThread.
     AndroidBridge::ConstructBridge();
     GeckoAppShellSupport::Init();
     GeckoThreadSupport::Init();
+    XPCOMEventTargetWrapper::Init();
     mozilla::GeckoBatteryManager::Init();
     mozilla::GeckoNetworkManager::Init();
     mozilla::GeckoProcessManager::Init();
     mozilla::GeckoScreenOrientation::Init();
     mozilla::GeckoSystemStateListener::Init();
     mozilla::PrefsHelper::Init();
     mozilla::widget::Telemetry::Init();
     mozilla::widget::WebExecutorSupport::Init();