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

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/BindingManagerImpl.java

Issue 1146813010: Introduce moderate binding (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 6 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 unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.content.browser; 5 package org.chromium.content.browser;
6 6
7 import android.util.Log; 7 import android.content.ComponentCallbacks2;
8 import android.content.Context;
9 import android.content.res.Configuration;
10 import android.util.LruCache;
8 import android.util.SparseArray; 11 import android.util.SparseArray;
9 12
13 import org.chromium.base.Log;
10 import org.chromium.base.SysUtils; 14 import org.chromium.base.SysUtils;
11 import org.chromium.base.ThreadUtils; 15 import org.chromium.base.ThreadUtils;
12 import org.chromium.base.VisibleForTesting; 16 import org.chromium.base.VisibleForTesting;
17 import org.chromium.base.metrics.RecordHistogram;
13 18
14 /** 19 /**
15 * Manages oom bindings used to bound child services. 20 * Manages oom bindings used to bound child services.
16 */ 21 */
17 class BindingManagerImpl implements BindingManager { 22 class BindingManagerImpl implements BindingManager {
18 private static final String TAG = "BindingManager"; 23 private static final String TAG = "cr.BindingManager";
19 24
20 // Delay of 1 second used when removing temporary strong binding of a proces s (only on 25 // Delay of 1 second used when removing temporary strong binding of a proces s (only on
21 // non-low-memory devices). 26 // non-low-memory devices).
22 private static final long DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS = 1 * 1000; 27 private static final long DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS = 1 * 1000;
23 28
24 // These fields allow to override the parameters for testing - see 29 // These fields allow to override the parameters for testing - see
25 // createBindingManagerForTesting(). 30 // createBindingManagerForTesting().
26 private final long mRemoveStrongBindingDelay; 31 private final long mRemoveStrongBindingDelay;
27 private final boolean mIsLowMemoryDevice; 32 private final boolean mIsLowMemoryDevice;
28 33
34 private static class ModerateBindingPool
35 extends LruCache<Integer, ManagedConnection> implements ComponentCal lbacks2 {
36 private final float mLowReduceRatio;
37 private final float mHighReduceRatio;
38
39 public ModerateBindingPool(int maxSize, float lowReduceRatio, float high ReduceRatio) {
40 super(maxSize);
41
42 mLowReduceRatio = lowReduceRatio;
43 mHighReduceRatio = highReduceRatio;
44 }
45
46 @Override
47 public void onTrimMemory(int level) {
48 Log.i(TAG, "onTrimMemory: level=" + level + ", size=" + size());
49 if (size() > 0) {
50 if (level <= TRIM_MEMORY_RUNNING_MODERATE) {
51 reduce(mLowReduceRatio);
52 } else if (level <= TRIM_MEMORY_RUNNING_LOW) {
53 reduce(mHighReduceRatio);
54 } else {
55 evictAll();
56 }
57 }
58 }
59
60 @Override
61 public void onLowMemory() {
62 Log.i(TAG, "onLowMemory: evict " + size() + " bindings");
63 evictAll();
64 }
65
66 @Override
67 public void onConfigurationChanged(Configuration configuration) {}
68
69 private void reduce(float reduceRatio) {
70 int newSize = (int) (size() * (1f - reduceRatio));
71 Log.i(TAG, "Reduce connections from " + size() + " to " + newSize);
72 if (newSize == 0) {
73 evictAll();
74 } else {
75 trimToSize(newSize);
76 }
77 }
78
79 void addConnection(ManagedConnection managedConnection) {
80 ChildProcessConnection connection = managedConnection.mConnection;
81 if (connection != null && connection.isInSandbox()) {
82 managedConnection.addModerateBinding();
83 if (connection.isModerateBindingBound()) {
84 put(connection.getServiceNumber(), managedConnection);
85 } else {
86 remove(connection.getServiceNumber());
87 }
88 }
89 }
90
91 void removeConnection(ManagedConnection managedConnection) {
92 ChildProcessConnection connection = managedConnection.mConnection;
93 if (connection != null && connection.isInSandbox()) {
94 remove(connection.getServiceNumber());
95 }
96 }
97
98 @Override
99 protected void entryRemoved(boolean evicted, Integer key, ManagedConnect ion oldValue,
100 ManagedConnection newValue) {
101 if (oldValue != newValue) {
102 oldValue.removeModerateBinding();
103 }
104 }
105 }
106
107 private ModerateBindingPool mModerateBindingPool;
108
29 /** 109 /**
30 * Wraps ChildProcessConnection keeping track of additional information need ed to manage the 110 * Wraps ChildProcessConnection keeping track of additional information need ed to manage the
31 * bindings of the connection. The reference to ChildProcessConnection is cl eared when the 111 * bindings of the connection. The reference to ChildProcessConnection is cl eared when the
32 * connection goes away, but ManagedConnection itself is kept (until overwri tten by a new entry 112 * connection goes away, but ManagedConnection itself is kept (until overwri tten by a new entry
33 * for the same pid). 113 * for the same pid).
34 */ 114 */
35 private class ManagedConnection { 115 private class ManagedConnection {
36 // Set in constructor, cleared in clearConnection(). 116 // Set in constructor, cleared in clearConnection().
37 private ChildProcessConnection mConnection; 117 private ChildProcessConnection mConnection;
38 118
(...skipping 13 matching lines...) Expand all
52 if (mConnection == null || !mConnection.isInitialBindingBound()) ret urn; 132 if (mConnection == null || !mConnection.isInitialBindingBound()) ret urn;
53 mConnection.removeInitialBinding(); 133 mConnection.removeInitialBinding();
54 } 134 }
55 135
56 /** Adds a strong service binding. */ 136 /** Adds a strong service binding. */
57 private void addStrongBinding() { 137 private void addStrongBinding() {
58 ChildProcessConnection connection = mConnection; 138 ChildProcessConnection connection = mConnection;
59 if (connection == null) return; 139 if (connection == null) return;
60 140
61 connection.addStrongBinding(); 141 connection.addStrongBinding();
142 if (mModerateBindingPool != null) {
143 mModerateBindingPool.removeConnection(this);
144 }
62 } 145 }
63 146
64 /** Removes a strong service binding. */ 147 /** Removes a strong service binding. */
65 private void removeStrongBinding() { 148 private void removeStrongBinding(final boolean keepsAsModerate) {
66 final ChildProcessConnection connection = mConnection; 149 final ChildProcessConnection connection = mConnection;
67 // We have to fail gracefully if the strong binding is not present, as on low-end the 150 // We have to fail gracefully if the strong binding is not present, as on low-end the
68 // binding could have been removed by dropOomBindings() when a new s ervice was started. 151 // binding could have been removed by dropOomBindings() when a new s ervice was started.
69 if (connection == null || !connection.isStrongBindingBound()) return ; 152 if (connection == null || !connection.isStrongBindingBound()) return ;
70 153
71 // This runnable performs the actual unbinding. It will be executed synchronously when 154 // This runnable performs the actual unbinding. It will be executed synchronously when
72 // on low-end devices and posted with a delay otherwise. 155 // on low-end devices and posted with a delay otherwise.
73 Runnable doUnbind = new Runnable() { 156 Runnable doUnbind = new Runnable() {
74 @Override 157 @Override
75 public void run() { 158 public void run() {
76 if (connection.isStrongBindingBound()) { 159 if (connection.isStrongBindingBound()) {
77 connection.removeStrongBinding(); 160 connection.removeStrongBinding();
161 if (mModerateBindingPool != null && !connection.isStrong BindingBound()
162 && keepsAsModerate) {
163 mModerateBindingPool.addConnection(ManagedConnection .this);
164 }
78 } 165 }
79 } 166 }
80 }; 167 };
81 168
82 if (mIsLowMemoryDevice) { 169 if (mIsLowMemoryDevice) {
83 doUnbind.run(); 170 doUnbind.run();
84 } else { 171 } else {
85 ThreadUtils.postOnUiThreadDelayed(doUnbind, mRemoveStrongBinding Delay); 172 ThreadUtils.postOnUiThreadDelayed(doUnbind, mRemoveStrongBinding Delay);
86 } 173 }
87 } 174 }
88 175
176 /** Removes the moderate service binding. */
177 private void removeModerateBinding() {
178 if (mConnection == null || !mConnection.isModerateBindingBound()) re turn;
179 mConnection.removeModerateBinding();
180 }
181
182 /** Adds the moderate service binding. */
183 private void addModerateBinding() {
184 ChildProcessConnection connection = mConnection;
185 if (connection == null) return;
186
187 connection.addModerateBinding();
188 }
189
89 /** 190 /**
90 * Drops the service bindings. This is used on low-end to drop bindings of the current 191 * Drops the service bindings. This is used on low-end to drop bindings of the current
91 * service when a new one is used in foreground. 192 * service when a new one is used in foreground.
92 */ 193 */
93 private void dropBindings() { 194 private void dropBindings() {
94 assert mIsLowMemoryDevice; 195 assert mIsLowMemoryDevice;
95 ChildProcessConnection connection = mConnection; 196 ChildProcessConnection connection = mConnection;
96 if (connection == null) return; 197 if (connection == null) return;
97 198
98 connection.dropOomBindings(); 199 connection.dropOomBindings();
99 } 200 }
100 201
101 ManagedConnection(ChildProcessConnection connection) { 202 ManagedConnection(ChildProcessConnection connection) {
102 mConnection = connection; 203 mConnection = connection;
103 } 204 }
104 205
105 /** 206 /**
106 * Sets the visibility of the service, adding or removing the strong bin ding as needed. 207 * Sets the visibility of the service, adding or removing the strong bin ding as needed.
107 */ 208 */
108 void setInForeground(boolean nextInForeground) { 209 void setInForeground(boolean nextInForeground) {
109 if (!mInForeground && nextInForeground) { 210 if (!mInForeground && nextInForeground) {
110 addStrongBinding(); 211 addStrongBinding();
111 } else if (mInForeground && !nextInForeground) { 212 } else if (mInForeground && !nextInForeground) {
112 removeStrongBinding(); 213 removeStrongBinding(true);
113 } 214 }
114 215
115 mInForeground = nextInForeground; 216 mInForeground = nextInForeground;
116 } 217 }
117 218
118 /** 219 /**
119 * Removes the initial binding. 220 * Removes the initial binding.
120 */ 221 */
121 void determinedVisibility() { 222 void determinedVisibility() {
122 removeInitialBinding(); 223 removeInitialBinding();
123 } 224 }
124 225
125 /** 226 /**
126 * Sets or removes additional binding when the service is main service d uring the embedder 227 * Sets or removes additional binding when the service is main service d uring the embedder
127 * background period. 228 * background period.
128 */ 229 */
129 void setBoundForBackgroundPeriod(boolean nextBound) { 230 void setBoundForBackgroundPeriod(boolean nextBound) {
130 if (!mBoundForBackgroundPeriod && nextBound) { 231 if (!mBoundForBackgroundPeriod && nextBound) {
131 addStrongBinding(); 232 addStrongBinding();
132 } else if (mBoundForBackgroundPeriod && !nextBound) { 233 } else if (mBoundForBackgroundPeriod && !nextBound) {
133 removeStrongBinding(); 234 removeStrongBinding(false);
134 } 235 }
135 236
136 mBoundForBackgroundPeriod = nextBound; 237 mBoundForBackgroundPeriod = nextBound;
137 } 238 }
138 239
139 boolean isOomProtected() { 240 boolean isOomProtected() {
140 // When a process crashes, we can be queried about its oom status be fore or after the 241 // When a process crashes, we can be queried about its oom status be fore or after the
141 // connection is cleared. For the latter case, the oom status is sta shed in 242 // connection is cleared. For the latter case, the oom status is sta shed in
142 // mWasOomProtected. 243 // mWasOomProtected.
143 return mConnection != null 244 return mConnection != null
144 ? mConnection.isOomProtectedOrWasWhenDied() : mWasOomProtect ed; 245 ? mConnection.isOomProtectedOrWasWhenDied() : mWasOomProtect ed;
145 } 246 }
146 247
147 void clearConnection() { 248 void clearConnection() {
148 mWasOomProtected = mConnection.isOomProtectedOrWasWhenDied(); 249 mWasOomProtected = mConnection.isOomProtectedOrWasWhenDied();
250 if (mModerateBindingPool != null) {
251 mModerateBindingPool.removeConnection(this);
252 }
149 mConnection = null; 253 mConnection = null;
150 } 254 }
151 255
152 /** @return true iff the reference to the connection is no longer held * / 256 /** @return true iff the reference to the connection is no longer held * /
153 @VisibleForTesting 257 @VisibleForTesting
154 boolean isConnectionCleared() { 258 boolean isConnectionCleared() {
155 return mConnection == null; 259 return mConnection == null;
156 } 260 }
157 } 261 }
158 262
159 // This can be manipulated on different threads, synchronize access on mMana gedConnections. 263 // This can be manipulated on different threads, synchronize access on mMana gedConnections.
160 private final SparseArray<ManagedConnection> mManagedConnections = 264 private final SparseArray<ManagedConnection> mManagedConnections =
161 new SparseArray<ManagedConnection>(); 265 new SparseArray<ManagedConnection>();
162 266
163 // The connection that was most recently set as foreground (using setInForeg round()). This is 267 // The connection that was most recently set as foreground (using setInForeg round()). This is
164 // used to add additional binding on it when the embedder goes to background . On low-end, this 268 // used to add additional binding on it when the embedder goes to background . On low-end, this
165 // is also used to drop process bidnings when a new one is created, making s ure that only one 269 // is also used to drop process bidnings when a new one is created, making s ure that only one
166 // renderer process at a time is protected from oom killing. 270 // renderer process at a time is protected from oom killing.
167 private ManagedConnection mLastInForeground; 271 private ManagedConnection mLastInForeground;
168 272
169 // Synchronizes operations that access mLastInForeground: setInForeground() and 273 // Synchronizes operations that access mLastInForeground: setInForeground() and
170 // addNewConnection(). 274 // addNewConnection().
171 private final Object mLastInForegroundLock = new Object(); 275 private final Object mLastInForegroundLock = new Object();
172 276
173 // The connection bound with additional binding in onSentToBackground(). 277 // The connection bound with additional binding in onSentToBackground().
174 private ManagedConnection mBoundForBackgroundPeriod; 278 private ManagedConnection mBoundForBackgroundPeriod;
175 279
280 // Whether this instance is used on testing.
281 private boolean mOnTesting;
Yaron 2015/06/17 02:56:12 final
Jaekyun Seok (inactive) 2015/06/17 04:35:46 Done.
282
176 /** 283 /**
177 * The constructor is private to hide parameters exposed for testing from th e regular consumer. 284 * The constructor is private to hide parameters exposed for testing from th e regular consumer.
178 * Use factory methods to create an instance. 285 * Use factory methods to create an instance.
179 */ 286 */
180 private BindingManagerImpl(boolean isLowMemoryDevice, long removeStrongBindi ngDelay) { 287 private BindingManagerImpl(
288 boolean isLowMemoryDevice, long removeStrongBindingDelay, boolean on Testing) {
181 mIsLowMemoryDevice = isLowMemoryDevice; 289 mIsLowMemoryDevice = isLowMemoryDevice;
182 mRemoveStrongBindingDelay = removeStrongBindingDelay; 290 mRemoveStrongBindingDelay = removeStrongBindingDelay;
291 mOnTesting = onTesting;
183 } 292 }
184 293
185 public static BindingManagerImpl createBindingManager() { 294 public static BindingManagerImpl createBindingManager() {
186 return new BindingManagerImpl(SysUtils.isLowEndDevice(), 295 return new BindingManagerImpl(
187 DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLIS); 296 SysUtils.isLowEndDevice(), DETACH_AS_ACTIVE_HIGH_END_DELAY_MILLI S, false);
188 } 297 }
189 298
190 /** 299 /**
191 * Creates a testing instance of BindingManager. Testing instance will have the unbinding delays 300 * Creates a testing instance of BindingManager. Testing instance will have the unbinding delays
192 * set to 0, so that the tests don't need to deal with actual waiting. 301 * set to 0, so that the tests don't need to deal with actual waiting.
193 * @param isLowEndDevice true iff the created instance should apply low-end binding policies 302 * @param isLowEndDevice true iff the created instance should apply low-end binding policies
194 */ 303 */
195 public static BindingManagerImpl createBindingManagerForTesting(boolean isLo wEndDevice) { 304 public static BindingManagerImpl createBindingManagerForTesting(boolean isLo wEndDevice) {
196 return new BindingManagerImpl(isLowEndDevice, 0); 305 return new BindingManagerImpl(isLowEndDevice, 0, true);
197 } 306 }
198 307
199 @Override 308 @Override
200 public void addNewConnection(int pid, ChildProcessConnection connection) { 309 public void addNewConnection(int pid, ChildProcessConnection connection) {
201 // This will reset the previous entry for the pid in the unlikely event of the OS 310 // This will reset the previous entry for the pid in the unlikely event of the OS
202 // reusing renderer pids. 311 // reusing renderer pids.
203 synchronized (mManagedConnections) { 312 synchronized (mManagedConnections) {
204 mManagedConnections.put(pid, new ManagedConnection(connection)); 313 mManagedConnections.put(pid, new ManagedConnection(connection));
205 } 314 }
206 } 315 }
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
249 public void onSentToBackground() { 358 public void onSentToBackground() {
250 assert mBoundForBackgroundPeriod == null; 359 assert mBoundForBackgroundPeriod == null;
251 synchronized (mLastInForegroundLock) { 360 synchronized (mLastInForegroundLock) {
252 // mLastInForeground can be null at this point as the embedding appl ication could be 361 // mLastInForeground can be null at this point as the embedding appl ication could be
253 // used in foreground without spawning any renderers. 362 // used in foreground without spawning any renderers.
254 if (mLastInForeground != null) { 363 if (mLastInForeground != null) {
255 mLastInForeground.setBoundForBackgroundPeriod(true); 364 mLastInForeground.setBoundForBackgroundPeriod(true);
256 mBoundForBackgroundPeriod = mLastInForeground; 365 mBoundForBackgroundPeriod = mLastInForeground;
257 } 366 }
258 } 367 }
368 if (mModerateBindingPool != null) {
369 Log.i(TAG, "Release moderate connections: " + mModerateBindingPool.s ize());
370 if (!mOnTesting) {
371 RecordHistogram.recordCountHistogram(
372 "Android.ModerateBindingCount", mModerateBindingPool.siz e());
373 }
374 mModerateBindingPool.evictAll();
375 }
259 } 376 }
260 377
261 @Override 378 @Override
262 public void onBroughtToForeground() { 379 public void onBroughtToForeground() {
263 if (mBoundForBackgroundPeriod != null) { 380 if (mBoundForBackgroundPeriod != null) {
264 mBoundForBackgroundPeriod.setBoundForBackgroundPeriod(false); 381 mBoundForBackgroundPeriod.setBoundForBackgroundPeriod(false);
265 mBoundForBackgroundPeriod = null; 382 mBoundForBackgroundPeriod = null;
266 } 383 }
267 } 384 }
268 385
(...skipping 18 matching lines...) Expand all
287 if (managedConnection != null) managedConnection.clearConnection(); 404 if (managedConnection != null) managedConnection.clearConnection();
288 } 405 }
289 406
290 /** @return true iff the connection reference is no longer held */ 407 /** @return true iff the connection reference is no longer held */
291 @VisibleForTesting 408 @VisibleForTesting
292 public boolean isConnectionCleared(int pid) { 409 public boolean isConnectionCleared(int pid) {
293 synchronized (mManagedConnections) { 410 synchronized (mManagedConnections) {
294 return mManagedConnections.get(pid).isConnectionCleared(); 411 return mManagedConnections.get(pid).isConnectionCleared();
295 } 412 }
296 } 413 }
414
415 @Override
416 public void startModerateBindingManagement(
417 Context context, int maxSize, float lowReduceRatio, float highReduce Ratio) {
418 if (mIsLowMemoryDevice || mModerateBindingPool != null) return;
419
420 Log.i(TAG, "Moderate binding enabled: maxSize=" + maxSize + " lowReduceR atio="
421 + lowReduceRatio + " highReduceRatio=" + highReduceRatio );
422 mModerateBindingPool = new ModerateBindingPool(maxSize, lowReduceRatio, highReduceRatio);
423 if (context != null) context.registerComponentCallbacks(mModerateBinding Pool);
424 }
297 } 425 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698