Bug 1344892 - Let native calls dispatch to XPCOM event queue; r=snorp

Bug 1344892 - 1. Add option to dispatch to priority queue; r=snorp

For the regular "gecko" option, change to dispatching to the XPCOM event
queue, and add a new "gecko_priority" option that dispatches calls to
the widget event queue. GeckoThread.waitOnGecko is changed to wait on
both the widget queue and the XPCOM queue. nsAppShell::SyncRunEvent is
changed to avoid a possible deadlock condition involving locking
sAppShellLock twice.

Bug 1344892 - 2. Update dispatchTo = "gecko" options; r=snorp

Update some existing dispatchTo = "gecko" options to "gecko_priority",
which typically involve UI events or JNI management calls like
disposeNative. As a rule, disposeNative is dispatched to the queue with
the least priority among the queues that other native members of the
same class dispatch to (i.e. "gecko_priority" if all other native
members dispatch to "gecko_priority", or "gecko" if any native members
dispatch to "gecko").

Bug 1344892 - 3. Update auto-generated bindings; r=me
This commit is contained in:
Jim Chen 2017-03-16 23:30:54 -04:00
parent 589919e4a7
commit b00f1b075f
17 changed files with 87 additions and 78 deletions

View File

@ -30,6 +30,7 @@ public class AnnotationInfo {
public enum DispatchTarget {
GECKO,
GECKO_PRIORITY,
PROXY,
CURRENT;

View File

@ -780,7 +780,7 @@ public class ZoomedView extends FrameLayout implements LayerView.DynamicToolbarL
return ((System.nanoTime() - lastStartTimeReRender) < MINIMUM_DELAY_BETWEEN_TWO_RENDER_CALLS_NS);
}
@WrapForJNI(dispatchTo = "gecko")
@WrapForJNI(dispatchTo = "gecko_priority")
private static native void requestZoomedViewData(ByteBuffer buffer, int tabId,
int xPos, int yPos, int width,
int height, float scale);

View File

@ -129,11 +129,11 @@ public class AndroidGamepadManager {
}
}
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko_priority")
private static native void onGamepadChange(int id, boolean added);
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko_priority")
private static native void onButtonChange(int id, int button, boolean pressed, float value);
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko_priority")
private static native void onAxisChange(int id, boolean[] valid, float[] values);
private static boolean sStarted;

View File

@ -250,11 +250,7 @@ public class GeckoAppShell
return sLayerView;
}
// Synchronously notify a Gecko observer; must be called from Gecko thread.
@WrapForJNI(calledFrom = "gecko")
public static native void syncNotifyObservers(String topic, String data);
@WrapForJNI(stubName = "NotifyObservers", dispatchTo = "proxy")
@WrapForJNI(stubName = "NotifyObservers", dispatchTo = "gecko")
private static native void nativeNotifyObservers(String topic, String data);
@RobocopTarget

View File

@ -30,8 +30,11 @@ public @interface WrapForJNI {
/**
* Action to take if member access returns an exception.
* One of "abort", "ignore", or "nsresult". "nsresult" is not supported for native
* methods.
* - "abort" will cause a crash if there is a pending exception.
* - "ignore" will not handle any pending exceptions; it is then the caller's
* responsibility to handle exceptions.
* - "nsresult" will clear any pending exceptions and return an error code; not
* supported for native methods.
*/
String exceptionMode() default "abort";
@ -43,9 +46,15 @@ public @interface WrapForJNI {
/**
* The thread that the method call will be dispatched to.
* One of "current", "gecko", or "proxy". Not supported for non-native methods,
* fields, and constructors. Only void-return methods are supported for anything other
* than current thread.
* - "current" indicates no dispatching; only supported value for fields,
* constructors, non-native methods, and non-void native methods.
* - "gecko" indicates dispatching to the Gecko XPCOM (nsThread) event queue.
* - "gecko_priority" indicates dispatching to the Gecko widget
* (nsAppShell) event queue; in most cases, events in the widget event
* queue (aka native event queue) are favored over events in the XPCOM
* event queue.
* - "proxy" indicates dispatching to a proxy function as a function object; see
* widget/jni/Natives.h.
*/
String dispatchTo() default "current";
}

View File

@ -80,7 +80,7 @@ public class LayerView extends FrameLayout {
/* package */ native void attachToJava(GeckoLayerClient layerClient,
NativePanZoomController npzc);
@WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
@WrapForJNI(calledFrom = "any", dispatchTo = "gecko_priority")
/* package */ native void onSizeChanged(int windowWidth, int windowHeight,
int screenWidth, int screenHeight);

View File

@ -220,7 +220,7 @@ class NativePanZoomController extends JNIObject implements PanZoomController {
mDestroyed = false;
}
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko") @Override // JNIObject
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko_priority") @Override // JNIObject
protected native void disposeNative();
@Override

View File

@ -650,7 +650,7 @@ public:
// Invoke callbacks synchronously if we're already on Gecko thread.
return aCall();
}
nsAppShell::PostEvent(Move(aCall));
NS_DispatchToMainThread(NS_NewRunnableFunction(Move(aCall)));
}
static void Finalize(const CallbackDelegate::LocalRef& aInstance)

View File

@ -101,7 +101,7 @@ template<class Impl>
class GeckoAppShell::Natives : public mozilla::jni::NativeImpl<GeckoAppShell, Impl>
{
public:
static const JNINativeMethod methods[8];
static const JNINativeMethod methods[7];
};
template<class Impl>
@ -133,11 +133,7 @@ const JNINativeMethod GeckoAppShell::Natives<Impl>::methods[] = {
mozilla::jni::MakeNativeMethod<GeckoAppShell::ReportJavaCrash_t>(
mozilla::jni::NativeStub<GeckoAppShell::ReportJavaCrash_t, Impl>
::template Wrap<&Impl::ReportJavaCrash>),
mozilla::jni::MakeNativeMethod<GeckoAppShell::SyncNotifyObservers_t>(
mozilla::jni::NativeStub<GeckoAppShell::SyncNotifyObservers_t, Impl>
::template Wrap<&Impl::SyncNotifyObservers>)
::template Wrap<&Impl::ReportJavaCrash>)
};
template<class Impl>

View File

@ -668,9 +668,6 @@ auto GeckoAppShell::StartGeckoServiceChildProcess(mozilla::jni::String::Param a0
return mozilla::jni::Method<StartGeckoServiceChildProcess_t>::Call(GeckoAppShell::Context(), nullptr, a0, a1, a2, a3);
}
constexpr char GeckoAppShell::SyncNotifyObservers_t::name[];
constexpr char GeckoAppShell::SyncNotifyObservers_t::signature[];
constexpr char GeckoAppShell::UnlockProfile_t::name[];
constexpr char GeckoAppShell::UnlockProfile_t::signature[];

View File

@ -66,7 +66,7 @@ public:
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::UI;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::GECKO;
mozilla::jni::DispatchTarget::GECKO_PRIORITY;
};
struct OnButtonChange_t {
@ -87,7 +87,7 @@ public:
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::UI;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::GECKO;
mozilla::jni::DispatchTarget::GECKO_PRIORITY;
};
struct OnGamepadAdded_t {
@ -127,7 +127,7 @@ public:
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::UI;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::GECKO;
mozilla::jni::DispatchTarget::GECKO_PRIORITY;
};
struct Start_t {
@ -1500,7 +1500,7 @@ public:
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::ANY;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::PROXY;
mozilla::jni::DispatchTarget::GECKO;
};
struct NotifyAlertListener_t {
@ -1882,25 +1882,6 @@ public:
static auto StartGeckoServiceChildProcess(mozilla::jni::String::Param, mozilla::jni::ObjectArray::Param, int32_t, int32_t) -> int32_t;
struct SyncNotifyObservers_t {
typedef GeckoAppShell Owner;
typedef void ReturnType;
typedef void SetterType;
typedef mozilla::jni::Args<
mozilla::jni::String::Param,
mozilla::jni::String::Param> Args;
static constexpr char name[] = "syncNotifyObservers";
static constexpr char signature[] =
"(Ljava/lang/String;Ljava/lang/String;)V";
static const bool isStatic = true;
static const mozilla::jni::ExceptionMode exceptionMode =
mozilla::jni::ExceptionMode::ABORT;
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::GECKO;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::CURRENT;
};
struct UnlockProfile_t {
typedef GeckoAppShell Owner;
typedef bool ReturnType;
@ -3933,7 +3914,7 @@ public:
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::ANY;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::GECKO;
mozilla::jni::DispatchTarget::GECKO_PRIORITY;
};
struct Reattach_t {
@ -4074,7 +4055,7 @@ public:
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::UI;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::GECKO;
mozilla::jni::DispatchTarget::GECKO_PRIORITY;
};
struct HandleMotionEvent_t {

View File

@ -595,7 +595,7 @@ public:
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::ANY;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::GECKO;
mozilla::jni::DispatchTarget::GECKO_PRIORITY;
};
static const mozilla::jni::CallingThread callingThread =

View File

@ -3,6 +3,8 @@
#include <jni.h>
#include "nsThreadUtils.h"
#include "mozilla/IndexSequence.h"
#include "mozilla/Move.h"
#include "mozilla/RefPtr.h"
@ -501,6 +503,21 @@ struct Dispatcher
HasThisArg, Args...>(Forward<ProxyArgs>(args)...));
}
template<class Traits, bool IsStatic = Traits::isStatic,
typename ThisArg, typename... ProxyArgs>
static typename EnableIf<
Traits::dispatchTarget == DispatchTarget::GECKO_PRIORITY, void>::Type
Run(ThisArg thisArg, ProxyArgs&&... args)
{
// For a static method, do not forward the "this arg" (i.e. the class
// local ref) if the implementation does not request it. This saves us
// a pair of calls to add/delete global ref.
DispatchToGeckoPriorityQueue(MakeUnique<ProxyNativeCall<
Impl, typename Traits::Owner, IsStatic, HasThisArg,
Args...>>(HasThisArg || !IsStatic ? thisArg : nullptr,
Forward<ProxyArgs>(args)...));
}
template<class Traits, bool IsStatic = Traits::isStatic,
typename ThisArg, typename... ProxyArgs>
static typename EnableIf<
@ -510,16 +527,19 @@ struct Dispatcher
// For a static method, do not forward the "this arg" (i.e. the class
// local ref) if the implementation does not request it. This saves us
// a pair of calls to add/delete global ref.
DispatchToGeckoThread(MakeUnique<ProxyNativeCall<
NS_DispatchToMainThread(NS_NewRunnableFunction(ProxyNativeCall<
Impl, typename Traits::Owner, IsStatic, HasThisArg,
Args...>>(HasThisArg || !IsStatic ? thisArg : nullptr,
Forward<ProxyArgs>(args)...));
Args...>(HasThisArg || !IsStatic ? thisArg : nullptr,
Forward<ProxyArgs>(args)...)));
}
template<class Traits, bool IsStatic = false, typename... ProxyArgs>
static typename EnableIf<
Traits::dispatchTarget == DispatchTarget::CURRENT, void>::Type
Run(ProxyArgs&&... args) {}
Run(ProxyArgs&&... args)
{
MOZ_CRASH("Unreachable code");
}
};
} // namespace detail

View File

@ -287,7 +287,7 @@ jclass GetClassRef(JNIEnv* aEnv, const char* aClassName)
return nullptr;
}
void DispatchToGeckoThread(UniquePtr<AbstractCall>&& aCall)
void DispatchToGeckoPriorityQueue(UniquePtr<AbstractCall>&& aCall)
{
class AbstractCallEvent : public nsAppShell::Event
{

View File

@ -50,9 +50,14 @@ enum class DispatchTarget
// wrapped in a function object and is passed thru UsesNativeCallProxy.
// Method must return void.
PROXY,
// Call is dispatched asynchronously on the Gecko thread. Method must
// return void.
// Call is dispatched asynchronously on the Gecko thread to the XPCOM
// (nsThread) event queue. Method must return void.
GECKO,
// Call is dispatched asynchronously on the Gecko thread to the widget
// (nsAppShell) event queue. In most cases, events in the widget event
// queue (aka native event queue) are favored over events in the XPCOM
// event queue. Method must return void.
GECKO_PRIORITY,
};
@ -133,7 +138,7 @@ struct AbstractCall
virtual void operator()() = 0;
};
void DispatchToGeckoThread(UniquePtr<AbstractCall>&& aCall);
void DispatchToGeckoPriorityQueue(UniquePtr<AbstractCall>&& aCall);
/**
* Returns whether Gecko is running in a Fennec environment, as determined by

View File

@ -146,8 +146,22 @@ public:
static void WaitOnGecko()
{
struct NoOpEvent : nsAppShell::Event {
void Run() override {}
struct NoOpRunnable : Runnable
{
NS_IMETHOD Run() override { return NS_OK; }
};
struct NoOpEvent : nsAppShell::Event
{
void Run() override
{
// We cannot call NS_DispatchToMainThread from within
// WaitOnGecko itself because the thread that is calling
// WaitOnGecko may not be an nsThread, and may not be able to do
// a sync dispatch.
NS_DispatchToMainThread(do_AddRef(new NoOpRunnable()),
NS_DISPATCH_SYNC);
}
};
nsAppShell::SyncRunEvent(NoOpEvent());
}
@ -254,20 +268,6 @@ public:
MOZ_CRASH("Uncaught Java exception");
}
static void SyncNotifyObservers(jni::String::Param aTopic,
jni::String::Param aData)
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
NotifyObservers(aTopic, aData);
}
template<typename Functor>
static void OnNativeCall(Functor&& aCall)
{
MOZ_ASSERT(aCall.IsTarget(&NotifyObservers));
NS_DispatchToMainThread(NS_NewRunnableFunction(mozilla::Move(aCall)));
}
static void NotifyObservers(jni::String::Param aTopic,
jni::String::Param aData)
{
@ -429,8 +429,10 @@ nsAppShell::nsAppShell()
nsAppShell::~nsAppShell()
{
{
// Release any thread waiting for a sync call to finish.
MutexAutoLock lock(*sAppShellLock);
sAppShell = nullptr;
mSyncRunFinished.NotifyAll();
}
while (mEventQueue.Pop(/* mayWait */ false)) {
@ -720,13 +722,13 @@ nsAppShell::SyncRunEvent(Event&& event,
bool finished = false;
auto runAndNotify = [&event, &finished] {
mozilla::MutexAutoLock shellLock(*sAppShellLock);
nsAppShell* const appShell = sAppShell;
nsAppShell* const appShell = nsAppShell::Get();
if (MOZ_UNLIKELY(!appShell || appShell->mSyncRunQuit)) {
return;
}
event.Run();
finished = true;
mozilla::MutexAutoLock shellLock(*sAppShellLock);
appShell->mSyncRunFinished.NotifyAll();
};

View File

@ -852,6 +852,8 @@ public:
mozilla::Move(aCall)), &LayerViewEvent::MakeEvent);
return;
}
MOZ_CRASH("Unexpected call");
}
static LayerViewSupport*