Index: content/browser/renderer_host/render_process_host_impl.cc |
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc |
index 08e1987116714336dec6bd19683f000acf061740..785f8dc3e744c132cdefee6457fbe47a53027556 100644 |
--- a/content/browser/renderer_host/render_process_host_impl.cc |
+++ b/content/browser/renderer_host/render_process_host_impl.cc |
@@ -464,6 +464,159 @@ class SessionStorageHolder : public base::SupportsUserData::Data { |
DISALLOW_COPY_AND_ASSIGN(SessionStorageHolder); |
}; |
+// This class manages spare RenderProcessHosts. |
+// |
+// There is a singleton instance of this class which manages a single spare |
+// renderer (g_spare_render_process_host_manager, below). This class |
+// encapsulates the implementation of |
+// RenderProcessHost::WarmupSpareRenderProcessHost() |
+// |
+// RenderProcessHostImpl should call |
+// SpareRenderProcessHostManager::MaybeTakeSpareRenderProcessHost when creating |
+// a new RPH. In this implementation, the spare renderer is bound to a |
+// BrowserContext and its default StoragePartition. If |
+// MaybeTakeSpareRenderProcessHost is called with a BrowserContext and |
+// StoragePartition that does not match, the spare renderer is discarded. In |
+// particular, only the default StoragePartition will be able to use a spare |
+// renderer. The spare renderer will also not be used as a guest renderer |
+// (is_for_guests_ == true). |
+// |
+// It is safe to call WarmupSpareRenderProcessHost multiple times, although if |
+// called in a context where the spare renderer is not likely to be used |
+// performance may suffer due to the unnecessary RPH creation. |
+class SpareRenderProcessHostManager : public RenderProcessHostObserver { |
+ public: |
+ SpareRenderProcessHostManager() {} |
+ |
+ void WarmupSpareRenderProcessHost(BrowserContext* browser_context) { |
+ StoragePartitionImpl* current_partition = |
+ static_cast<StoragePartitionImpl*>( |
+ BrowserContext::GetStoragePartition(browser_context, nullptr)); |
+ |
+ if (spare_render_process_host_ && |
+ matching_browser_context_ == browser_context && |
+ matching_storage_partition_ == current_partition) |
+ return; // Nothing to warm up. |
+ |
+ CleanupSpareRenderProcessHost(); |
+ |
+ // Don't create a spare renderer if we're using --single-process or if we've |
+ // got too many processes. See also ShouldTryToUseExistingProcessHost in |
+ // this file. |
+ if (RenderProcessHost::run_renderer_in_process() || |
+ g_all_hosts.Get().size() >= |
+ RenderProcessHostImpl::GetMaxRendererProcessCount()) |
+ return; |
+ |
+ matching_browser_context_ = browser_context; |
+ matching_storage_partition_ = current_partition; |
+ |
+ spare_render_process_host_ = RenderProcessHostImpl::CreateRenderProcessHost( |
+ browser_context, current_partition, nullptr, |
+ false /* is_for_guests_only */); |
+ spare_render_process_host_->AddObserver(this); |
+ spare_render_process_host_->Init(); |
+ } |
+ |
+ // If |partition| is null, this gets the default partition from the browser |
+ // context. |
+ RenderProcessHost* MaybeTakeSpareRenderProcessHost( |
+ BrowserContext* browser_context, |
+ StoragePartition* partition, |
+ SiteInstance* site_instance, |
+ bool is_for_guests_only) { |
+ if (spare_render_process_host_ && |
+ browser_context == matching_browser_context_ && !is_for_guests_only && |
+ !partition) { |
+ // If the spare renderer matches for everything but possibly the storage |
+ // partition, and the passed-in partition is null, get the default storage |
+ // partition. If this is the case, the default storage partition will |
+ // already have been created and there is no possibility of breaking tests |
+ // by GetDefaultStoragePartition prematurely creating one. |
+ partition = |
+ BrowserContext::GetStoragePartition(browser_context, site_instance); |
+ } |
+ |
+ if (!spare_render_process_host_ || |
+ browser_context != matching_browser_context_ || |
+ partition != matching_storage_partition_ || is_for_guests_only) { |
+ // As a new RenderProcessHost will almost certainly be created, we cleanup |
+ // the non-matching one so as not to waste resources. |
+ CleanupSpareRenderProcessHost(); |
+ return nullptr; |
+ } |
+ |
+ CHECK(spare_render_process_host_->HostHasNotBeenUsed()); |
+ RenderProcessHost* rph = spare_render_process_host_; |
+ DropSpareRenderProcessHost(spare_render_process_host_); |
+ return rph; |
+ } |
+ |
+ // Remove |host| as a possible spare renderer. Does not shut it down cleanly; |
+ // the assumption is that the host was shutdown somewhere else and has |
+ // notifying the SpareRenderProcessHostManager. |
+ void DropSpareRenderProcessHost(RenderProcessHost* host) { |
+ if (spare_render_process_host_ && spare_render_process_host_ == host) { |
+ spare_render_process_host_->RemoveObserver(this); |
+ spare_render_process_host_ = nullptr; |
+ } |
+ } |
+ |
+ // Remove |host| as a possible spare renderer. If |host| is not the spare |
+ // renderer, then shut down the spare renderer. The idea is that a navigation |
+ // was just made to |host|, and we do not expect another immediate navigation, |
+ // so that the spare renderer can be dropped in order to free up resources. |
+ void DropSpareOnProcessCreation(RenderProcessHost* new_host) { |
+ if (spare_render_process_host_ == new_host) { |
+ DropSpareRenderProcessHost(new_host); |
+ } else { |
+ CleanupSpareRenderProcessHost(); |
+ } |
+ } |
+ |
+ // Gracefully remove and cleanup a spare RenderProcessHost if it exists. |
+ void CleanupSpareRenderProcessHost() { |
+ if (spare_render_process_host_) { |
+ spare_render_process_host_->Cleanup(); |
+ DropSpareRenderProcessHost(spare_render_process_host_); |
+ } |
+ } |
+ |
+ RenderProcessHost* spare_render_process_host() { |
+ return spare_render_process_host_; |
+ } |
+ |
+ private: |
+ // RenderProcessHostObserver |
+ void RenderProcessWillExit(RenderProcessHost* host) override { |
+ DropSpareRenderProcessHost(host); |
+ } |
+ |
+ void RenderProcessExited(RenderProcessHost* host, |
+ base::TerminationStatus unused_status, |
+ int unused_exit_code) override { |
+ DropSpareRenderProcessHost(host); |
+ } |
+ |
+ void RenderProcessHostDestroyed(RenderProcessHost* host) override { |
+ DropSpareRenderProcessHost(host); |
+ } |
+ |
+ // This is a bare pointer, because RenderProcessHost manages the lifetime of |
+ // all its instances; see g_all_hosts, above. |
+ RenderProcessHost* spare_render_process_host_ = nullptr; |
+ |
+ // Used only to check if a creation request matches the spare, and not |
+ // accessed. |
+ const BrowserContext* matching_browser_context_ = nullptr; |
+ const StoragePartition* matching_storage_partition_ = nullptr; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SpareRenderProcessHostManager); |
+}; |
+ |
+base::LazyInstance<SpareRenderProcessHostManager>::Leaky |
+ g_spare_render_process_host_manager = LAZY_INSTANCE_INITIALIZER; |
+ |
const void* const kDefaultSubframeProcessHostHolderKey = |
&kDefaultSubframeProcessHostHolderKey; |
@@ -477,17 +630,17 @@ class DefaultSubframeProcessHostHolder : public base::SupportsUserData::Data, |
// Gets the correct render process to use for this SiteInstance. |
RenderProcessHost* GetProcessHost(SiteInstance* site_instance, |
bool is_for_guests_only) { |
- StoragePartition* default_partition = |
- BrowserContext::GetDefaultStoragePartition(browser_context_); |
- StoragePartition* partition = |
- BrowserContext::GetStoragePartition(browser_context_, site_instance); |
+ StoragePartitionImpl* default_partition = |
+ static_cast<StoragePartitionImpl*>( |
+ BrowserContext::GetDefaultStoragePartition(browser_context_)); |
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>( |
+ BrowserContext::GetStoragePartition(browser_context_, site_instance)); |
// Is this the default storage partition? If it isn't, then just give it its |
// own non-shared process. |
if (partition != default_partition || is_for_guests_only) { |
- RenderProcessHostImpl* host = new RenderProcessHostImpl( |
- browser_context_, static_cast<StoragePartitionImpl*>(partition), |
- is_for_guests_only); |
+ RenderProcessHost* host = RenderProcessHostImpl::CreateRenderProcessHost( |
+ browser_context_, partition, site_instance, is_for_guests_only); |
host->SetIsNeverSuitableForReuse(); |
return host; |
} |
@@ -497,9 +650,9 @@ class DefaultSubframeProcessHostHolder : public base::SupportsUserData::Data, |
if (host_) |
return host_; |
- host_ = new RenderProcessHostImpl( |
- browser_context_, static_cast<StoragePartitionImpl*>(partition), |
- false /* for guests only */); |
+ host_ = RenderProcessHostImpl::CreateOrUseSpareRenderProcessHost( |
+ browser_context_, partition, site_instance, |
+ false /* is for guests only */); |
host_->SetIsNeverSuitableForReuse(); |
host_->AddObserver(this); |
@@ -518,7 +671,7 @@ class DefaultSubframeProcessHostHolder : public base::SupportsUserData::Data, |
// The default subframe render process used for the default storage partition |
// of this BrowserContext. |
- RenderProcessHostImpl* host_ = nullptr; |
+ RenderProcessHost* host_ = nullptr; |
}; |
void CreateMemoryCoordinatorHandle( |
@@ -1038,6 +1191,47 @@ void RenderProcessHost::SetMaxRendererProcessCount(size_t count) { |
g_max_renderer_count_override = count; |
} |
+// static |
+RenderProcessHost* RenderProcessHostImpl::CreateRenderProcessHost( |
+ BrowserContext* browser_context, |
+ StoragePartitionImpl* storage_partition_impl, |
+ SiteInstance* site_instance, |
+ bool is_for_guests_only) { |
+ if (g_render_process_host_factory_) { |
+ return g_render_process_host_factory_->CreateRenderProcessHost( |
+ browser_context); |
+ } |
+ |
+ if (!storage_partition_impl) { |
+ storage_partition_impl = static_cast<StoragePartitionImpl*>( |
+ BrowserContext::GetStoragePartition(browser_context, site_instance)); |
+ } |
+ |
+ return new RenderProcessHostImpl(browser_context, storage_partition_impl, |
+ is_for_guests_only); |
+} |
+ |
+// static |
+RenderProcessHost* RenderProcessHostImpl::CreateOrUseSpareRenderProcessHost( |
+ BrowserContext* browser_context, |
+ StoragePartitionImpl* storage_partition_impl, |
+ SiteInstance* site_instance, |
+ bool is_for_guests_only) { |
+ RenderProcessHost* render_process_host = |
+ g_spare_render_process_host_manager.Get().MaybeTakeSpareRenderProcessHost( |
+ browser_context, storage_partition_impl, site_instance, |
+ is_for_guests_only); |
+ |
+ if (!render_process_host) { |
+ render_process_host = |
+ CreateRenderProcessHost(browser_context, storage_partition_impl, |
+ site_instance, is_for_guests_only); |
+ } |
+ |
+ DCHECK(render_process_host); |
+ return render_process_host; |
+} |
+ |
RenderProcessHostImpl::RenderProcessHostImpl( |
BrowserContext* browser_context, |
StoragePartitionImpl* storage_partition_impl, |
@@ -1896,6 +2090,7 @@ void RenderProcessHostImpl::ForceReleaseWorkerRefCounts() { |
return; |
service_worker_ref_count_ = 0; |
shared_worker_ref_count_ = 0; |
+ // Cleaning up will also remove this from the SpareRenderProcessHostManager. |
Cleanup(); |
} |
@@ -2127,6 +2322,22 @@ void RenderProcessHostImpl::RemoveExpectedNavigationToSite( |
tracker->DecrementSiteProcessCount(site_url, render_process_host->GetID()); |
} |
+// static |
+void RenderProcessHostImpl::CleanupSpareRenderProcessHost() { |
+ g_spare_render_process_host_manager.Get().CleanupSpareRenderProcessHost(); |
+} |
+ |
+// static |
+RenderProcessHost* |
+RenderProcessHostImpl::GetSpareRenderProcessHostForTesting() { |
+ return g_spare_render_process_host_manager.Get().spare_render_process_host(); |
+} |
+ |
+bool RenderProcessHostImpl::HostHasNotBeenUsed() { |
+ return IsUnused() && listeners_.IsEmpty() && GetWorkerRefCount() == 0 && |
+ pending_views_ == 0; |
+} |
+ |
bool RenderProcessHostImpl::IsForGuestsOnly() const { |
return is_for_guests_only_; |
} |
@@ -3071,6 +3282,13 @@ bool RenderProcessHostImpl::IsSuitableHost(RenderProcessHost* host, |
return GetContentClient()->browser()->IsSuitableHost(host, site_url); |
} |
+// static |
+void RenderProcessHost::WarmupSpareRenderProcessHost( |
+ content::BrowserContext* browser_context) { |
+ g_spare_render_process_host_manager.Get().WarmupSpareRenderProcessHost( |
+ browser_context); |
+} |
+ |
// static |
bool RenderProcessHost::run_renderer_in_process() { |
return g_run_renderer_in_process_; |
@@ -3149,6 +3367,10 @@ RenderProcessHost* RenderProcessHost::GetExistingProcessHost( |
if (!suitable_renderers.empty()) { |
int suitable_count = static_cast<int>(suitable_renderers.size()); |
int random_index = base::RandInt(0, suitable_count - 1); |
+ // If the process chosen was the spare RenderProcessHost, ensure it won't be |
+ // used as a spare in the future, or drop the spare if it wasn't used. |
+ g_spare_render_process_host_manager.Get().DropSpareOnProcessCreation( |
+ suitable_renderers[random_index]); |
return suitable_renderers[random_index]; |
} |
@@ -3275,18 +3497,14 @@ RenderProcessHost* RenderProcessHostImpl::GetProcessHostForSiteInstance( |
render_process_host = GetExistingProcessHost(browser_context, site_url); |
} |
- // Otherwise (or if that fails), create a new one. |
+ // Otherwise, use the spare RenderProcessHost or create a new one. |
if (!render_process_host) { |
- if (g_render_process_host_factory_) { |
- render_process_host = |
- g_render_process_host_factory_->CreateRenderProcessHost( |
- browser_context); |
- } else { |
- StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>( |
- BrowserContext::GetStoragePartition(browser_context, site_instance)); |
- render_process_host = new RenderProcessHostImpl( |
- browser_context, partition, is_for_guests_only); |
- } |
+ // Pass a null StoragePartition. Tests with TestBrowserContext using a |
+ // RenderProcessHostFactory may not instantiate a StoragePartition, and |
+ // creating one here with GetStoragePartition() can run into cross-thread |
+ // issues as TestBrowserContext initialization is done on the main thread. |
+ render_process_host = CreateOrUseSpareRenderProcessHost( |
+ browser_context, nullptr, site_instance, is_for_guests_only); |
} |
if (is_unmatched_service_worker) { |