| Index: content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java
|
| index 1a8996c81a8bbc549361a8b4ac3df2fdc6125d39..b17d282b5f46a57d3cc27e2b682d9744f32b3fb0 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java
|
| @@ -42,6 +42,10 @@ import java.util.concurrent.ConcurrentHashMap;
|
|
|
| /**
|
| * This class provides the method to start/stop ChildProcess called by native.
|
| + *
|
| + * Note about threading. The threading here is complicated and not well documented.
|
| + * Code can run on these threads: UI, Launcher, async thread pool, binder, and one-off
|
| + * background threads.
|
| */
|
| @JNINamespace("content")
|
| public class ChildProcessLauncher {
|
| @@ -65,7 +69,8 @@ public class ChildProcessLauncher {
|
| private final boolean mInSandbox;
|
| // Each Allocator keeps a queue for the pending spawn data. Once a connection is free, we
|
| // dequeue the pending spawn data from the same allocator as the connection.
|
| - private final PendingSpawnQueue mPendingSpawnQueue = new PendingSpawnQueue();
|
| + // SHOULD BE ACCESSED WITH mConnectionLock.
|
| + private final Queue<SpawnData> mPendingSpawnQueue = new LinkedList<>();
|
|
|
| public ChildConnectionAllocator(boolean inSandbox, int numChildServices,
|
| String serviceClassName) {
|
| @@ -78,28 +83,32 @@ public class ChildProcessLauncher {
|
| mInSandbox = inSandbox;
|
| }
|
|
|
| - public ChildProcessConnection allocate(
|
| - Context context, ChildProcessConnection.DeathCallback deathCallback,
|
| - ChromiumLinkerParams chromiumLinkerParams,
|
| - boolean alwaysInForeground,
|
| - ChildProcessCreationParams creationParams) {
|
| + // Allocate or enqueue. If there are no free slots, return null and enqueue the spawn data.
|
| + public ChildProcessConnection allocate(SpawnData spawnData,
|
| + ChildProcessConnection.DeathCallback deathCallback,
|
| + ChromiumLinkerParams chromiumLinkerParams, boolean alwaysInForeground) {
|
| + assert spawnData.inSandbox() == mInSandbox;
|
| synchronized (mConnectionLock) {
|
| if (mFreeConnectionIndices.isEmpty()) {
|
| Log.d(TAG, "Ran out of services to allocate.");
|
| + if (!spawnData.forWarmUp()) {
|
| + mPendingSpawnQueue.add(spawnData);
|
| + }
|
| return null;
|
| }
|
| int slot = mFreeConnectionIndices.remove(0);
|
| assert mChildProcessConnections[slot] == null;
|
| - mChildProcessConnections[slot] = new ChildProcessConnectionImpl(context, slot,
|
| - mInSandbox, deathCallback, mChildClassName, chromiumLinkerParams,
|
| - alwaysInForeground, creationParams);
|
| + mChildProcessConnections[slot] = new ChildProcessConnectionImpl(spawnData.context(),
|
| + slot, mInSandbox, deathCallback, mChildClassName, chromiumLinkerParams,
|
| + alwaysInForeground, spawnData.getCreationParams());
|
| Log.d(TAG, "Allocator allocated a connection, sandbox: %b, slot: %d", mInSandbox,
|
| slot);
|
| return mChildProcessConnections[slot];
|
| }
|
| }
|
|
|
| - public void free(ChildProcessConnection connection) {
|
| + // Also return the first SpawnData in the pending queue, if any.
|
| + public SpawnData free(ChildProcessConnection connection) {
|
| synchronized (mConnectionLock) {
|
| int slot = connection.getServiceNumber();
|
| if (mChildProcessConnections[slot] != connection) {
|
| @@ -115,6 +124,7 @@ public class ChildProcessLauncher {
|
| Log.d(TAG, "Allocator freed a connection, sandbox: %b, slot: %d", mInSandbox,
|
| slot);
|
| }
|
| + return mPendingSpawnQueue.poll();
|
| }
|
| }
|
|
|
| @@ -124,10 +134,6 @@ public class ChildProcessLauncher {
|
| }
|
| }
|
|
|
| - public PendingSpawnQueue getPendingSpawnQueue() {
|
| - return mPendingSpawnQueue;
|
| - }
|
| -
|
| /** @return the count of connections managed by the allocator */
|
| @VisibleForTesting
|
| int allocatedConnectionsCountForTesting() {
|
| @@ -138,9 +144,24 @@ public class ChildProcessLauncher {
|
| ChildProcessConnection[] connectionArrayForTesting() {
|
| return mChildProcessConnections;
|
| }
|
| +
|
| + @VisibleForTesting
|
| + void enqueuePendingQueueForTesting(SpawnData spawnData) {
|
| + synchronized (mConnectionLock) {
|
| + mPendingSpawnQueue.add(spawnData);
|
| + }
|
| + }
|
| +
|
| + @VisibleForTesting
|
| + int pendingSpawnsCountForTesting() {
|
| + synchronized (mConnectionLock) {
|
| + return mPendingSpawnQueue.size();
|
| + }
|
| + }
|
| }
|
|
|
| - private static class PendingSpawnData {
|
| + private static class SpawnData {
|
| + private final boolean mForWarmup; // Do not queue up if failed.
|
| private final Context mContext;
|
| private final String[] mCommandLine;
|
| private final int mChildProcessId;
|
| @@ -150,15 +171,10 @@ public class ChildProcessLauncher {
|
| private final boolean mInSandbox;
|
| private final ChildProcessCreationParams mCreationParams;
|
|
|
| - private PendingSpawnData(
|
| - Context context,
|
| - String[] commandLine,
|
| - int childProcessId,
|
| - FileDescriptorInfo[] filesToBeMapped,
|
| - long clientContext,
|
| - int callbackType,
|
| - boolean inSandbox,
|
| - ChildProcessCreationParams creationParams) {
|
| + private SpawnData(boolean forWarmUp, Context context, String[] commandLine,
|
| + int childProcessId, FileDescriptorInfo[] filesToBeMapped, long clientContext,
|
| + int callbackType, boolean inSandbox, ChildProcessCreationParams creationParams) {
|
| + mForWarmup = forWarmUp;
|
| mContext = context;
|
| mCommandLine = commandLine;
|
| mChildProcessId = childProcessId;
|
| @@ -169,6 +185,10 @@ public class ChildProcessLauncher {
|
| mCreationParams = creationParams;
|
| }
|
|
|
| + private boolean forWarmUp() {
|
| + return mForWarmup;
|
| + }
|
| +
|
| private Context context() {
|
| return mContext;
|
| }
|
| @@ -195,37 +215,6 @@ public class ChildProcessLauncher {
|
| }
|
| }
|
|
|
| - private static class PendingSpawnQueue {
|
| - // The list of pending process spawn requests and its lock.
|
| - // Operations on this queue must be atomical w.r.t. free connections updates.
|
| - private Queue<PendingSpawnData> mPendingSpawns = new LinkedList<PendingSpawnData>();
|
| - final Object mPendingSpawnsLock = new Object();
|
| -
|
| - /**
|
| - * Queue up a spawn requests to be processed once a free service is available.
|
| - * Called when a spawn is requested while we are at the capacity.
|
| - */
|
| - public void enqueueLocked(final PendingSpawnData pendingSpawn) {
|
| - assert Thread.holdsLock(mPendingSpawnsLock);
|
| - mPendingSpawns.add(pendingSpawn);
|
| - }
|
| -
|
| - /**
|
| - * Pop the next request from the queue. Called when a free service is available.
|
| - * @return the next spawn request waiting in the queue.
|
| - */
|
| - public PendingSpawnData dequeueLocked() {
|
| - assert Thread.holdsLock(mPendingSpawnsLock);
|
| - return mPendingSpawns.poll();
|
| - }
|
| -
|
| - /** @return the count of pending spawns in the queue */
|
| - public int sizeLocked() {
|
| - assert Thread.holdsLock(mPendingSpawnsLock);
|
| - return mPendingSpawns.size();
|
| - }
|
| - }
|
| -
|
| // Service class for child process.
|
| // Map from package name to ChildConnectionAllocator.
|
| private static Map<String, ChildConnectionAllocator> sSandboxedChildConnectionAllocatorMap;
|
| @@ -356,18 +345,14 @@ public class ChildProcessLauncher {
|
| return sSandboxedChildConnectionAllocatorMap.get(packageName);
|
| }
|
|
|
| - /**
|
| - * Get the PendingSpawnQueue of the Allocator. Initialize the Allocator if needed.
|
| - */
|
| - private static PendingSpawnQueue getPendingSpawnQueue(Context context, String packageName,
|
| - boolean inSandbox) {
|
| + private static ChildConnectionAllocator getAllocatorForTesting(
|
| + Context context, String packageName, boolean inSandbox) {
|
| initConnectionAllocatorsIfNecessary(context, inSandbox, packageName);
|
| - return getConnectionAllocator(packageName, inSandbox).getPendingSpawnQueue();
|
| + return getConnectionAllocator(packageName, inSandbox);
|
| }
|
|
|
| - private static ChildProcessConnection allocateConnection(Context context, boolean inSandbox,
|
| - ChromiumLinkerParams chromiumLinkerParams, boolean alwaysInForeground,
|
| - ChildProcessCreationParams creationParams) {
|
| + private static ChildProcessConnection allocateConnection(SpawnData spawnData,
|
| + ChromiumLinkerParams chromiumLinkerParams, boolean alwaysInForeground) {
|
| ChildProcessConnection.DeathCallback deathCallback =
|
| new ChildProcessConnection.DeathCallback() {
|
| @Override
|
| @@ -379,12 +364,14 @@ public class ChildProcessLauncher {
|
| }
|
| }
|
| };
|
| - String packageName = creationParams != null ? creationParams.getPackageName()
|
| - : context.getPackageName();
|
| + final ChildProcessCreationParams creationParams = spawnData.getCreationParams();
|
| + final Context context = spawnData.context();
|
| + final boolean inSandbox = spawnData.inSandbox();
|
| + String packageName =
|
| + creationParams != null ? creationParams.getPackageName() : context.getPackageName();
|
| initConnectionAllocatorsIfNecessary(context, inSandbox, packageName);
|
| return getConnectionAllocator(packageName, inSandbox)
|
| - .allocate(context, deathCallback, chromiumLinkerParams, alwaysInForeground,
|
| - creationParams);
|
| + .allocate(spawnData, deathCallback, chromiumLinkerParams, alwaysInForeground);
|
| }
|
|
|
| private static boolean sLinkerInitialized;
|
| @@ -417,20 +404,23 @@ public class ChildProcessLauncher {
|
| }
|
| }
|
|
|
| - private static ChildProcessConnection allocateBoundConnection(Context context,
|
| - String[] commandLine, boolean inSandbox, boolean alwaysInForeground,
|
| - ChildProcessCreationParams creationParams,
|
| - ChildProcessConnection.StartCallback startCallback) {
|
| + private static ChildProcessConnection allocateBoundConnection(SpawnData spawnData,
|
| + boolean alwaysInForeground, ChildProcessConnection.StartCallback startCallback) {
|
| + final Context context = spawnData.context();
|
| + final String[] commandLine = spawnData.commandLine();
|
| + final boolean inSandbox = spawnData.inSandbox();
|
| + final ChildProcessCreationParams creationParams = spawnData.getCreationParams();
|
| ChromiumLinkerParams chromiumLinkerParams = getLinkerParamsForNewConnection();
|
| - ChildProcessConnection connection = allocateConnection(
|
| - context, inSandbox, chromiumLinkerParams, alwaysInForeground, creationParams);
|
| + ChildProcessConnection connection =
|
| + allocateConnection(spawnData, chromiumLinkerParams, alwaysInForeground);
|
| if (connection != null) {
|
| connection.start(commandLine, startCallback);
|
|
|
| String packageName = creationParams != null ? creationParams.getPackageName()
|
| - : context.getPackageName();
|
| - if (inSandbox && !getConnectionAllocator(packageName, inSandbox)
|
| - .isFreeConnectionAvailable()) {
|
| + : context.getPackageName();
|
| + if (inSandbox
|
| + && !getConnectionAllocator(packageName, inSandbox)
|
| + .isFreeConnectionAvailable()) {
|
| // Proactively releases all the moderate bindings once all the sandboxed services
|
| // are allocated, which will be very likely to have some of them killed by OOM
|
| // killer.
|
| @@ -456,7 +446,7 @@ public class ChildProcessLauncher {
|
| ThreadUtils.postOnUiThreadDelayed(new Runnable() {
|
| @Override
|
| public void run() {
|
| - final PendingSpawnData pendingSpawn = freeConnectionAndDequeuePending(conn);
|
| + final SpawnData pendingSpawn = freeConnectionAndDequeuePending(conn);
|
| if (pendingSpawn != null) {
|
| new Thread(new Runnable() {
|
| @Override
|
| @@ -472,15 +462,11 @@ public class ChildProcessLauncher {
|
| }, FREE_CONNECTION_DELAY_MILLIS);
|
| }
|
|
|
| - private static PendingSpawnData freeConnectionAndDequeuePending(ChildProcessConnection conn) {
|
| + private static SpawnData freeConnectionAndDequeuePending(ChildProcessConnection conn) {
|
| ChildConnectionAllocator allocator = getConnectionAllocator(
|
| conn.getPackageName(), conn.isInSandbox());
|
| assert allocator != null;
|
| - PendingSpawnQueue pendingSpawnQueue = allocator.getPendingSpawnQueue();
|
| - synchronized (pendingSpawnQueue.mPendingSpawnsLock) {
|
| - allocator.free(conn);
|
| - return pendingSpawnQueue.dequeueLocked();
|
| - }
|
| + return allocator.free(conn);
|
| }
|
|
|
| // Represents an invalid process handle; same as base/process/process.h kNullProcessHandle.
|
| @@ -612,8 +598,12 @@ public class ChildProcessLauncher {
|
| }
|
| }
|
| };
|
| - sSpareSandboxedConnection = allocateBoundConnection(context, null, true, false,
|
| - params, startCallback);
|
| + SpawnData spawnData = new SpawnData(true /* forWarmUp*/, context,
|
| + null /* commandLine */, -1 /* child process id */,
|
| + null /* filesToBeMapped */, 0 /* clientContext */,
|
| + CALLBACK_FOR_RENDERER_PROCESS, true /* inSandbox */, params);
|
| + sSpareSandboxedConnection = allocateBoundConnection(
|
| + spawnData, false /* alwaysInForeground */, startCallback);
|
| }
|
| }
|
| }
|
| @@ -722,8 +712,6 @@ public class ChildProcessLauncher {
|
| if (allocatedConnection == null) {
|
| boolean alwaysInForeground = false;
|
| if (callbackType == CALLBACK_FOR_GPU_PROCESS) alwaysInForeground = true;
|
| - PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue(
|
| - context, packageName, inSandbox);
|
| ChildProcessConnection.StartCallback startCallback =
|
| new ChildProcessConnection.StartCallback() {
|
| @Override
|
| @@ -747,17 +735,14 @@ public class ChildProcessLauncher {
|
| });
|
| }
|
| };
|
| - synchronized (pendingSpawnQueue.mPendingSpawnsLock) {
|
| - allocatedConnection = allocateBoundConnection(
|
| - context, commandLine, inSandbox, alwaysInForeground, creationParams,
|
| - startCallback);
|
| - if (allocatedConnection == null) {
|
| - Log.d(TAG, "Allocation of new service failed. Queuing up pending spawn.");
|
| - pendingSpawnQueue.enqueueLocked(new PendingSpawnData(context, commandLine,
|
| - childProcessId, filesToBeMapped, clientContext,
|
| - callbackType, inSandbox, creationParams));
|
| - return null;
|
| - }
|
| +
|
| + SpawnData spawnData = new SpawnData(false /* forWarmUp */, context, commandLine,
|
| + childProcessId, filesToBeMapped, clientContext, callbackType, inSandbox,
|
| + creationParams);
|
| + allocatedConnection =
|
| + allocateBoundConnection(spawnData, alwaysInForeground, startCallback);
|
| + if (allocatedConnection == null) {
|
| + return null;
|
| }
|
| }
|
|
|
| @@ -893,14 +878,21 @@ public class ChildProcessLauncher {
|
| @VisibleForTesting
|
| static ChildProcessConnection allocateBoundConnectionForTesting(Context context,
|
| ChildProcessCreationParams creationParams) {
|
| - return allocateBoundConnection(context, null, true, false, creationParams, null);
|
| + return allocateBoundConnection(
|
| + new SpawnData(false /* forWarmUp */, context, null /* commandLine */,
|
| + 0 /* childProcessId */, null /* filesToBeMapped */, 0 /* clientContext */,
|
| + CALLBACK_FOR_RENDERER_PROCESS, true /* inSandbox */, creationParams),
|
| + false /* alwaysInForeground */, null);
|
| }
|
|
|
| @VisibleForTesting
|
| static ChildProcessConnection allocateConnectionForTesting(Context context,
|
| ChildProcessCreationParams creationParams) {
|
| return allocateConnection(
|
| - context, true, getLinkerParamsForNewConnection(), false, creationParams);
|
| + new SpawnData(false /* forWarmUp */, context, null /* commandLine */,
|
| + 0 /* childProcessId */, null /* filesToBeMapped */, 0 /* clientContext */,
|
| + CALLBACK_FOR_RENDERER_PROCESS, true /* inSandbox */, creationParams),
|
| + getLinkerParamsForNewConnection(), false);
|
| }
|
|
|
| /**
|
| @@ -911,13 +903,11 @@ public class ChildProcessLauncher {
|
| ChildProcessCreationParams creationParams, boolean inSandbox) {
|
| String packageName = creationParams != null ? creationParams.getPackageName()
|
| : context.getPackageName();
|
| - PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue(context,
|
| - packageName, inSandbox);
|
| - synchronized (pendingSpawnQueue.mPendingSpawnsLock) {
|
| - pendingSpawnQueue.enqueueLocked(new PendingSpawnData(context, commandLine, 1,
|
| - new FileDescriptorInfo[0], 0, CALLBACK_FOR_RENDERER_PROCESS, true,
|
| - creationParams));
|
| - }
|
| + ChildConnectionAllocator allocator =
|
| + getAllocatorForTesting(context, packageName, inSandbox);
|
| + allocator.enqueuePendingQueueForTesting(new SpawnData(false /* forWarmUp*/, context,
|
| + commandLine, 1, new FileDescriptorInfo[0], 0, CALLBACK_FOR_RENDERER_PROCESS, true,
|
| + creationParams));
|
| }
|
|
|
| /**
|
| @@ -935,8 +925,7 @@ public class ChildProcessLauncher {
|
| * @return gets the service connection array for a specific package name.
|
| */
|
| @VisibleForTesting
|
| - static ChildProcessConnection[] getSandboxedConnectionArrayForTesting(
|
| - String packageName) {
|
| + static ChildProcessConnection[] getSandboxedConnectionArrayForTesting(String packageName) {
|
| return sSandboxedChildConnectionAllocatorMap.get(packageName).connectionArrayForTesting();
|
| }
|
|
|
| @@ -953,12 +942,11 @@ public class ChildProcessLauncher {
|
| * @return the count of pending spawns in the queue.
|
| */
|
| @VisibleForTesting
|
| - static int pendingSpawnsCountForTesting(Context context, String packageName,
|
| - boolean inSandbox) {
|
| - PendingSpawnQueue pendingSpawnQueue = getPendingSpawnQueue(context, packageName, inSandbox);
|
| - synchronized (pendingSpawnQueue.mPendingSpawnsLock) {
|
| - return pendingSpawnQueue.sizeLocked();
|
| - }
|
| + static int pendingSpawnsCountForTesting(
|
| + Context context, String packageName, boolean inSandbox) {
|
| + ChildConnectionAllocator allocator =
|
| + getAllocatorForTesting(context, packageName, inSandbox);
|
| + return allocator.pendingSpawnsCountForTesting();
|
| }
|
|
|
| /**
|
|
|