Bug 1339160 - 1. Allow GeckoThread to launch without being initialized; r=snorp

When GeckoThread is launched without being initialized, it will load all
Gecko libs and then wait until it is initialized, before calling the
Gecko entry point. This allows us to preload Gecko libs without actually
running Gecko.
This commit is contained in:
Jim Chen 2017-02-15 17:12:56 -05:00
Родитель e9e3761a4a
Коммит 94bd2e7d8e
5 изменённых файлов: 111 добавлений и 99 удалений

Просмотреть файл

@ -1220,8 +1220,8 @@ public abstract class GeckoApp
final String args = intent.getStringExtra("args"); final String args = intent.getStringExtra("args");
sAlreadyLoaded = true; sAlreadyLoaded = true;
GeckoThread.init(/* profile */ null, args, action, GeckoThread.initMainProcess(/* profile */ null, args,
/* debugging */ ACTION_DEBUG.equals(action)); /* debugging */ ACTION_DEBUG.equals(action));
// Speculatively pre-fetch the profile in the background. // Speculatively pre-fetch the profile in the background.
ThreadUtils.postToBackgroundThread(new Runnable() { ThreadUtils.postToBackgroundThread(new Runnable() {

Просмотреть файл

@ -150,7 +150,8 @@ public class GeckoService extends Service {
throw new IllegalArgumentException("Intent must specify profile."); throw new IllegalArgumentException("Intent must specify profile.");
} }
if (!GeckoThread.initWithProfile(profileName, profileDir != null ? new File(profileDir) : null)) { if (!GeckoThread.initMainProcessWithProfile(
profileName, profileDir != null ? new File(profileDir) : null)) {
Log.w(LOGTAG, "Ignoring due to profile mismatch: " + Log.w(LOGTAG, "Ignoring due to profile mismatch: " +
profileName + " [" + profileDir + ']'); profileName + " [" + profileDir + ']');

Просмотреть файл

@ -83,7 +83,6 @@ public final class GeckoProfile {
private final String mName; private final String mName;
private final File mMozillaDir; private final File mMozillaDir;
private final Context mApplicationContext;
private Object mData; private Object mData;
@ -320,7 +319,6 @@ public final class GeckoProfile {
throw new IllegalArgumentException("Custom profile must have a directory"); throw new IllegalArgumentException("Custom profile must have a directory");
} }
mApplicationContext = context.getApplicationContext();
mName = profileName; mName = profileName;
mMozillaDir = GeckoProfileDirectories.getMozillaDirectory(context); mMozillaDir = GeckoProfileDirectories.getMozillaDirectory(context);

Просмотреть файл

@ -125,52 +125,64 @@ public class GeckoThread extends Thread {
} }
}; };
private static GeckoThread sGeckoThread; private static final GeckoThread INSTANCE = new GeckoThread();
@WrapForJNI @WrapForJNI
private static final ClassLoader clsLoader = GeckoThread.class.getClassLoader(); private static final ClassLoader clsLoader = GeckoThread.class.getClassLoader();
@WrapForJNI @WrapForJNI
private static MessageQueue msgQueue; private static MessageQueue msgQueue;
private boolean mInitialized;
private String[] mArgs;
// Main process parameters
private GeckoProfile mProfile; private GeckoProfile mProfile;
private String mExtraArgs;
private boolean mDebugging;
private final String mArgs; // Child process parameters
private final String mAction;
private final boolean mDebugging;
private String[] mChildProcessArgs;
private int mCrashFileDescriptor; private int mCrashFileDescriptor;
private int mIPCFileDescriptor; private int mIPCFileDescriptor;
GeckoThread(GeckoProfile profile, String args, String action, boolean debugging) { GeckoThread() {
mProfile = profile;
mArgs = args;
mAction = action;
mDebugging = debugging;
mChildProcessArgs = null;
mCrashFileDescriptor = -1;
mIPCFileDescriptor = -1;
setName("Gecko"); setName("Gecko");
} }
public static boolean init(GeckoProfile profile, String args, String action, boolean debugging) { private boolean isChildProcess() {
ThreadUtils.assertOnUiThread(); return mIPCFileDescriptor != -1;
if (isState(State.INITIAL) && sGeckoThread == null) {
sGeckoThread = new GeckoThread(profile, args, action, debugging);
return true;
}
return false;
} }
public static boolean initChildProcess(GeckoProfile profile, String[] args, int crashFd, int ipcFd, boolean debugging) { private synchronized boolean init(final GeckoProfile profile, final String[] args,
if (init(profile, null, null, debugging)) { final String extraArgs, final boolean debugging,
sGeckoThread.mChildProcessArgs = args; final int crashFd, final int ipcFd) {
sGeckoThread.mCrashFileDescriptor = crashFd; ThreadUtils.assertOnUiThread();
sGeckoThread.mIPCFileDescriptor = ipcFd;
return true; if (mInitialized) {
return false;
} }
return false;
mProfile = profile;
mArgs = args;
mExtraArgs = extraArgs;
mDebugging = debugging;
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,
/* 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);
} }
private static boolean canUseProfile(final Context context, final GeckoProfile profile, private static boolean canUseProfile(final Context context, final GeckoProfile profile,
@ -203,7 +215,8 @@ public class GeckoThread extends Thread {
profileName, profileDir); profileName, profileDir);
} }
public static boolean initWithProfile(final String profileName, final File profileDir) { public static boolean initMainProcessWithProfile(final String profileName,
final File profileDir) {
if (profileName == null) { if (profileName == null) {
throw new IllegalArgumentException("Null profile name"); throw new IllegalArgumentException("Null profile name");
} }
@ -222,14 +235,15 @@ public class GeckoThread extends Thread {
} }
// We haven't initialized yet; okay to initialize now. // We haven't initialized yet; okay to initialize now.
return init(GeckoProfile.get(context, profileName, profileDir), return initMainProcess(GeckoProfile.get(context, profileName, profileDir),
/* args */ null, /* action */ null, /* debugging */ false); /* args */ null, /* debugging */ false);
} }
public static boolean launch() { public static boolean launch() {
ThreadUtils.assertOnUiThread(); ThreadUtils.assertOnUiThread();
if (checkAndSetState(State.INITIAL, State.LAUNCHED)) { if (checkAndSetState(State.INITIAL, State.LAUNCHED)) {
sGeckoThread.start(); INSTANCE.start();
return true; return true;
} }
return false; return false;
@ -397,7 +411,7 @@ public class GeckoThread extends Thread {
GeckoLoader.loadGeckoLibs(context, resourcePath); GeckoLoader.loadGeckoLibs(context, resourcePath);
} }
private static String initGeckoEnvironment() { private static void initGeckoEnvironment() {
final Context context = GeckoAppShell.getApplicationContext(); final Context context = GeckoAppShell.getApplicationContext();
GeckoLoader.loadMozGlue(context); GeckoLoader.loadMozGlue(context);
setState(State.MOZGLUE_READY); setState(State.MOZGLUE_READY);
@ -437,48 +451,41 @@ public class GeckoThread extends Thread {
} }
setState(State.LIBS_READY); setState(State.LIBS_READY);
return resourcePath;
} }
private void addCustomProfileArg(String args, ArrayList<String> list) { private String[] getMainProcessArgs() {
// Make sure a profile exists.
final GeckoProfile profile = getProfile();
profile.getDir(); // call the lazy initializer
boolean needsProfile = true;
if (args != null) {
StringTokenizer st = new StringTokenizer(args);
while (st.hasMoreTokens()) {
String token = st.nextToken();
if ("-P".equals(token) || "-profile".equals(token)) {
needsProfile = false;
}
list.add(token);
}
}
// If args don't include the profile, make sure it's included.
if (args == null || needsProfile) {
if (profile.isCustomProfile()) {
list.add("-profile");
list.add(profile.getDir().getAbsolutePath());
} else {
list.add("-P");
list.add(profile.getName());
}
}
}
private String[] getGeckoArgs(final String apkPath) {
// argv[0] is the program name, which for us is the package name.
final Context context = GeckoAppShell.getApplicationContext(); final Context context = GeckoAppShell.getApplicationContext();
final ArrayList<String> args = new ArrayList<String>(); final ArrayList<String> args = new ArrayList<String>();
// argv[0] is the program name, which for us is the package name.
args.add(context.getPackageName()); args.add(context.getPackageName());
args.add("-greomni"); args.add("-greomni");
args.add(apkPath); args.add(context.getPackageResourcePath());
addCustomProfileArg(mArgs, args); final GeckoProfile profile = getProfile();
if (profile.isCustomProfile()) {
args.add("-profile");
args.add(profile.getDir().getAbsolutePath());
} else {
profile.getDir(); // Make sure the profile dir exists.
args.add("-P");
args.add(profile.getName());
}
if (mExtraArgs != null) {
final StringTokenizer st = new StringTokenizer(mExtraArgs);
while (st.hasMoreTokens()) {
final String token = st.nextToken();
if ("-P".equals(token) || "-profile".equals(token)) {
// Skip -P and -profile arguments because we added them above.
if (st.hasMoreTokens()) {
st.nextToken();
}
continue;
}
args.add(token);
}
}
// In un-official builds, we want to load Javascript resources fresh // In un-official builds, we want to load Javascript resources fresh
// with each build. In official builds, the startup cache is purged by // with each build. In official builds, the startup cache is purged by
@ -495,20 +502,20 @@ public class GeckoThread extends Thread {
} }
public static GeckoProfile getActiveProfile() { public static GeckoProfile getActiveProfile() {
if (sGeckoThread == null) { return INSTANCE.getProfile();
return null;
}
final GeckoProfile profile = sGeckoThread.mProfile;
if (profile != null) {
return profile;
}
return sGeckoThread.getProfile();
} }
public synchronized GeckoProfile getProfile() { public synchronized GeckoProfile getProfile() {
if (!mInitialized) {
return null;
}
if (isChildProcess()) {
throw new UnsupportedOperationException(
"Cannot access profile from child process");
}
if (mProfile == null) { if (mProfile == null) {
final Context context = GeckoAppShell.getApplicationContext(); final Context context = GeckoAppShell.getApplicationContext();
mProfile = GeckoProfile.initFromArgs(context, mArgs); mProfile = GeckoProfile.initFromArgs(context, mExtraArgs);
} }
return mProfile; return mProfile;
} }
@ -536,20 +543,7 @@ public class GeckoThread extends Thread {
}; };
Looper.myQueue().addIdleHandler(idleHandler); Looper.myQueue().addIdleHandler(idleHandler);
if (mDebugging) { initGeckoEnvironment();
try {
Thread.sleep(5 * 1000 /* 5 seconds */);
} catch (final InterruptedException e) {
}
}
final String[] args;
if (mChildProcessArgs != null) {
initGeckoEnvironment();
args = mChildProcessArgs;
} else {
args = getGeckoArgs(initGeckoEnvironment());
}
// This can only happen after the call to initGeckoEnvironment // This can only happen after the call to initGeckoEnvironment
// above, because otherwise the JNI code hasn't been loaded yet. // above, because otherwise the JNI code hasn't been loaded yet.
@ -559,11 +553,30 @@ public class GeckoThread extends Thread {
} }
}); });
// 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) {
try {
Thread.sleep(5 * 1000 /* 5 seconds */);
} catch (final InterruptedException e) {
}
}
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - runGecko"); Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - runGecko");
final GeckoAppShell.GeckoInterface gi = GeckoAppShell.getGeckoInterface(); final GeckoAppShell.GeckoInterface gi = GeckoAppShell.getGeckoInterface();
if (gi == null || !gi.isOfficial()) { if (gi == null || !gi.isOfficial()) {
Log.i(LOGTAG, "RunGecko - args = " + args); Log.i(LOGTAG, "RunGecko - args = " + TextUtils.join(" ", args));
} }
// And go. // And go.

Просмотреть файл

@ -74,7 +74,7 @@ public class GeckoServiceChildProcess extends Service {
public void run() { public void run() {
GeckoAppShell.ensureCrashHandling(); GeckoAppShell.ensureCrashHandling();
GeckoAppShell.setApplicationContext(getApplicationContext()); GeckoAppShell.setApplicationContext(getApplicationContext());
if (GeckoThread.initChildProcess(null, args, crashReporterFd, ipcFd, false)) { if (GeckoThread.initChildProcess(args, crashReporterFd, ipcFd)) {
GeckoThread.launch(); GeckoThread.launch();
} }
} }