Commit 6f562220 authored by ppi@chromium.org's avatar ppi@chromium.org

Don't share renderers between unrelated tabs on Android.

On Android we explicitly allow the OS to kill Chrome's background
renderers when under memory pressure and we don't try to control the
number of renderers ourselves.

The process limit logic in content causes process sharing
between unrelated tabs when the number of renderer process hosts
(not the number of actual live processes) is too high. Because on
Android the system adjusts the number of actual live processes for us,
we don't want to limit the number of process hosts or to ever share
renderers between unrelated tabs.

This patch:
- disables the renderer process host limit on Android. If not
  overridden, ShouldTryToUseExistingProcessHost() will always return
  false.
- drops the logic that sets the renderer limit based on the number of
  declared renderer services

BUG=325842

Review URL: https://codereview.chromium.org/356453003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284095 0039d316-1c4b-4281-b951-d872f2087c98
parent 2ba6b280
...@@ -51,8 +51,7 @@ public abstract class AwBrowserProcess { ...@@ -51,8 +51,7 @@ public abstract class AwBrowserProcess {
@Override @Override
public void run() { public void run() {
try { try {
BrowserStartupController.get(context).startBrowserProcessesSync( BrowserStartupController.get(context).startBrowserProcessesSync(true);
BrowserStartupController.MAX_RENDERERS_SINGLE_PROCESS);
initializePlatformKeySystem(); initializePlatformKeySystem();
} catch (ProcessInitException e) { } catch (ProcessInitException e) {
throw new RuntimeException("Cannot initialize WebView", e); throw new RuntimeException("Cannot initialize WebView", e);
......
...@@ -112,8 +112,7 @@ public abstract class ChromiumSyncAdapter extends AbstractThreadedSyncAdapter { ...@@ -112,8 +112,7 @@ public abstract class ChromiumSyncAdapter extends AbstractThreadedSyncAdapter {
private void startBrowserProcessesSync( private void startBrowserProcessesSync(
final BrowserStartupController.StartupCallback callback) { final BrowserStartupController.StartupCallback callback) {
try { try {
BrowserStartupController.get(mApplication).startBrowserProcessesSync( BrowserStartupController.get(mApplication).startBrowserProcessesSync(false);
BrowserStartupController.MAX_RENDERERS_LIMIT);
} catch (ProcessInitException e) { } catch (ProcessInitException e) {
Log.e(TAG, "Unable to load native library.", e); Log.e(TAG, "Unable to load native library.", e);
System.exit(-1); System.exit(-1);
......
...@@ -42,8 +42,7 @@ public class ChromeShellTestBase extends ActivityInstrumentationTestCase2<Chrome ...@@ -42,8 +42,7 @@ public class ChromeShellTestBase extends ActivityInstrumentationTestCase2<Chrome
public void run() { public void run() {
CommandLine.initFromFile("/data/local/tmp/chrome-shell-command-line"); CommandLine.initFromFile("/data/local/tmp/chrome-shell-command-line");
try { try {
BrowserStartupController.get(targetContext).startBrowserProcessesSync( BrowserStartupController.get(targetContext).startBrowserProcessesSync(false);
BrowserStartupController.MAX_RENDERERS_LIMIT);
} catch (ProcessInitException e) { } catch (ProcessInitException e) {
Log.e(TAG, "Unable to load native library.", e); Log.e(TAG, "Unable to load native library.", e);
fail("Unable to load native library"); fail("Unable to load native library");
......
...@@ -249,8 +249,7 @@ public class GCMDriver { ...@@ -249,8 +249,7 @@ public class GCMDriver {
// ChromeShellApplication.initCommandLine() as appropriate. // ChromeShellApplication.initCommandLine() as appropriate.
try { try {
final int MAX_RENDERERS = 1; BrowserStartupController.get(context).startBrowserProcessesSync(false);
BrowserStartupController.get(context).startBrowserProcessesSync(MAX_RENDERERS);
if (sInstance != null) { if (sInstance != null) {
task.run(); task.run();
} else { } else {
......
...@@ -28,13 +28,13 @@ bool RegisterBrowserStartupController(JNIEnv* env) { ...@@ -28,13 +28,13 @@ bool RegisterBrowserStartupController(JNIEnv* env) {
static void SetCommandLineFlags(JNIEnv* env, static void SetCommandLineFlags(JNIEnv* env,
jclass clazz, jclass clazz,
jint max_render_process_count, jboolean single_process,
jstring plugin_descriptor) { jstring plugin_descriptor) {
std::string plugin_str = std::string plugin_str =
(plugin_descriptor == NULL (plugin_descriptor == NULL
? std::string() ? std::string()
: base::android::ConvertJavaStringToUTF8(env, plugin_descriptor)); : base::android::ConvertJavaStringToUTF8(env, plugin_descriptor));
SetContentCommandLineFlags(max_render_process_count, plugin_str); SetContentCommandLineFlags(static_cast<bool>(single_process), plugin_str);
} }
static jboolean IsOfficialBuild(JNIEnv* env, jclass clazz) { static jboolean IsOfficialBuild(JNIEnv* env, jclass clazz) {
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
namespace content { namespace content {
void SetContentCommandLineFlags(int max_render_process_count, void SetContentCommandLineFlags(bool single_process,
const std::string& plugin_descriptor) { const std::string& plugin_descriptor) {
// May be called multiple times, to cover all possible program entry points. // May be called multiple times, to cover all possible program entry points.
static bool already_initialized = false; static bool already_initialized = false;
...@@ -34,9 +34,7 @@ void SetContentCommandLineFlags(int max_render_process_count, ...@@ -34,9 +34,7 @@ void SetContentCommandLineFlags(int max_render_process_count,
switches::kRendererProcessLimit); switches::kRendererProcessLimit);
int value; int value;
if (base::StringToInt(limit, &value)) { if (base::StringToInt(limit, &value)) {
command_line_renderer_limit = value; command_line_renderer_limit = std::max(0, value);
if (value <= 0)
max_render_process_count = 0;
} }
} }
...@@ -44,16 +42,13 @@ void SetContentCommandLineFlags(int max_render_process_count, ...@@ -44,16 +42,13 @@ void SetContentCommandLineFlags(int max_render_process_count,
int limit = std::min(command_line_renderer_limit, int limit = std::min(command_line_renderer_limit,
static_cast<int>(kMaxRendererProcessCount)); static_cast<int>(kMaxRendererProcessCount));
RenderProcessHost::SetMaxRendererProcessCount(limit); RenderProcessHost::SetMaxRendererProcessCount(limit);
} else if (max_render_process_count <= 0) { }
if (single_process || command_line_renderer_limit == 0) {
// Need to ensure the command line flag is consistent as a lot of chrome // Need to ensure the command line flag is consistent as a lot of chrome
// internal code checks this directly, but it wouldn't normally get set when // internal code checks this directly, but it wouldn't normally get set when
// we are implementing an embedded WebView. // we are implementing an embedded WebView.
parsed_command_line->AppendSwitch(switches::kSingleProcess); parsed_command_line->AppendSwitch(switches::kSingleProcess);
} else {
int default_maximum = RenderProcessHost::GetMaxRendererProcessCount();
DCHECK(default_maximum <= static_cast<int>(kMaxRendererProcessCount));
if (max_render_process_count < default_maximum)
RenderProcessHost::SetMaxRendererProcessCount(max_render_process_count);
} }
parsed_command_line->AppendSwitch( parsed_command_line->AppendSwitch(
......
...@@ -12,7 +12,7 @@ namespace content { ...@@ -12,7 +12,7 @@ namespace content {
// Force-appends flags to the command line turning on Android-specific // Force-appends flags to the command line turning on Android-specific
// features owned by Content. This is called as soon as possible during // features owned by Content. This is called as soon as possible during
// initialization to make sure code sees the new flags. // initialization to make sure code sees the new flags.
void SetContentCommandLineFlags(int max_render_process_count, void SetContentCommandLineFlags(bool single_process,
const std::string& plugin_descriptor); const std::string& plugin_descriptor);
} // namespace content } // namespace content
......
...@@ -394,15 +394,22 @@ size_t RenderProcessHost::GetMaxRendererProcessCount() { ...@@ -394,15 +394,22 @@ size_t RenderProcessHost::GetMaxRendererProcessCount() {
if (g_max_renderer_count_override) if (g_max_renderer_count_override)
return g_max_renderer_count_override; return g_max_renderer_count_override;
// Defines the maximum number of renderer processes according to the #if defined(OS_ANDROID)
// amount of installed memory as reported by the OS. The calculation // On Android we don't maintain a limit of renderer process hosts - we are
// assumes that you want the renderers to use half of the installed // happy with keeping a lot of these, as long as the number of live renderer
// RAM and assuming that each WebContents uses ~40MB. // processes remains reasonable, and on Android the OS takes care of that.
// If you modify this assumption, you need to adjust the return std::numeric_limits<size_t>::max();
// ThirtyFourTabs test to match the expected number of processes. #endif
// On other platforms, we calculate the maximum number of renderer process
// hosts according to the amount of installed memory as reported by the OS.
// The calculation assumes that you want the renderers to use half of the
// installed RAM and assuming that each WebContents uses ~40MB. If you modify
// this assumption, you need to adjust the ThirtyFourTabs test to match the
// expected number of processes.
// //
// With the given amounts of installed memory below on a 32-bit CPU, // With the given amounts of installed memory below on a 32-bit CPU, the
// the maximum renderer count will roughly be as follows: // maximum renderer count will roughly be as follows:
// //
// 128 MB -> 3 // 128 MB -> 3
// 512 MB -> 6 // 512 MB -> 6
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <limits>
#include "content/public/common/content_constants.h"
#include "content/public/test/mock_render_process_host.h" #include "content/public/test/mock_render_process_host.h"
#include "content/test/test_render_view_host.h" #include "content/test/test_render_view_host.h"
...@@ -26,4 +29,51 @@ TEST_F(RenderProcessHostUnitTest, GuestsAreNotSuitableHosts) { ...@@ -26,4 +29,51 @@ TEST_F(RenderProcessHostUnitTest, GuestsAreNotSuitableHosts) {
RenderProcessHost::GetExistingProcessHost(browser_context(), test_url)); RenderProcessHost::GetExistingProcessHost(browser_context(), test_url));
} }
#if !defined(OS_ANDROID)
TEST_F(RenderProcessHostUnitTest, RendererProcessLimit) {
// Disable any overrides.
RenderProcessHostImpl::SetMaxRendererProcessCount(0);
// Verify that the limit is between 1 and kMaxRendererProcessCount.
EXPECT_GT(RenderProcessHostImpl::GetMaxRendererProcessCount(), 0u);
EXPECT_LE(RenderProcessHostImpl::GetMaxRendererProcessCount(),
kMaxRendererProcessCount);
// Add dummy process hosts to saturate the limit.
ASSERT_NE(0u, kMaxRendererProcessCount);
ScopedVector<MockRenderProcessHost> hosts;
for (size_t i = 0; i < kMaxRendererProcessCount; ++i) {
hosts.push_back(new MockRenderProcessHost(browser_context()));
}
// Verify that the renderer sharing will happen.
GURL test_url("http://foo.com");
EXPECT_TRUE(RenderProcessHostImpl::ShouldTryToUseExistingProcessHost(
browser_context(), test_url));
}
#endif
#if defined(OS_ANDROID)
TEST_F(RenderProcessHostUnitTest, NoRendererProcessLimitOnAndroid) {
// Disable any overrides.
RenderProcessHostImpl::SetMaxRendererProcessCount(0);
// Verify that by default the limit on Android returns max size_t.
EXPECT_EQ(std::numeric_limits<size_t>::max(),
RenderProcessHostImpl::GetMaxRendererProcessCount());
// Add a few dummy process hosts.
ASSERT_NE(0u, kMaxRendererProcessCount);
ScopedVector<MockRenderProcessHost> hosts;
for (size_t i = 0; i < kMaxRendererProcessCount; ++i) {
hosts.push_back(new MockRenderProcessHost(browser_context()));
}
// Verify that the renderer sharing still won't happen.
GURL test_url("http://foo.com");
EXPECT_FALSE(RenderProcessHostImpl::ShouldTryToUseExistingProcessHost(
browser_context(), test_url));
}
#endif
} // namespace content } // namespace content
...@@ -562,6 +562,11 @@ TEST_F(SiteInstanceTest, ProcessSharingByType) { ...@@ -562,6 +562,11 @@ TEST_F(SiteInstanceTest, ProcessSharingByType) {
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess))
return; return;
// On Android by default the number of renderer hosts is unlimited and process
// sharing doesn't happen. We set the override so that the test can run
// everywhere.
RenderProcessHost::SetMaxRendererProcessCount(kMaxRendererProcessCount);
ChildProcessSecurityPolicyImpl* policy = ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance(); ChildProcessSecurityPolicyImpl::GetInstance();
...@@ -607,6 +612,9 @@ TEST_F(SiteInstanceTest, ProcessSharingByType) { ...@@ -607,6 +612,9 @@ TEST_F(SiteInstanceTest, ProcessSharingByType) {
} }
DrainMessageLoops(); DrainMessageLoops();
// Disable the process limit override.
RenderProcessHost::SetMaxRendererProcessCount(0u);
} }
// Test to ensure that HasWrongProcessForURL behaves properly for different // Test to ensure that HasWrongProcessForURL behaves properly for different
......
...@@ -94,20 +94,6 @@ public class BrowserStartupController { ...@@ -94,20 +94,6 @@ public class BrowserStartupController {
// Whether the async startup of the browser process is complete. // Whether the async startup of the browser process is complete.
private boolean mStartupDone; private boolean mStartupDone;
// Use single-process mode that runs the renderer on a separate thread in
// the main application.
public static final int MAX_RENDERERS_SINGLE_PROCESS = 0;
// Cap on the maximum number of renderer processes that can be requested.
// This is currently set to account for:
// 13: The maximum number of sandboxed processes we have available
// - 1: The regular New Tab Page
// - 1: The incognito New Tab Page
// - 1: A regular incognito tab
// - 1: Safety buffer (http://crbug.com/251279)
public static final int MAX_RENDERERS_LIMIT =
ChildProcessLauncher.MAX_REGISTERED_SANDBOXED_SERVICES - 4;
// This field is set after startup has been completed based on whether the startup was a success // This field is set after startup has been completed based on whether the startup was a success
// or not. It is used when later requests to startup come in that happen after the initial set // or not. It is used when later requests to startup come in that happen after the initial set
// of enqueued callbacks have been executed. // of enqueued callbacks have been executed.
...@@ -161,7 +147,7 @@ public class BrowserStartupController { ...@@ -161,7 +147,7 @@ public class BrowserStartupController {
// flag that indicates that we have kicked off starting the browser process. // flag that indicates that we have kicked off starting the browser process.
mHasStartedInitializingBrowserProcess = true; mHasStartedInitializingBrowserProcess = true;
prepareToStartBrowserProcess(MAX_RENDERERS_LIMIT); prepareToStartBrowserProcess(false);
setAsynchronousStartup(true); setAsynchronousStartup(true);
if (contentStart() > 0) { if (contentStart() > 0) {
...@@ -178,15 +164,15 @@ public class BrowserStartupController { ...@@ -178,15 +164,15 @@ public class BrowserStartupController {
* <p/> * <p/>
* Note that this can only be called on the UI thread. * Note that this can only be called on the UI thread.
* *
* @param maxRenderers The maximum number of renderer processes the browser may * @param singleProcess true iff the browser should run single-process, ie. keep renderers in
* create. Zero for single process mode. * the browser process
* @throws ProcessInitException * @throws ProcessInitException
*/ */
public void startBrowserProcessesSync(int maxRenderers) throws ProcessInitException { public void startBrowserProcessesSync(boolean singleProcess) throws ProcessInitException {
// If already started skip to checking the result // If already started skip to checking the result
if (!mStartupDone) { if (!mStartupDone) {
if (!mHasStartedInitializingBrowserProcess) { if (!mHasStartedInitializingBrowserProcess) {
prepareToStartBrowserProcess(maxRenderers); prepareToStartBrowserProcess(singleProcess);
} }
setAsynchronousStartup(false); setAsynchronousStartup(false);
...@@ -260,8 +246,8 @@ public class BrowserStartupController { ...@@ -260,8 +246,8 @@ public class BrowserStartupController {
} }
@VisibleForTesting @VisibleForTesting
void prepareToStartBrowserProcess(int maxRendererProcesses) throws ProcessInitException { void prepareToStartBrowserProcess(boolean singleProcess) throws ProcessInitException {
Log.i(TAG, "Initializing chromium process, renderers=" + maxRendererProcesses); Log.i(TAG, "Initializing chromium process, singleProcess=" + singleProcess);
// Normally Main.java will have kicked this off asynchronously for Chrome. But other // Normally Main.java will have kicked this off asynchronously for Chrome. But other
// ContentView apps like tests also need them so we make sure we've extracted resources // ContentView apps like tests also need them so we make sure we've extracted resources
...@@ -280,8 +266,7 @@ public class BrowserStartupController { ...@@ -280,8 +266,7 @@ public class BrowserStartupController {
// Now we really need to have the resources ready. // Now we really need to have the resources ready.
resourceExtractor.waitForCompletion(); resourceExtractor.waitForCompletion();
nativeSetCommandLineFlags(maxRendererProcesses, nativeSetCommandLineFlags(singleProcess, nativeIsPluginEnabled() ? getPlugins() : null);
nativeIsPluginEnabled() ? getPlugins() : null);
ContentMain.initApplicationContext(appContext); ContentMain.initApplicationContext(appContext);
} }
...@@ -292,18 +277,15 @@ public class BrowserStartupController { ...@@ -292,18 +277,15 @@ public class BrowserStartupController {
ResourceExtractor resourceExtractor = ResourceExtractor.get(mContext); ResourceExtractor resourceExtractor = ResourceExtractor.get(mContext);
resourceExtractor.startExtractingResources(); resourceExtractor.startExtractingResources();
resourceExtractor.waitForCompletion(); resourceExtractor.waitForCompletion();
nativeSetCommandLineFlags(false, null);
// Having a single renderer should be sufficient for tests. We can't have more than
// MAX_RENDERERS_LIMIT.
nativeSetCommandLineFlags(1 /* maxRenderers */, null);
} }
private String getPlugins() { private String getPlugins() {
return PepperPluginManager.getPlugins(mContext); return PepperPluginManager.getPlugins(mContext);
} }
private static native void nativeSetCommandLineFlags(int maxRenderProcesses, private static native void nativeSetCommandLineFlags(
String pluginDescriptor); boolean singleProcess, String pluginDescriptor);
// Is this an official build of Chrome? Only native code knows for sure. Official build // Is this an official build of Chrome? Only native code knows for sure. Official build
// knowledge is needed very early in process startup. // knowledge is needed very early in process startup.
......
...@@ -92,7 +92,8 @@ public class ChildProcessLauncher { ...@@ -92,7 +92,8 @@ public class ChildProcessLauncher {
ChromiumLinkerParams chromiumLinkerParams) { ChromiumLinkerParams chromiumLinkerParams) {
synchronized (mConnectionLock) { synchronized (mConnectionLock) {
if (mFreeConnectionIndices.isEmpty()) { if (mFreeConnectionIndices.isEmpty()) {
Log.w(TAG, "Ran out of service."); Log.e(TAG, "Ran out of services to allocate.");
assert false;
return null; return null;
} }
int slot = mFreeConnectionIndices.remove(0); int slot = mFreeConnectionIndices.remove(0);
......
...@@ -27,7 +27,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase { ...@@ -27,7 +27,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
private int mInitializedCounter = 0; private int mInitializedCounter = 0;
@Override @Override
void prepareToStartBrowserProcess(int numRenderers) throws ProcessInitException { void prepareToStartBrowserProcess(boolean singleProcess) throws ProcessInitException {
if (!mLibraryLoadSucceeds) { if (!mLibraryLoadSucceeds) {
throw new ProcessInitException( throw new ProcessInitException(
LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED); LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED);
...@@ -360,7 +360,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase { ...@@ -360,7 +360,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
@Override @Override
public void run() { public void run() {
try { try {
mController.startBrowserProcessesSync(1); mController.startBrowserProcessesSync(false);
} catch (Exception e) { } catch (Exception e) {
fail("Browser should have started successfully"); fail("Browser should have started successfully");
} }
...@@ -392,7 +392,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase { ...@@ -392,7 +392,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
// to do both these in a since Runnable instance. This avoids the // to do both these in a since Runnable instance. This avoids the
// unpredictable race that happens in real situations. // unpredictable race that happens in real situations.
try { try {
mController.startBrowserProcessesSync(1); mController.startBrowserProcessesSync(false);
} catch (Exception e) { } catch (Exception e) {
fail("Browser should have started successfully"); fail("Browser should have started successfully");
} }
...@@ -421,7 +421,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase { ...@@ -421,7 +421,7 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
@Override @Override
public void run() { public void run() {
try { try {
mController.startBrowserProcessesSync(1); mController.startBrowserProcessesSync(false);
} catch (Exception e) { } catch (Exception e) {
fail("Browser should have started successfully"); fail("Browser should have started successfully");
} }
......
...@@ -279,8 +279,8 @@ class CONTENT_EXPORT RenderProcessHost : public IPC::Sender, ...@@ -279,8 +279,8 @@ class CONTENT_EXPORT RenderProcessHost : public IPC::Sender,
// A value of zero means to use the default heuristic. // A value of zero means to use the default heuristic.
static void SetMaxRendererProcessCount(size_t count); static void SetMaxRendererProcessCount(size_t count);
// Returns the current max number of renderer processes used by the content // Returns the current maximum number of renderer process hosts kept by the
// module. // content module.
static size_t GetMaxRendererProcessCount(); static size_t GetMaxRendererProcessCount();
}; };
......
...@@ -78,8 +78,7 @@ public class ContentShellActivity extends Activity { ...@@ -78,8 +78,7 @@ public class ContentShellActivity extends Activity {
if (CommandLine.getInstance().hasSwitch(ContentSwitches.DUMP_RENDER_TREE)) { if (CommandLine.getInstance().hasSwitch(ContentSwitches.DUMP_RENDER_TREE)) {
try { try {
BrowserStartupController.get(this).startBrowserProcessesSync( BrowserStartupController.get(this).startBrowserProcessesSync(false);
BrowserStartupController.MAX_RENDERERS_LIMIT);
} catch (ProcessInitException e) { } catch (ProcessInitException e) {
Log.e(TAG, "Failed to load native library.", e); Log.e(TAG, "Failed to load native library.", e);
System.exit(-1); System.exit(-1);
......
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