Commit e6857926 authored by Dmitry Skiba's avatar Dmitry Skiba Committed by Commit Bot

Preload native library when child process is bound.

Library loading is the first thing that happens when a renderer starts
navigating to a page. NativeLibraryPreloader is called during loading
and is responsible for majority of the loading time. However, unlike
library loading, preloading doesn't need to know command line of the
child process, and thus can happen earlier.

This CL preloads libraries when a child process is bound, which speeds
up navigation for warmed up renderers.

Bug: 783913
Change-Id: I3bffdc613717bfc61e038a2525cd96c5d0f0c0c3
Reviewed-on: https://chromium-review.googlesource.com/757561
Commit-Queue: Dmitry Skiba <dskiba@chromium.org>
Reviewed-by: default avatarBo <boliu@chromium.org>
Reviewed-by: default avatarRichard Coles <torne@chromium.org>
Cr-Commit-Position: refs/heads/master@{#517527}
parent 256f2b2f
......@@ -55,6 +55,7 @@ public class LibraryLoader {
// The singleton instance of NativeLibraryPreloader.
private static NativeLibraryPreloader sLibraryPreloader;
private static boolean sLibraryPreloaderCalled;
// The singleton instance of LibraryLoader.
private static volatile LibraryLoader sInstance;
......@@ -150,6 +151,38 @@ public class LibraryLoader {
}
}
/**
* Calls native library preloader (see {@link #setNativeLibraryPreloader}) with the app
* context. If there is no preloader set, this function does nothing.
* Preloader is called only once, so calling it explicitly via this method means
* that it won't be (implicitly) called during library loading.
*/
public void preloadNow() {
preloadNowOverrideApplicationContext(ContextUtils.getApplicationContext());
}
/**
* Similar to {@link #preloadNow}, but allows specifying app context to use.
*/
public void preloadNowOverrideApplicationContext(Context appContext) {
synchronized (sLock) {
if (!Linker.isUsed()) {
preloadAlreadyLocked(appContext);
}
}
}
private void preloadAlreadyLocked(Context appContext) {
try (TraceEvent te = TraceEvent.scoped("LibraryLoader.preloadAlreadyLocked")) {
// Preloader uses system linker, we shouldn't preload if Chromium linker is used.
assert !Linker.isUsed();
if (sLibraryPreloader != null && !sLibraryPreloaderCalled) {
mLibraryPreloaderStatus = sLibraryPreloader.loadLibrary(appContext);
sLibraryPreloaderCalled = true;
}
}
}
/**
* Checks if library is fully loaded and initialized.
*/
......@@ -361,9 +394,7 @@ public class LibraryLoader {
linker.finishLibraryLoad();
} else {
if (sLibraryPreloader != null) {
mLibraryPreloaderStatus = sLibraryPreloader.loadLibrary(appContext);
}
preloadAlreadyLocked(appContext);
// Load libraries using the system linker.
for (String library : NativeLibraries.LIBRARIES) {
try {
......
......@@ -8,6 +8,12 @@ import android.content.Context;
/**
* This is interface to preload the native library before calling System.loadLibrary.
*
* Preloading shouldn't call System.loadLibrary() or otherwise cause any Chromium
* code to be run, because it can be called before Chromium command line is known.
* It can however open the library via dlopen() or android_dlopen_ext() so that
* dlopen() later called by System.loadLibrary() becomes a noop. This is what the
* only subclass (MonochromeLibraryPreloader) is doing.
*/
public abstract class NativeLibraryPreloader {
public abstract int loadLibrary(Context context);
......
......@@ -43,6 +43,14 @@ public interface ChildProcessServiceDelegate {
*/
boolean loadNativeLibrary(Context hostContext);
/**
* Called when the delegate should preload the native library.
* Preloading is automatically done during library loading, but can also be called explicitly
* to speed up the loading. See {@link LibraryLoader.preloadNow}.
* @param hostContext The host context the library should be preloaded with (i.e. Chrome).
*/
void preloadNativeLibrary(Context hostContext);
/**
* Should return a map that associatesfile descriptors' IDs to keys.
* This is needed as at the moment we use 2 different stores for the FDs in native code:
......
......@@ -8,7 +8,9 @@ import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
......@@ -143,6 +145,7 @@ public class ChildProcessServiceImpl {
// The ClassLoader for the host context.
private ClassLoader mHostClassLoader;
private Context mHostContext;
/**
* Loads Chrome's native libraries and initializes a ChildProcessServiceImpl.
......@@ -152,6 +155,7 @@ public class ChildProcessServiceImpl {
// For sCreateCalled check.
public void create(final Context context, final Context hostContext) {
mHostClassLoader = hostContext.getClassLoader();
mHostContext = hostContext;
Log.i(TAG, "Creating new ChildProcessService pid=%d", Process.myPid());
if (sCreateCalled) {
throw new RuntimeException("Illegal child process reuse.");
......@@ -278,6 +282,9 @@ public class ChildProcessServiceImpl {
intent.getBooleanExtra(ChildProcessConstants.EXTRA_BIND_TO_CALLER, false);
mServiceBound = true;
mDelegate.onServiceBound(intent);
// Don't block bind() with any extra work, post it to the application thread instead.
new Handler(Looper.getMainLooper())
.post(() -> mDelegate.preloadNativeLibrary(mHostContext));
return mBinder;
}
......
......@@ -53,6 +53,15 @@ public class MultiprocessTestClientServiceDelegate implements ChildProcessServic
@Override
public void onDestroy() {}
@Override
public void preloadNativeLibrary(Context hostContext) {
try {
LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).preloadNow();
} catch (ProcessInitException pie) {
Log.w(TAG, "Unable to preload native libraries.", pie);
}
}
@Override
public boolean loadNativeLibrary(Context hostContext) {
try {
......
......@@ -87,6 +87,19 @@ public class ContentChildProcessServiceDelegate implements ChildProcessServiceDe
}
}
@Override
public void preloadNativeLibrary(Context hostContext) {
// This function can be called before command line is set. That is fine because
// preloading explicitly doesn't run any Chromium code, see NativeLibraryPreloader
// for more info.
try {
LibraryLoader libraryLoader = LibraryLoader.get(mLibraryProcessType);
libraryLoader.preloadNowOverrideApplicationContext(hostContext);
} catch (ProcessInitException e) {
Log.w(TAG, "Failed to preload native library", e);
}
}
@Override
public boolean loadNativeLibrary(Context hostContext) {
String processType =
......
......@@ -81,6 +81,15 @@ public class TestChildProcessService extends ChildProcessService {
}
}
@Override
public void preloadNativeLibrary(Context hostContext) {
try {
LibraryLoader.get(LibraryProcessType.PROCESS_CHILD).preloadNow();
} catch (ProcessInitException e) {
Log.e(TAG, "Failed to preload native library.", e);
}
}
@Override
public boolean loadNativeLibrary(Context hostContext) {
// Store the command line before loading the library to avoid an assert in CommandLine.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment