зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1594820: Part 1 - Modify XPCOMEventTarget to accept method calls before JNI is ready; r=snorp
Since `XPCOMEventTarget` uses JNI, this patch makes it possible for consumers to retrieve and invoke methods on one without needing to worry about whether JNI is actually up yet. To achieve this, we create the `IXPCOMEventTarget` interface, and observe that both of its methods can be handled by a proxy if JNI is not ready: * Calls to `dispatch` may be enqueued until JNI is up; * Observe that, when JNI is not up yet, the result of `isOnCurrentThread` can never be `true`. Once JNI is up and the event targets have been resolved, the proxies are replaced with the real, concrete `XPCOMEventTarget`s and are no longer used for the remainder of the Gecko instance's lifetime. Differential Revision: https://phabricator.services.mozilla.com/D57837 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
d447a96547
Коммит
cec0194852
|
@ -0,0 +1,11 @@
|
||||||
|
/* -*- 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 interface IXPCOMEventTarget {
|
||||||
|
public void dispatch(final Runnable runnable);
|
||||||
|
public boolean isOnCurrentThread();
|
||||||
|
}
|
|
@ -6,43 +6,81 @@
|
||||||
package org.mozilla.gecko.util;
|
package org.mozilla.gecko.util;
|
||||||
|
|
||||||
import org.mozilla.gecko.annotation.WrapForJNI;
|
import org.mozilla.gecko.annotation.WrapForJNI;
|
||||||
|
import org.mozilla.gecko.GeckoThread;
|
||||||
import org.mozilla.gecko.mozglue.JNIObject;
|
import org.mozilla.gecko.mozglue.JNIObject;
|
||||||
|
import org.mozilla.geckoview.BuildConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for nsIEventTarget, enabling seamless dispatch of java runnables to
|
* Wrapper for nsIEventTarget, enabling seamless dispatch of java runnables to
|
||||||
* Gecko event queues.
|
* Gecko event queues.
|
||||||
*/
|
*/
|
||||||
@WrapForJNI
|
@WrapForJNI
|
||||||
public final class XPCOMEventTarget extends JNIObject {
|
public final class XPCOMEventTarget extends JNIObject implements IXPCOMEventTarget {
|
||||||
|
@Override
|
||||||
public void dispatch(final Runnable runnable) {
|
public void dispatch(final Runnable runnable) {
|
||||||
dispatchNative(new JNIRunnable(runnable));
|
dispatchNative(new JNIRunnable(runnable));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized XPCOMEventTarget mainThread() {
|
public static synchronized IXPCOMEventTarget mainThread() {
|
||||||
if (mMainThread == null) {
|
if (mMainThread == null) {
|
||||||
mMainThread = createWrapper("main");
|
mMainThread = new AsyncProxy("main");
|
||||||
}
|
}
|
||||||
return mMainThread;
|
return mMainThread;
|
||||||
}
|
}
|
||||||
private static XPCOMEventTarget mMainThread = null;
|
private static IXPCOMEventTarget mMainThread = null;
|
||||||
|
|
||||||
public static synchronized XPCOMEventTarget launcherThread() {
|
public static synchronized IXPCOMEventTarget launcherThread() {
|
||||||
if (mLauncherThread == null) {
|
if (mLauncherThread == null) {
|
||||||
mLauncherThread = createWrapper("launcher");
|
mLauncherThread = new AsyncProxy("launcher");
|
||||||
}
|
}
|
||||||
return mLauncherThread;
|
return mLauncherThread;
|
||||||
}
|
}
|
||||||
private static XPCOMEventTarget mLauncherThread = null;
|
private static IXPCOMEventTarget mLauncherThread = null;
|
||||||
|
|
||||||
|
public static void assertOnLauncherThread() {
|
||||||
|
if (BuildConfig.DEBUG && !launcherThread().isOnCurrentThread()) {
|
||||||
|
throw new IllegalThreadStateException("Expected to be running on XPCOM launcher thread");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized IXPCOMEventTarget getTarget(final String name) {
|
||||||
|
if (name.equals("launcher")) {
|
||||||
|
return mLauncherThread;
|
||||||
|
} else if (name.equals("main")) {
|
||||||
|
return mMainThread;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Attempt to assign to unknown thread named " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@WrapForJNI
|
||||||
|
private static synchronized void setTarget(final String name, final XPCOMEventTarget target) {
|
||||||
|
if (name.equals("main")) {
|
||||||
|
mMainThread = target;
|
||||||
|
} else if (name.equals("launcher")) {
|
||||||
|
mLauncherThread = target;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Attempt to assign to unknown thread named " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public native boolean isOnCurrentThread();
|
public native boolean isOnCurrentThread();
|
||||||
private native void dispatchNative(JNIRunnable runnable);
|
|
||||||
private static native XPCOMEventTarget createWrapper(String name);
|
private native void dispatchNative(final JNIRunnable runnable);
|
||||||
|
|
||||||
|
@WrapForJNI
|
||||||
|
private static synchronized void resolveAndDispatch(final String name, final Runnable runnable) {
|
||||||
|
getTarget(name).dispatch(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native void resolveAndDispatchNative(final String name, final Runnable runnable);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected native void disposeNative();
|
protected native void disposeNative();
|
||||||
|
|
||||||
@WrapForJNI
|
@WrapForJNI
|
||||||
final class JNIRunnable {
|
private static final class JNIRunnable {
|
||||||
JNIRunnable(final Runnable inner) {
|
JNIRunnable(final Runnable inner) {
|
||||||
mInner = inner;
|
mInner = inner;
|
||||||
}
|
}
|
||||||
|
@ -54,5 +92,43 @@ public final class XPCOMEventTarget extends JNIObject {
|
||||||
|
|
||||||
private Runnable mInner;
|
private Runnable mInner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class AsyncProxy implements IXPCOMEventTarget {
|
||||||
|
private String mTargetName;
|
||||||
|
|
||||||
|
public AsyncProxy(final String targetName) {
|
||||||
|
mTargetName = targetName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispatch(final Runnable runnable) {
|
||||||
|
final IXPCOMEventTarget target = XPCOMEventTarget.getTarget(mTargetName);
|
||||||
|
|
||||||
|
if (target != null && target instanceof XPCOMEventTarget) {
|
||||||
|
target.dispatch(runnable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GeckoThread.queueNativeCallUntil(GeckoThread.State.JNI_READY,
|
||||||
|
XPCOMEventTarget.class, "resolveAndDispatchNative",
|
||||||
|
String.class, mTargetName, Runnable.class, runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOnCurrentThread() {
|
||||||
|
final IXPCOMEventTarget target = XPCOMEventTarget.getTarget(mTargetName);
|
||||||
|
|
||||||
|
// If target is not yet a XPCOMEventTarget then JNI is not
|
||||||
|
// initialized yet. If JNI is not initialized yet, then we cannot
|
||||||
|
// possibly be running on a target with an XPCOMEventTarget.
|
||||||
|
if (target == null || !(target instanceof XPCOMEventTarget)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise we have a real XPCOMEventTarget, so we can delegate
|
||||||
|
// this call to it.
|
||||||
|
return target.isOnCurrentThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -363,24 +363,26 @@ class XPCOMEventTargetWrapper final
|
||||||
|
|
||||||
bool IsOnCurrentThread() { return mTarget->IsOnCurrentThread(); }
|
bool IsOnCurrentThread() { return mTarget->IsOnCurrentThread(); }
|
||||||
|
|
||||||
// Instantiates a wrapper. The Java code calls this only once per wrapped
|
static void Init() {
|
||||||
// thread, and caches the result.
|
java::XPCOMEventTarget::Natives<XPCOMEventTargetWrapper>::Init();
|
||||||
static java::XPCOMEventTarget::LocalRef CreateWrapper(
|
CreateWrapper(NS_LITERAL_STRING("main"), do_GetMainThread());
|
||||||
mozilla::jni::String::Param aName) {
|
if (XRE_IsParentProcess()) {
|
||||||
nsString name(aName->ToString());
|
CreateWrapper(NS_LITERAL_STRING("launcher"), ipc::GetIPCLauncher());
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CreateWrapper(mozilla::jni::String::Param aName,
|
||||||
|
nsCOMPtr<nsIEventTarget> aTarget) {
|
||||||
auto java = java::XPCOMEventTarget::New();
|
auto java = java::XPCOMEventTarget::New();
|
||||||
auto native = MakeUnique<XPCOMEventTargetWrapper>(target.forget());
|
auto native = MakeUnique<XPCOMEventTargetWrapper>(aTarget.forget());
|
||||||
AttachNative(java, std::move(native));
|
AttachNative(java, std::move(native));
|
||||||
return java;
|
|
||||||
|
java::XPCOMEventTarget::SetTarget(aName, java);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ResolveAndDispatchNative(mozilla::jni::String::Param aName,
|
||||||
|
mozilla::jni::Object::Param aRunnable) {
|
||||||
|
java::XPCOMEventTarget::ResolveAndDispatch(aName, aRunnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit XPCOMEventTargetWrapper(already_AddRefed<nsIEventTarget> aTarget)
|
explicit XPCOMEventTargetWrapper(already_AddRefed<nsIEventTarget> aTarget)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче