| Index: content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java b/content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java
|
| index 99f41223869821f5ca8c04537e8084c2ec9336f6..9281f3afb1bd91296bfe9efad795a5103403ac6d 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java
|
| @@ -11,11 +11,10 @@ import org.chromium.base.Log;
|
| import org.chromium.base.VisibleForTesting;
|
| import org.chromium.base.process_launcher.ChildProcessCreationParams;
|
|
|
| -import javax.annotation.concurrent.GuardedBy;
|
| -
|
| /**
|
| * ManagedChildProcessConnection is a connection to a child service that can hold several bindings
|
| * to the service so it can be more or less agressively protected against OOM.
|
| + * Accessed from the launcher thread. (but for isOomProtectedOrWasWhenDied()).
|
| */
|
| public class ManagedChildProcessConnection extends BaseChildProcessConnection {
|
| private static final String TAG = "ManChildProcessConn";
|
| @@ -25,45 +24,40 @@ public class ManagedChildProcessConnection extends BaseChildProcessConnection {
|
| public BaseChildProcessConnection create(Context context, int number, boolean sandboxed,
|
| DeathCallback deathCallback, String serviceClassName,
|
| Bundle childProcessCommonParameters, ChildProcessCreationParams creationParams) {
|
| + assert LauncherThread.runningOnLauncherThread();
|
| return new ManagedChildProcessConnection(context, number, sandboxed, deathCallback,
|
| serviceClassName, childProcessCommonParameters, creationParams);
|
| }
|
| };
|
|
|
| - // Synchronization: While most internal flow occurs on the UI thread, the public API
|
| - // (specifically start and stop) may be called from any thread, hence all entry point methods
|
| - // into the class are synchronized on the lock to protect access to these members.
|
| - private final Object mBindingLock = new Object();
|
| -
|
| // Initial binding protects the newly spawned process from being killed before it is put to use,
|
| // it is maintained between calls to start() and removeInitialBinding().
|
| - @GuardedBy("mBindingLock")
|
| private final ChildServiceConnection mInitialBinding;
|
|
|
| // Strong binding will make the service priority equal to the priority of the activity. We want
|
| // the OS to be able to kill background renderers as it kills other background apps, so strong
|
| // bindings are maintained only for services that are active at the moment (between
|
| // addStrongBinding() and removeStrongBinding()).
|
| - @GuardedBy("mBindingLock")
|
| private final ChildServiceConnection mStrongBinding;
|
|
|
| // Low priority binding maintained in the entire lifetime of the connection, i.e. between calls
|
| // to start() and stop().
|
| - @GuardedBy("mBindingLock")
|
| private final ChildServiceConnection mWaivedBinding;
|
|
|
| // Incremented on addStrongBinding(), decremented on removeStrongBinding().
|
| - @GuardedBy("mBindingLock")
|
| private int mStrongBindingCount;
|
|
|
| // Moderate binding will make the service priority equal to the priority of a visible process
|
| // while the app is in the foreground. It will stay bound only while the app is in the
|
| // foreground to protect a background process from the system out-of-memory killer.
|
| - @GuardedBy("mBindingLock")
|
| private final ChildServiceConnection mModerateBinding;
|
|
|
| - @GuardedBy("mBindingLock")
|
| - private boolean mWasOomProtectedOnUnbind;
|
| + // Indicates whether the connection is OOM protected (if the connection is unbound, it contains
|
| + // the state at time of unbinding).
|
| + private boolean mOomProtected;
|
| +
|
| + // Set to true once unbind() was called.
|
| + private boolean mUnbound;
|
|
|
| @VisibleForTesting
|
| ManagedChildProcessConnection(Context context, int number, boolean sandboxed,
|
| @@ -74,152 +68,130 @@ public class ManagedChildProcessConnection extends BaseChildProcessConnection {
|
|
|
| int initialFlags = Context.BIND_AUTO_CREATE;
|
| int extraBindFlags = shouldBindAsExportedService() ? Context.BIND_EXTERNAL_SERVICE : 0;
|
| -
|
| - synchronized (mBindingLock) {
|
| - mInitialBinding = createServiceConnection(initialFlags | extraBindFlags);
|
| - mStrongBinding = createServiceConnection(
|
| - Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | extraBindFlags);
|
| - mWaivedBinding = createServiceConnection(
|
| - Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY | extraBindFlags);
|
| - mModerateBinding = createServiceConnection(Context.BIND_AUTO_CREATE | extraBindFlags);
|
| - }
|
| + mInitialBinding = createServiceConnection(initialFlags | extraBindFlags);
|
| + mStrongBinding = createServiceConnection(
|
| + Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | extraBindFlags);
|
| + mWaivedBinding = createServiceConnection(
|
| + Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY | extraBindFlags);
|
| + mModerateBinding = createServiceConnection(Context.BIND_AUTO_CREATE | extraBindFlags);
|
| }
|
|
|
| @Override
|
| protected boolean bind() {
|
| - synchronized (mBindingLock) {
|
| - if (!mInitialBinding.bind()) {
|
| - return false;
|
| - }
|
| - mWaivedBinding.bind();
|
| + assert LauncherThread.runningOnLauncherThread();
|
| + assert !mUnbound;
|
| + if (!mInitialBinding.bind()) {
|
| + return false;
|
| }
|
| + updateOomProtectedState();
|
| + mWaivedBinding.bind();
|
| return true;
|
| }
|
|
|
| @Override
|
| public void unbind() {
|
| - synchronized (mBindingLock) {
|
| - if (!isBound()) {
|
| - return;
|
| - }
|
| - mWasOomProtectedOnUnbind = isCurrentlyOomProtected();
|
| - mInitialBinding.unbind();
|
| - mStrongBinding.unbind();
|
| - mWaivedBinding.unbind();
|
| - mModerateBinding.unbind();
|
| - mStrongBindingCount = 0;
|
| - }
|
| - }
|
| -
|
| - @GuardedBy("mBindingLock")
|
| - private boolean isBound() {
|
| - return mInitialBinding.isBound() || mStrongBinding.isBound() || mWaivedBinding.isBound()
|
| - || mModerateBinding.isBound();
|
| + assert LauncherThread.runningOnLauncherThread();
|
| + mUnbound = true;
|
| + mInitialBinding.unbind();
|
| + mStrongBinding.unbind();
|
| + // Note that we don't update the OOM state here as to preserve the last OOM state.
|
| + mWaivedBinding.unbind();
|
| + mModerateBinding.unbind();
|
| + mStrongBindingCount = 0;
|
| }
|
|
|
| public boolean isInitialBindingBound() {
|
| - synchronized (mBindingLock) {
|
| - return mInitialBinding.isBound();
|
| - }
|
| + assert LauncherThread.runningOnLauncherThread();
|
| + return mInitialBinding.isBound();
|
| }
|
|
|
| public boolean isStrongBindingBound() {
|
| - synchronized (mBindingLock) {
|
| - return mStrongBinding.isBound();
|
| - }
|
| + assert LauncherThread.runningOnLauncherThread();
|
| + return mStrongBinding.isBound();
|
| }
|
|
|
| public void removeInitialBinding() {
|
| - synchronized (mBindingLock) {
|
| - mInitialBinding.unbind();
|
| - }
|
| + assert LauncherThread.runningOnLauncherThread();
|
| + mInitialBinding.unbind();
|
| + updateOomProtectedState();
|
| }
|
|
|
| + /**
|
| + * @return true if the connection is bound and OOM protected or was OOM protected when unbound.
|
| + */
|
| public boolean isOomProtectedOrWasWhenDied() {
|
| - // Call isConnected() outside of the synchronized block or we could deadlock.
|
| - final boolean isConnected = isConnected();
|
| - synchronized (mBindingLock) {
|
| - if (isConnected) {
|
| - return isCurrentlyOomProtected();
|
| - }
|
| - return mWasOomProtectedOnUnbind;
|
| - }
|
| - }
|
| -
|
| - @GuardedBy("mBindingLock")
|
| - private boolean isCurrentlyOomProtected() {
|
| - return mInitialBinding.isBound() || mStrongBinding.isBound();
|
| + // WARNING: this method can be called from a thread other than the launcher thread.
|
| + // Note that it returns the current OOM protected state and is racy. This not really
|
| + // preventable without changing the caller's API, short of blocking.
|
| + return mOomProtected;
|
| }
|
|
|
| public void dropOomBindings() {
|
| - synchronized (mBindingLock) {
|
| - mInitialBinding.unbind();
|
| + assert LauncherThread.runningOnLauncherThread();
|
| + mInitialBinding.unbind();
|
|
|
| - mStrongBindingCount = 0;
|
| - mStrongBinding.unbind();
|
| + mStrongBindingCount = 0;
|
| + mStrongBinding.unbind();
|
| + updateOomProtectedState();
|
|
|
| - mModerateBinding.unbind();
|
| - }
|
| + mModerateBinding.unbind();
|
| }
|
|
|
| public void addStrongBinding() {
|
| - // Call isConnected() outside of the synchronized block or we could deadlock.
|
| - final boolean isConnected = isConnected();
|
| - synchronized (mBindingLock) {
|
| - if (!isConnected) {
|
| - Log.w(TAG, "The connection is not bound for %d", getPid());
|
| - return;
|
| - }
|
| - if (mStrongBindingCount == 0) {
|
| - mStrongBinding.bind();
|
| - }
|
| - mStrongBindingCount++;
|
| + assert LauncherThread.runningOnLauncherThread();
|
| + if (!isConnected()) {
|
| + Log.w(TAG, "The connection is not bound for %d", getPid());
|
| + return;
|
| + }
|
| + if (mStrongBindingCount == 0) {
|
| + mStrongBinding.bind();
|
| + updateOomProtectedState();
|
| }
|
| + mStrongBindingCount++;
|
| }
|
|
|
| public void removeStrongBinding() {
|
| - // Call isConnected() outside of the synchronized block or we could deadlock.
|
| - final boolean isConnected = isConnected();
|
| - synchronized (mBindingLock) {
|
| - if (!isConnected) {
|
| - Log.w(TAG, "The connection is not bound for %d", getPid());
|
| - return;
|
| - }
|
| - assert mStrongBindingCount > 0;
|
| - mStrongBindingCount--;
|
| - if (mStrongBindingCount == 0) {
|
| - mStrongBinding.unbind();
|
| - }
|
| + assert LauncherThread.runningOnLauncherThread();
|
| + if (!isConnected()) {
|
| + Log.w(TAG, "The connection is not bound for %d", getPid());
|
| + return;
|
| }
|
| + assert mStrongBindingCount > 0;
|
| + mStrongBindingCount--;
|
| + if (mStrongBindingCount == 0) {
|
| + mStrongBinding.unbind();
|
| + updateOomProtectedState();
|
| + }
|
| + updateOomProtectedState();
|
| }
|
|
|
| public boolean isModerateBindingBound() {
|
| - synchronized (mBindingLock) {
|
| - return mModerateBinding.isBound();
|
| - }
|
| + assert LauncherThread.runningOnLauncherThread();
|
| + return mModerateBinding.isBound();
|
| }
|
|
|
| public void addModerateBinding() {
|
| - // Call isConnected() outside of the synchronized block or we could deadlock.
|
| - final boolean isConnected = isConnected();
|
| - synchronized (mBindingLock) {
|
| - if (!isConnected) {
|
| - Log.w(TAG, "The connection is not bound for %d", getPid());
|
| - return;
|
| - }
|
| - mModerateBinding.bind();
|
| + assert LauncherThread.runningOnLauncherThread();
|
| + if (!isConnected()) {
|
| + Log.w(TAG, "The connection is not bound for %d", getPid());
|
| + return;
|
| }
|
| + mModerateBinding.bind();
|
| }
|
|
|
| public void removeModerateBinding() {
|
| - // Call isConnected() outside of the synchronized block or we could deadlock.
|
| - final boolean isConnected = isConnected();
|
| - synchronized (mBindingLock) {
|
| - if (!isConnected) {
|
| - Log.w(TAG, "The connection is not bound for %d", getPid());
|
| - return;
|
| - }
|
| - mModerateBinding.unbind();
|
| + assert LauncherThread.runningOnLauncherThread();
|
| + if (!isConnected()) {
|
| + Log.w(TAG, "The connection is not bound for %d", getPid());
|
| + return;
|
| + }
|
| + mModerateBinding.unbind();
|
| + }
|
| +
|
| + // Should be called every time the mInitialBinding or mStrongBinding are bound/unbound.
|
| + private void updateOomProtectedState() {
|
| + if (!mUnbound) {
|
| + mOomProtected = mInitialBinding.isBound() || mStrongBinding.isBound();
|
| }
|
| }
|
| }
|
|
|