Bug 1157908 - Optimize pumpMessageLoop call to use less JNI; r=snorp

This commit is contained in:
Jim Chen 2015-04-24 14:40:55 -04:00
parent 5864b21201
commit d34c3d89e6
10 changed files with 61 additions and 70 deletions

View File

@ -254,7 +254,7 @@ public class GeckoAppShell
// Initialization methods
public static native void registerJavaUiThread();
public static native void nativeInit(ClassLoader clsLoader);
public static native void nativeInit(ClassLoader clsLoader, MessageQueue msgQueue);
// helper methods
public static native void onResume();
@ -264,7 +264,6 @@ public class GeckoAppShell
public static void removeObserver(String observerKey) {
sendEventToGecko(GeckoEvent.createRemoveObserverEvent(observerKey));
}
public static native Message getNextMessageFromQueue(MessageQueue queue);
public static native void onSurfaceTextureFrameAvailable(Object surfaceTexture, int id);
public static native void dispatchMemoryPressure();
@ -351,7 +350,7 @@ public class GeckoAppShell
Looper.myQueue().addIdleHandler(idleHandler);
// Initialize AndroidBridge.
nativeInit(GeckoAppShell.class.getClassLoader());
nativeInit(GeckoAppShell.class.getClassLoader(), Looper.myQueue());
// First argument is the .apk path
String combinedArgs = apkPath + " -greomni " + apkPath;
@ -2432,13 +2431,8 @@ public class GeckoAppShell
}
@WrapElementForJNI
public static boolean pumpMessageLoop() {
Handler geckoHandler = ThreadUtils.sGeckoHandler;
Message msg = getNextMessageFromQueue(ThreadUtils.sGeckoQueue);
if (msg == null) {
return false;
}
public static boolean pumpMessageLoop(final Message msg) {
final Handler geckoHandler = ThreadUtils.sGeckoHandler;
if (msg.obj == geckoHandler && msg.getTarget() == geckoHandler) {
// Our "queue is empty" message; see runGecko()

View File

@ -156,7 +156,6 @@ public class GeckoThread extends Thread implements GeckoEventListener {
Looper.prepare();
ThreadUtils.sGeckoThread = this;
ThreadUtils.sGeckoHandler = new Handler();
ThreadUtils.sGeckoQueue = Looper.myQueue();
if (mDebugging) {
try {

View File

@ -36,7 +36,6 @@ public final class ThreadUtils {
// Once Bug 709230 is resolved we should reconsider this as ProGuard should be able to optimise
// this out at compile time.
public static Handler sGeckoHandler;
public static MessageQueue sGeckoQueue;
public static volatile Thread sGeckoThread;
// Delayed Runnable that resets the Gecko thread priority.

View File

@ -77,16 +77,16 @@ Java_org_mozilla_gecko_GeckoAppShell_registerJavaUiThread(JNIEnv * arg0, jclass
#ifdef JNI_STUBS
typedef void (*Java_org_mozilla_gecko_GeckoAppShell_nativeInit_t)(JNIEnv *, jclass, jobject);
typedef void (*Java_org_mozilla_gecko_GeckoAppShell_nativeInit_t)(JNIEnv *, jclass, jobject, jobject);
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, jobject arg2) {
Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv * arg0, jclass arg1, jobject arg2, jobject arg3) {
if (!f_Java_org_mozilla_gecko_GeckoAppShell_nativeInit) {
arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
"JNI Function called before it was loaded");
return ;
}
f_Java_org_mozilla_gecko_GeckoAppShell_nativeInit(arg0, arg1, arg2);
f_Java_org_mozilla_gecko_GeckoAppShell_nativeInit(arg0, arg1, arg2, arg3);
}
#endif
@ -115,25 +115,6 @@ Java_org_mozilla_gecko_GeckoAppShell_onResume(JNIEnv * arg0, jclass arg1) {
#ifdef JNI_STUBS
typedef jobject (*Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue_t)(JNIEnv *, jclass, jobject);
static Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue_t f_Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue;
extern "C" NS_EXPORT jobject JNICALL
Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue(JNIEnv * arg0, jclass arg1, jobject arg2) {
if (!f_Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue) {
arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
"JNI Function called before it was loaded");
return nullptr;
}
return f_Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue(arg0, arg1, arg2);
}
#endif
#ifdef JNI_BINDINGS
xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue", &f_Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue);
#endif
#ifdef JNI_STUBS
typedef void (*Java_org_mozilla_gecko_GeckoAppShell_onSurfaceTextureFrameAvailable_t)(JNIEnv *, jclass, jobject, jint);
static Java_org_mozilla_gecko_GeckoAppShell_onSurfaceTextureFrameAvailable_t f_Java_org_mozilla_gecko_GeckoAppShell_onSurfaceTextureFrameAvailable;
extern "C" NS_EXPORT void JNICALL

View File

@ -150,7 +150,7 @@ jfieldID AndroidBridge::GetStaticFieldID(JNIEnv* env, jclass jClass,
}
void
AndroidBridge::ConstructBridge(JNIEnv *jEnv, Object::Param clsLoader)
AndroidBridge::ConstructBridge(JNIEnv *jEnv, Object::Param clsLoader, Object::Param msgQueue)
{
/* NSS hack -- bionic doesn't handle recursive unloads correctly,
* because library finalizer functions are called with the dynamic
@ -165,6 +165,16 @@ AndroidBridge::ConstructBridge(JNIEnv *jEnv, Object::Param clsLoader)
MOZ_ASSERT(!sBridge);
sBridge = new AndroidBridge;
sBridge->Init(jEnv, clsLoader); // Success or crash
auto msgQueueClass = ClassObject::LocalRef::Adopt(
jEnv, jEnv->GetObjectClass(msgQueue.Get()));
sBridge->mMessageQueue = msgQueue;
// mMessageQueueNext must not be null
sBridge->mMessageQueueNext = GetMethodID(
jEnv, msgQueueClass.Get(), "next", "()Landroid/os/Message;");
// mMessageQueueMessages may be null (e.g. due to proguard optimization)
sBridge->mMessageQueueMessages = jEnv->GetFieldID(
msgQueueClass.Get(), "mMessages", "Landroid/os/Message;");
}
void
@ -1666,6 +1676,32 @@ AndroidBridge::GetProxyForURI(const nsACString & aSpec,
return NS_OK;
}
bool
AndroidBridge::PumpMessageLoop()
{
JNIEnv* const env = GetJNIEnv();
if (mMessageQueueMessages) {
auto msg = Object::LocalRef::Adopt(env,
env->GetObjectField(mMessageQueue.Get(),
mMessageQueueMessages));
// if queue.mMessages is null, queue.next() will block, which we don't
// want. It turns out to be an order of magnitude more performant to do
// this extra check here and block less vs. one fewer checks here and
// more blocking.
if (!msg) {
return false;
}
}
auto msg = Object::LocalRef::Adopt(
env, env->CallObjectMethod(mMessageQueue.Get(), mMessageQueueNext));
if (!msg) {
return false;
}
return GeckoAppShell::PumpMessageLoop(msg);
}
/* attribute nsIAndroidBrowserApp browserApp; */
NS_IMETHODIMP nsAndroidBridge::GetBrowserApp(nsIAndroidBrowserApp * *aBrowserApp)

View File

@ -133,7 +133,9 @@ public:
return pthread_equal(pthread_self(), sJavaUiThread);
}
static void ConstructBridge(JNIEnv *jEnv, jni::Object::Param clsLoader);
static void ConstructBridge(JNIEnv *jEnv,
jni::Object::Param clsLoader,
jni::Object::Param msgQueue);
static AndroidBridge *Bridge() {
return sBridge;
@ -314,6 +316,8 @@ public:
const int32_t aPort,
nsACString & aResult);
bool PumpMessageLoop();
// Utility methods.
static jstring NewJavaString(JNIEnv* env, const char16_t* string, uint32_t len);
static jstring NewJavaString(JNIEnv* env, const nsAString& string);
@ -410,6 +414,10 @@ protected:
jni::Object::GlobalRef mClassLoader;
jmethodID mClassLoaderLoadClass;
jni::Object::GlobalRef mMessageQueue;
jfieldID mMessageQueueMessages;
jmethodID mMessageQueueNext;
// calls we've dlopened from libjnigraphics.so
int (* AndroidBitmap_getInfo)(JNIEnv *env, jobject bitmap, void *info);
int (* AndroidBitmap_lockPixels)(JNIEnv *env, jobject bitmap, void **buffer);

View File

@ -65,9 +65,10 @@ Java_org_mozilla_gecko_GeckoAppShell_registerJavaUiThread(JNIEnv *jenv, jclass j
}
NS_EXPORT void JNICALL
Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv *jenv, jclass, jobject clsLoader)
Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv *jenv, jclass, jobject clsLoader, jobject msgQueue)
{
AndroidBridge::ConstructBridge(jenv, jni::Object::Ref::From(clsLoader));
AndroidBridge::ConstructBridge(
jenv, jni::Object::Ref::From(clsLoader), jni::Object::Ref::From(msgQueue));
}
NS_EXPORT void JNICALL
@ -854,33 +855,6 @@ Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden(JNIEnv* jenv, jcla
NS_DispatchToMainThread(runnable);
}
NS_EXPORT jobject JNICALL
Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue(JNIEnv* jenv, jclass, jobject queue)
{
static jclass jMessageQueueCls = nullptr;
static jfieldID jMessagesField;
static jmethodID jNextMethod;
if (!jMessageQueueCls) {
jMessageQueueCls = (jclass) jenv->NewGlobalRef(jenv->FindClass("android/os/MessageQueue"));
jNextMethod = jenv->GetMethodID(jMessageQueueCls, "next", "()Landroid/os/Message;");
jMessagesField = jenv->GetFieldID(jMessageQueueCls, "mMessages", "Landroid/os/Message;");
}
if (!jMessageQueueCls || !jNextMethod)
return nullptr;
if (jMessagesField) {
jobject msg = jenv->GetObjectField(queue, jMessagesField);
// if queue.mMessages is null, queue.next() will block, which we don't want
// It turns out to be an order of magnitude more performant to do this extra check here and
// block less vs. one fewer checks here and more blocking.
if (!msg) {
return nullptr;
}
}
return jenv->CallObjectMethod(queue, jNextMethod);
}
NS_EXPORT void JNICALL
Java_org_mozilla_gecko_GeckoAppShell_onSurfaceTextureFrameAvailable(JNIEnv* jenv, jclass, jobject surfaceTexture, jint id)
{

View File

@ -529,9 +529,9 @@ void GeckoAppShell::PerformHapticFeedback(bool a0)
constexpr char GeckoAppShell::PumpMessageLoop_t::name[];
constexpr char GeckoAppShell::PumpMessageLoop_t::signature[];
bool GeckoAppShell::PumpMessageLoop()
bool GeckoAppShell::PumpMessageLoop(mozilla::jni::Object::Param a0)
{
return mozilla::jni::Method<PumpMessageLoop_t>::Call(nullptr, nullptr);
return mozilla::jni::Method<PumpMessageLoop_t>::Call(nullptr, nullptr, a0);
}
constexpr char GeckoAppShell::RegisterSurfaceTextureFrameListener_t::name[];

View File

@ -1009,13 +1009,13 @@ public:
typedef bool SetterType;
static constexpr char name[] = "pumpMessageLoop";
static constexpr char signature[] =
"()Z";
"(Landroid/os/Message;)Z";
static const bool isStatic = true;
static const bool isMultithreaded = false;
static const mozilla::jni::ExceptionMode exceptionMode = mozilla::jni::ExceptionMode::ABORT;
};
static bool PumpMessageLoop();
static bool PumpMessageLoop(mozilla::jni::Object::Param);
public:
struct RegisterSurfaceTextureFrameListener_t {

View File

@ -248,7 +248,7 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
// (bug 750713). Looper messages effectively have the lowest
// priority because we only process them before we're about to
// wait for new events.
if (widget::GeckoAppShell::PumpMessageLoop()) {
if (AndroidBridge::Bridge()->PumpMessageLoop()) {
return true;
}