Bug 897123 - Make GeckoAppShell.pumpMessageLoop waste less CPU time r=jchen
authorChris Kitching <ckitching@mozilla.com>
Fri, 26 Jul 2013 12:02:00 -0700
changeset 140272 fac2d791f66b83268e9421042d17ecf2b1ae2261
parent 140271 f5c294e75e01d391d5dab62ffb2664fe9692ad98
child 140273 5458a907d1a54a578067dc798738a2840b7ba1f4
push id1952
push usermozilla@noorenberghe.ca
push dateMon, 29 Jul 2013 05:36:04 +0000
treeherderfx-team@fac2d791f66b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjchen
bugs897123
milestone25.0a1
Bug 897123 - Make GeckoAppShell.pumpMessageLoop waste less CPU time r=jchen
mobile/android/base/GeckoAppShell.java
mobile/android/base/GeckoThread.java
mobile/android/base/util/ThreadUtils.java
widget/android/AndroidJNI.cpp
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -292,17 +292,17 @@ public class GeckoAppShell
     public static LayerView getLayerView() {
         return sLayerView;
     }
 
     public static void runGecko(String apkPath, String args, String url, String type) {
         // Preparation for pumpMessageLoop()
         MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler() {
             @Override public boolean queueIdle() {
-                Handler geckoHandler = ThreadUtils.getGeckoHandler();
+                final Handler geckoHandler = ThreadUtils.sGeckoHandler;
                 Message idleMsg = Message.obtain(geckoHandler);
                 // Use |Message.obj == GeckoHandler| to identify our "queue is empty" message
                 idleMsg.obj = geckoHandler;
                 geckoHandler.sendMessageAtFrontOfQueue(idleMsg);
                 // Keep this IdleHandler
                 return true;
             }
         };
@@ -2460,22 +2460,22 @@ public class GeckoAppShell
         GeckoScreenOrientationListener.getInstance().lockScreenOrientation(aOrientation);
     }
 
     public static void unlockScreenOrientation() {
         GeckoScreenOrientationListener.getInstance().unlockScreenOrientation();
     }
 
     public static boolean pumpMessageLoop() {
-        Handler geckoHandler = ThreadUtils.getGeckoHandler();
-        MessageQueue mq = Looper.myQueue();
-        Message msg = getNextMessageFromQueue(mq); 
+        Handler geckoHandler = ThreadUtils.sGeckoHandler;
+        Message msg = getNextMessageFromQueue(ThreadUtils.sGeckoQueue);
+
         if (msg == null)
             return false;
-        if (msg.getTarget() == geckoHandler && msg.obj == geckoHandler) {
+        if (msg.obj == geckoHandler && msg.getTarget() == geckoHandler) {
             // Our "queue is empty" message; see runGecko()
             msg.recycle();
             return false;
         }
         if (msg.getTarget() == null) 
             Looper.myLooper().quit();
         else
             msg.getTarget().dispatchMessage(msg);
--- a/mobile/android/base/GeckoThread.java
+++ b/mobile/android/base/GeckoThread.java
@@ -110,17 +110,19 @@ public class GeckoThread extends Thread 
             }
         }
         return (args != null ? args : "") + profile;
     }
 
     @Override
     public void run() {
         Looper.prepare();
-        ThreadUtils.setGeckoThread(this, new Handler());
+        ThreadUtils.sGeckoThread = this;
+        ThreadUtils.sGeckoHandler = new Handler();
+        ThreadUtils.sGeckoQueue = Looper.myQueue();
 
         String path = initGeckoEnvironment();
 
         Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - runGecko");
 
         String args = addCustomProfileArg(mIntent.getStringExtra("args"));
         String type = getTypeFromAction(mIntent.getAction());
         mIntent = null;
--- a/mobile/android/base/util/ThreadUtils.java
+++ b/mobile/android/base/util/ThreadUtils.java
@@ -1,29 +1,36 @@
 /* -*- 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 android.os.Handler;
+import android.os.MessageQueue;
 import android.util.Log;
 
 import java.util.Map;
 
 public final class ThreadUtils {
     private static final String LOGTAG = "ThreadUtils";
 
     private static Thread sUiThread;
-    private static Thread sGeckoThread;
     private static Thread sBackgroundThread;
 
     private static Handler sUiHandler;
-    private static Handler sGeckoHandler;
+
+    // Referenced directly from GeckoAppShell in highly performance-sensitive code (The extra
+    // function call of the getter was harming performance. (Bug 897123))
+    // 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 Thread sGeckoThread;
 
     @SuppressWarnings("serial")
     public static class UiThreadBlockedException extends RuntimeException {
         public UiThreadBlockedException() {
             super();
         }
 
         public UiThreadBlockedException(String msg) {
@@ -51,45 +58,32 @@ public final class ThreadUtils {
         }
     }
 
     public static void setUiThread(Thread thread, Handler handler) {
         sUiThread = thread;
         sUiHandler = handler;
     }
 
-    public static void setGeckoThread(Thread thread, Handler handler) {
-        sGeckoThread = thread;
-        sGeckoHandler = handler;
-    }
-
     public static void setBackgroundThread(Thread thread) {
         sBackgroundThread = thread;
     }
 
     public static Thread getUiThread() {
         return sUiThread;
     }
 
     public static Handler getUiHandler() {
         return sUiHandler;
     }
 
     public static void postToUiThread(Runnable runnable) {
         sUiHandler.post(runnable);
     }
 
-    public static Thread getGeckoThread() {
-        return sGeckoThread;
-    }
-
-    public static Handler getGeckoHandler() {
-        return sGeckoHandler;
-    }
-
     public static Thread getBackgroundThread() {
         return sBackgroundThread;
     }
 
     public static Handler getBackgroundHandler() {
         return GeckoBackgroundThread.getHandler();
     }
 
@@ -97,17 +91,17 @@ public final class ThreadUtils {
         GeckoBackgroundThread.post(runnable);
     }
 
     public static void assertOnUiThread() {
         assertOnThread(getUiThread());
     }
 
     public static void assertOnGeckoThread() {
-        assertOnThread(getGeckoThread());
+        assertOnThread(sGeckoThread);
     }
 
     public static void assertOnBackgroundThread() {
         assertOnThread(getBackgroundThread());
     }
 
     public static void assertOnThread(Thread expectedThread) {
         Thread currentThread = Thread.currentThread();
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -798,23 +798,36 @@ Java_org_mozilla_gecko_GeckoAppShell_onF
   nsCOMPtr<nsIRunnable> runnable = new ExitFullScreenRunnable(jenv->NewGlobalRef(view));
   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 NULL;
+
+    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 NULL;
+        }
+    }
     return jenv->CallObjectMethod(queue, jNextMethod);
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_onSurfaceTextureFrameAvailable(JNIEnv* jenv, jclass, jobject surfaceTexture, jint id)
 {
   nsSurfaceTexture* st = nsSurfaceTexture::Find(id);
   if (!st) {