Commit 29b4c657 authored by Bo Liu's avatar Bo Liu Committed by Commit Bot

android: Delay removing binding

The effective process importance is computed from many inputs that can
change independently. An undesired case happens when one change causes a
binding to be dropped, but a closely-followed subsequent change adds the
binding back. This causes the process to temporarily drop the binding
which then can cause the OS to kill the process.

Use a herustic to delay calls to drop bindings to avoid situations like
this. To be conservative, only use apply this delay on the initial one
second of the process to avoid wider impact.

Bug: 1045059
Change-Id: I73462cf3c43140416871ef7abef3d77a419ca5a8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2031305Reviewed-by: default avatarssid <ssid@chromium.org>
Commit-Queue: Bo <boliu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#738324}
parent eb6c1eee
...@@ -750,7 +750,6 @@ public class ChildProcessConnection { ...@@ -750,7 +750,6 @@ public class ChildProcessConnection {
public void removeStrongBinding() { public void removeStrongBinding() {
assert isRunningOnLauncherThread(); assert isRunningOnLauncherThread();
if (!isConnected()) { if (!isConnected()) {
Log.w(TAG, "The connection is not bound for %d", getPid());
return; return;
} }
assert mStrongBindingCount > 0; assert mStrongBindingCount > 0;
...@@ -782,7 +781,6 @@ public class ChildProcessConnection { ...@@ -782,7 +781,6 @@ public class ChildProcessConnection {
public void removeModerateBinding() { public void removeModerateBinding() {
assert isRunningOnLauncherThread(); assert isRunningOnLauncherThread();
if (!isConnected()) { if (!isConnected()) {
Log.w(TAG, "The connection is not bound for %d", getPid());
return; return;
} }
assert mModerateBindingCount > 0; assert mModerateBindingCount > 0;
......
...@@ -61,6 +61,14 @@ public final class ChildProcessLauncherHelperImpl { ...@@ -61,6 +61,14 @@ public final class ChildProcessLauncherHelperImpl {
private static final String NUM_PRIVILEGED_SERVICES_KEY = private static final String NUM_PRIVILEGED_SERVICES_KEY =
"org.chromium.content.browser.NUM_PRIVILEGED_SERVICES"; "org.chromium.content.browser.NUM_PRIVILEGED_SERVICES";
// When decrementing the refcount on bindings, delay the decrement by this amount of time in
// case a new ref count is added in the mean time. This is a heuristic to avoid temporarily
// dropping bindings when inputs to calculating importance change independently.
private static final int REMOVE_BINDING_DELAY_MS = 500;
// To be conservative, only delay removing binding in the initial second of the process.
private static final int TIMEOUT_FOR_DELAY_BINDING_REMOVE_MS = 1000;
// Flag to check features after native is initialized. // Flag to check features after native is initialized.
private static boolean sCheckedFeatures; private static boolean sCheckedFeatures;
...@@ -183,6 +191,8 @@ public final class ChildProcessLauncherHelperImpl { ...@@ -183,6 +191,8 @@ public final class ChildProcessLauncherHelperImpl {
private long mNativeChildProcessLauncherHelper; private long mNativeChildProcessLauncherHelper;
private long mStartTimeMs;
// This is the current computed importance from all the inputs from setPriority. // This is the current computed importance from all the inputs from setPriority.
// The initial value is MODERATE since a newly created connection has moderate bindings. // The initial value is MODERATE since a newly created connection has moderate bindings.
private @ChildProcessImportance int mEffectiveImportance = ChildProcessImportance.MODERATE; private @ChildProcessImportance int mEffectiveImportance = ChildProcessImportance.MODERATE;
...@@ -448,6 +458,7 @@ public final class ChildProcessLauncherHelperImpl { ...@@ -448,6 +458,7 @@ public final class ChildProcessLauncherHelperImpl {
private void start() { private void start() {
mLauncher.start(true /* doSetupConnection */, true /* queueIfNoFreeConnection */); mLauncher.start(true /* doSetupConnection */, true /* queueIfNoFreeConnection */);
mStartTimeMs = System.currentTimeMillis();
} }
/** /**
...@@ -552,19 +563,28 @@ public final class ChildProcessLauncherHelperImpl { ...@@ -552,19 +563,28 @@ public final class ChildProcessLauncherHelperImpl {
if (mBindingManager != null) mBindingManager.rankingChanged(); if (mBindingManager != null) mBindingManager.rankingChanged();
} }
if (mEffectiveImportance != newEffectiveImportance) { if (mEffectiveImportance != newEffectiveImportance
switch (mEffectiveImportance) { && mEffectiveImportance != ChildProcessImportance.NORMAL) {
case ChildProcessImportance.NORMAL: final int existingEffectiveImportance = mEffectiveImportance;
// Nothing to remove. Runnable removeBindingRunnable = () -> {
break; switch (existingEffectiveImportance) {
case ChildProcessImportance.MODERATE: case ChildProcessImportance.NORMAL:
connection.removeModerateBinding(); // Nothing to remove.
break; break;
case ChildProcessImportance.IMPORTANT: case ChildProcessImportance.MODERATE:
connection.removeStrongBinding(); connection.removeModerateBinding();
break; break;
default: case ChildProcessImportance.IMPORTANT:
assert false; connection.removeStrongBinding();
break;
default:
assert false;
}
};
if (System.currentTimeMillis() - mStartTimeMs < TIMEOUT_FOR_DELAY_BINDING_REMOVE_MS) {
LauncherThread.postDelayed(removeBindingRunnable, REMOVE_BINDING_DELAY_MS);
} else {
removeBindingRunnable.run();
} }
} }
......
...@@ -25,6 +25,8 @@ import org.chromium.content_public.browser.RenderFrameHost; ...@@ -25,6 +25,8 @@ import org.chromium.content_public.browser.RenderFrameHost;
import org.chromium.content_public.browser.UiThreadTaskTraits; import org.chromium.content_public.browser.UiThreadTaskTraits;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsStatics; import org.chromium.content_public.browser.WebContentsStatics;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.content_shell.Shell; import org.chromium.content_shell.Shell;
import org.chromium.content_shell_apk.ChildProcessLauncherTestUtils; import org.chromium.content_shell_apk.ChildProcessLauncherTestUtils;
...@@ -385,8 +387,14 @@ public class WebContentsTest { ...@@ -385,8 +387,14 @@ public class WebContentsTest {
TestThreadUtils.runOnUiThreadBlocking(() -> webContents.onHide()); TestThreadUtils.runOnUiThreadBlocking(() -> webContents.onHide());
final ChildProcessConnection connection = getSandboxedChildProcessConnection(); final ChildProcessConnection connection = getSandboxedChildProcessConnection();
ChildProcessLauncherTestUtils.runOnLauncherThreadBlocking( // Need to poll here because there is an intentional delay for removing binding.
() -> Assert.assertFalse(connection.isModerateBindingBound())); CriteriaHelper.pollInstrumentationThread(new Criteria("Failed to remove moderate binding") {
@Override
public boolean isSatisfied() {
return ChildProcessLauncherTestUtils.runOnLauncherAndGetResult(
() -> !connection.isModerateBindingBound());
}
});
TestThreadUtils.runOnUiThreadBlocking( TestThreadUtils.runOnUiThreadBlocking(
() -> webContents.setImportance(ChildProcessImportance.MODERATE)); () -> webContents.setImportance(ChildProcessImportance.MODERATE));
...@@ -407,8 +415,13 @@ public class WebContentsTest { ...@@ -407,8 +415,13 @@ public class WebContentsTest {
() -> Assert.assertTrue(connection.isStrongBindingBound())); () -> Assert.assertTrue(connection.isStrongBindingBound()));
TestThreadUtils.runOnUiThreadBlocking(() -> webContents.onHide()); TestThreadUtils.runOnUiThreadBlocking(() -> webContents.onHide());
ChildProcessLauncherTestUtils.runOnLauncherThreadBlocking( CriteriaHelper.pollInstrumentationThread(new Criteria("Failed to remove strong binding") {
() -> Assert.assertFalse(connection.isStrongBindingBound())); @Override
public boolean isSatisfied() {
return ChildProcessLauncherTestUtils.runOnLauncherAndGetResult(
() -> !connection.isStrongBindingBound());
}
});
TestThreadUtils.runOnUiThreadBlocking(() -> webContents.onShow()); TestThreadUtils.runOnUiThreadBlocking(() -> webContents.onShow());
ChildProcessLauncherTestUtils.runOnLauncherThreadBlocking( ChildProcessLauncherTestUtils.runOnLauncherThreadBlocking(
......
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