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

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

Powered by Google App Engine
This is Rietveld 408576698