Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(350)

Unified Diff: content/public/android/java/src/org/chromium/content/browser/ManagedChildProcessConnection.java

Issue 2840303002: Making ChildProcessConnection only accessed from the launcher thread. (Closed)
Patch Set: Clean-up + sync Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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();
}
}
}

Powered by Google App Engine
This is Rietveld 408576698