Skip to content

Commit

Permalink
[Merge to M67] Propagate memory pressure to child processes.
Browse files Browse the repository at this point in the history
MemoryPressureMonitor (added in https://crrev.com/c/953166) polls
memory pressure when it gets CRITICAL to (1) lower memory usage by
repeatedly invoking pressure listeners and (2) sense (and notify
listeners) when pressure subsides.

However the polling only happens in the browser / WebView process,
because (1) ActivityManager.getMyMemoryState() can't be called from
isolated services, and (2) we want to poll only when Chrome is in
the foreground / there are WebView instances around.

This CL propagates pressure signals from the polling process to all
its services. That way in addition to getting pressure signals from
Android via ComponentCallbacks2, services also get signals from the
their main process.

(cherry picked from commit 9bd1a50)

Bug: 813909
Change-Id: Icef3b31106dcf432e6cdbdb0e1cdd84539dd690b
Reviewed-on: https://chromium-review.googlesource.com/992865
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: Bo <[email protected]>
Reviewed-by: agrieve <[email protected]>
Commit-Queue: Dmitry Skiba <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#550850}
Reviewed-on: https://chromium-review.googlesource.com/1016065
Reviewed-by: Dmitry Skiba <[email protected]>
Cr-Commit-Position: refs/branch-heads/3396@{#74}
Cr-Branched-From: 9ef2aa8-refs/heads/master@{#550428}
  • Loading branch information
Dmitry Skiba committed Apr 18, 2018
1 parent 1e01ecc commit e148251
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public static void addCallback(MemoryPressureCallback callback) {
* Removes previously added memory pressure callback.
* This method should be called only on ThreadUtils.UiThread.
*/
public void removeCallback(MemoryPressureCallback callback) {
public static void removeCallback(MemoryPressureCallback callback) {
sCallbacks.removeObserver(callback);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
* around).
*
* 2. Services (GPU, renderers) don't poll, instead they get additional pressure signals
* from the main process (this is TBD).
* from the main process.
*
* NOTE: except for notifyCurrentPressure() this class should only be used on UiThread
* as defined by ThreadUtils (which is Android main thread for Chrome, but can be
Expand Down Expand Up @@ -186,6 +186,15 @@ public void notifyPressure(@MemoryPressureLevel int pressure) {
}
}

/**
* Last pressure that was reported to MemoryPressureListener.
* Returns MemoryPressureLevel.NONE if nothing was reported yet.
*/
public @MemoryPressureLevel int getLastReportedPressure() {
ThreadUtils.assertOnUiThread();
return mLastReportedPressure;
}

private void notifyPressureOnUiThread(@MemoryPressureLevel int pressure) {
if (mIsInsideThrottlingInterval) {
// We've already reported during this interval. Save |pressure| and act on
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
import android.os.RemoteException;

import org.chromium.base.Log;
import org.chromium.base.MemoryPressureLevel;
import org.chromium.base.MemoryPressureListener;
import org.chromium.base.ThreadUtils;
import org.chromium.base.TraceEvent;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.memory.MemoryPressureCallback;

import java.util.List;

Expand Down Expand Up @@ -220,6 +224,8 @@ private static class ConnectionParams {
// Set to true once unbind() was called.
private boolean mUnbound;

private MemoryPressureCallback mMemoryPressureCallback;

public ChildProcessConnection(Context context, ComponentName serviceName, boolean bindToCaller,
boolean bindAsExternalService, Bundle serviceBundle) {
this(context, serviceName, bindToCaller, bindAsExternalService, serviceBundle,
Expand Down Expand Up @@ -415,6 +421,12 @@ private void onServiceConnectedOnLauncherThread(IBinder service) {

mServiceConnectComplete = true;

if (mMemoryPressureCallback == null) {
final MemoryPressureCallback callback = this ::onMemoryPressure;
ThreadUtils.postOnUiThread(() -> MemoryPressureListener.addCallback(callback));
mMemoryPressureCallback = callback;
}

// Run the setup if the connection parameters have already been provided. If
// not, doConnectionSetup() will be called from setupConnection().
if (mConnectionParams != null) {
Expand Down Expand Up @@ -512,6 +524,12 @@ protected void unbind() {
mInitialBinding.unbind();
// Note that we don't update the waived bound only state here as to preserve the state when
// disconnected.

if (mMemoryPressureCallback != null) {
final MemoryPressureCallback callback = mMemoryPressureCallback;
ThreadUtils.postOnUiThread(() -> MemoryPressureListener.removeCallback(callback));
mMemoryPressureCallback = null;
}
}

public boolean isInitialBindingBound() {
Expand Down Expand Up @@ -641,4 +659,17 @@ public boolean didOnServiceConnectedForTesting() {
protected Handler getLauncherHandler() {
return mLauncherHandler;
}

private void onMemoryPressure(@MemoryPressureLevel int pressure) {
mLauncherHandler.post(() -> onMemoryPressureOnLauncherThread(pressure));
}

private void onMemoryPressureOnLauncherThread(@MemoryPressureLevel int pressure) {
if (mService == null) return;
try {
mService.onMemoryPressure(pressure);
} catch (RemoteException ex) {
// Ignore
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import org.chromium.base.CommandLine;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.MemoryPressureLevel;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.MainDex;
import org.chromium.base.memory.MemoryPressureMonitor;

import java.util.List;
import java.util.concurrent.Semaphore;
Expand Down Expand Up @@ -129,6 +131,30 @@ public void crashIntentionallyForTesting() {
assert mServiceBound;
Process.killProcess(Process.myPid());
}

@Override
public void onMemoryPressure(@MemoryPressureLevel int pressure) {
// This method is called by the host process when the host process reports pressure
// to its native side. The key difference between the host process and its services is
// that the host process polls memory pressure when it gets CRITICAL, and periodically
// invokes pressure listeners until pressure subsides. (See MemoryPressureMonitor for
// more info.)
//
// Services don't poll, so this side-channel is used to notify services about memory
// pressure from the host process's POV.
//
// However, since both host process and services listen to ComponentCallbacks2, we
// can't be sure that the host process won't get better signals than their services.
// I.e. we need to watch out for a situation where a service gets CRITICAL, but the
// host process gets MODERATE - in this case we need to ignore MODERATE.
//
// So we're ignoring pressure from the host process if it's better than the last
// reported pressure. I.e. the host process can drive pressure up, but it'll go
// down only when we the service get a signal through ComponentCallbacks2.
if (pressure >= MemoryPressureMonitor.INSTANCE.getLastReportedPressure()) {
MemoryPressureMonitor.INSTANCE.notifyPressure(pressure);
}
}
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ interface IChildProcessService {

// Asks the child service to crash so that we can test the termination logic.
oneway void crashIntentionallyForTesting();

// Notifies about memory pressure. The argument is MemoryPressureLevel enum.
oneway void onMemoryPressure(int pressure);
}

0 comments on commit e148251

Please sign in to comment.