Bug 1404144 - 3. Add flag to preload child process during main GeckoThread startup; r?snorp draft
authorJim Chen <nchen@mozilla.com>
Wed, 04 Oct 2017 22:28:43 -0400
changeset 675259 49be3fd093b7c63d3b364674bf61a59e147d5861
parent 675258 871db755e3a21a09d8c22a55bcf508c95d32940c
child 675260 a02a5c7fd6b90b377049a2150d38a50c148426cd
push id83082
push userbmo:nchen@mozilla.com
push dateThu, 05 Oct 2017 02:29:11 +0000
reviewerssnorp
bugs1404144
milestone58.0a1
Bug 1404144 - 3. Add flag to preload child process during main GeckoThread startup; r?snorp We need to wait for GeckoThread to load the Gecko libs in the main process before we can launch any child processes, so that the child process doesn't try to extract libs, which can conflict with any extraction going on in the main process. MozReview-Commit-ID: 2gUd2R1TUBI
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1126,20 +1126,20 @@ public abstract class GeckoApp extends G
             // also happen if we're not the first activity to run within a session.
             mIsRestoringActivity = true;
             Telemetry.addToHistogram("FENNEC_RESTORING_ACTIVITY", 1);
 
         } else {
             final String action = intent.getAction();
             final String args = GeckoApplication.addDefaultGeckoArgs(
                     intent.getStringExtra("args"));
+            final int flags = ACTION_DEBUG.equals(action) ? GeckoThread.FLAG_DEBUGGING : 0;
 
             sAlreadyLoaded = true;
-            GeckoThread.initMainProcess(/* profile */ null, args,
-                                        /* debugging */ ACTION_DEBUG.equals(action));
+            GeckoThread.initMainProcess(/* profile */ null, args, flags);
 
             // Speculatively pre-fetch the profile in the background.
             ThreadUtils.postToBackgroundThread(new Runnable() {
                 @Override
                 public void run() {
                     getProfile();
                 }
             });
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
@@ -3,16 +3,17 @@
  * 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;
 
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.mozglue.GeckoLoader;
+import org.mozilla.gecko.process.GeckoProcessManager;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.FileUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Handler;
@@ -111,65 +112,68 @@ public class GeckoThread extends Thread 
     private static MessageQueue msgQueue;
     @WrapForJNI
     private static int uiThreadId;
 
     private boolean mInitialized;
     private String[] mArgs;
 
     // Main process parameters
+    public static final int FLAG_DEBUGGING = 1; // Debugging mode.
+    public static final int FLAG_PRELOAD_CHILD = 2; // Preload child during main thread start.
+
     private GeckoProfile mProfile;
     private String mExtraArgs;
-    private boolean mDebugging;
+    private int mFlags;
 
     // Child process parameters
     private int mCrashFileDescriptor = -1;
     private int mIPCFileDescriptor = -1;
 
     GeckoThread() {
         setName("Gecko");
     }
 
     @WrapForJNI
     private static boolean isChildProcess() {
         return INSTANCE.mIPCFileDescriptor != -1;
     }
 
     private synchronized boolean init(final GeckoProfile profile, final String[] args,
-                                      final String extraArgs, final boolean debugging,
+                                      final String extraArgs, final int flags,
                                       final int crashFd, final int ipcFd) {
         ThreadUtils.assertOnUiThread();
         uiThreadId = android.os.Process.myTid();
 
         if (mInitialized) {
             return false;
         }
 
         mProfile = profile;
         mArgs = args;
         mExtraArgs = extraArgs;
-        mDebugging = debugging;
+        mFlags = flags;
         mCrashFileDescriptor = crashFd;
         mIPCFileDescriptor = ipcFd;
 
         mInitialized = true;
         notifyAll();
         return true;
     }
 
     public static boolean initMainProcess(final GeckoProfile profile, final String extraArgs,
-                                          final boolean debugging) {
-        return INSTANCE.init(profile, /* args */ null, extraArgs, debugging,
+                                          final int flags) {
+        return INSTANCE.init(profile, /* args */ null, extraArgs, flags,
                                  /* crashFd */ -1, /* ipcFd */ -1);
     }
 
     public static boolean initChildProcess(final String[] args, final int crashFd,
                                            final int ipcFd) {
         return INSTANCE.init(/* profile */ null, args, /* extraArgs */ null,
-                                 /* debugging */ false, crashFd, ipcFd);
+                                 /* flags */ 0, crashFd, ipcFd);
     }
 
     private static boolean canUseProfile(final Context context, final GeckoProfile profile,
                                          final String profileName, final File profileDir) {
         if (profileDir != null && !profileDir.isDirectory()) {
             return false;
         }
 
@@ -214,17 +218,17 @@ public class GeckoThread extends Thread 
 
         if (profile != null) {
             // We already have a compatible profile.
             return true;
         }
 
         // We haven't initialized yet; okay to initialize now.
         return initMainProcess(GeckoProfile.get(context, profileName, profileDir),
-                               args, /* debugging */ false);
+                               args, /* flags */ 0);
     }
 
     public static boolean launch() {
         ThreadUtils.assertOnUiThread();
 
         if (checkAndSetState(State.INITIAL, State.LAUNCHED)) {
             INSTANCE.start();
             return true;
@@ -359,38 +363,48 @@ public class GeckoThread extends Thread 
                 // Keep this IdleHandler
                 return true;
             }
         };
         Looper.myQueue().addIdleHandler(idleHandler);
 
         initGeckoEnvironment();
 
+        if ((mFlags & FLAG_PRELOAD_CHILD) != 0) {
+            ThreadUtils.postToBackgroundThread(new Runnable() {
+                @Override
+                public void run() {
+                    // Preload the content ("tab") child process.
+                    GeckoProcessManager.getInstance().preload("tab");
+                }
+            });
+        }
+
         // Wait until initialization before calling Gecko entry point.
         synchronized (this) {
             while (!mInitialized) {
                 try {
                     wait();
                 } catch (final InterruptedException e) {
                 }
             }
         }
 
         final String[] args = isChildProcess() ? mArgs : getMainProcessArgs();
 
-        if (mDebugging) {
+        if ((mFlags & FLAG_DEBUGGING) != 0) {
             try {
                 Thread.sleep(5 * 1000 /* 5 seconds */);
             } catch (final InterruptedException e) {
             }
         }
 
         Log.w(LOGTAG, "zerdatime " + SystemClock.elapsedRealtime() + " - runGecko");
 
-        if (mDebugging) {
+        if ((mFlags & FLAG_DEBUGGING) != 0) {
             Log.i(LOGTAG, "RunGecko - args = " + TextUtils.join(" ", args));
         }
 
         // And go.
         GeckoLoader.nativeRun(args, mCrashFileDescriptor, mIPCFileDescriptor);
 
         // And... we're done.
         final boolean restarting = isState(State.RESTARTING);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -15,16 +15,17 @@ import java.util.Set;
 import org.mozilla.gecko.annotation.ReflectionTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.mozglue.JNIObject;
 import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
+import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.net.Uri;
@@ -457,41 +458,44 @@ public class GeckoView extends LayerView
     }
 
     /**
      * Preload GeckoView by starting Gecko in the background, if Gecko is not already running.
      *
      * @param context Activity or Application Context for starting GeckoView.
      */
     public static void preload(final Context context) {
-        preload(context, /* geckoArgs */ null);
+        preload(context, /* geckoArgs */ null, /* multiprocess */ false);
     }
 
     /**
      * Preload GeckoView by starting Gecko with the specified arguments in the background,
      * if Gecko is not already running.
      *
      * @param context Activity or Application Context for starting GeckoView.
-     * @param geckoArgs Arguments to be passed to Gecko, if Gecko is not already running
+     * @param geckoArgs Arguments to be passed to Gecko, if Gecko is not already running.
+     * @param multiprocess True if child process in multiprocess mode should be preloaded.
      */
-    public static void preload(final Context context, final String geckoArgs) {
+    public static void preload(final Context context, final String geckoArgs,
+                               final boolean multiprocess) {
         final Context appContext = context.getApplicationContext();
         if (GeckoAppShell.getApplicationContext() == null) {
             GeckoAppShell.setApplicationContext(appContext);
         }
 
-        if (GeckoThread.initMainProcess(/* profile */ null,
-                                        geckoArgs,
-                                        /* debugging */ false)) {
+        final int flags = multiprocess ? GeckoThread.FLAG_PRELOAD_CHILD : 0;
+        if (GeckoThread.initMainProcess(/* profile */ null, geckoArgs, flags)) {
             GeckoThread.launch();
         }
     }
 
     private void init(final Context context, final GeckoViewSettings settings) {
-        preload(context);
+        final boolean multiprocess = settings != null &&
+                                     settings.getBoolean(GeckoViewSettings.USE_MULTIPROCESS);
+        preload(context, /* geckoArgs */ null, multiprocess);
 
         initializeView();
         mListener.registerListeners();
 
         if (settings == null) {
             mSettings = new GeckoViewSettings(getEventDispatcher());
         } else {
             mSettings = settings;