Chromium Code Reviews| 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 67c5e5d64a18b8230867611b904860f3603a688c..509e1cb2cabb98a816f0f3afe7dfed571468a9c7 100644 |
| --- a/content/browser/renderer_host/render_process_host_impl.cc |
| +++ b/content/browser/renderer_host/render_process_host_impl.cc |
| @@ -463,6 +463,131 @@ 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, false /* is_for_guests_only */); |
| + spare_render_process_host_->AddObserver(this); |
| + spare_render_process_host_->Init(); |
| + } |
| + |
| + RenderProcessHost* MaybeTakeSpareRenderProcessHost( |
| + const BrowserContext* browser_context, |
| + const StoragePartition* partition, |
| + bool is_for_guests_only) { |
| + 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(static_cast<RenderProcessHostImpl*>(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; |
| + } |
| + } |
| + |
| + 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); |
| + } |
| + |
| + void CleanupSpareRenderProcessHost() { |
| + if (spare_render_process_host_) { |
| + spare_render_process_host_->Cleanup(); |
| + DropSpareRenderProcessHost(spare_render_process_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_; |
| + |
| + // 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; |
| @@ -476,17 +601,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, is_for_guests_only); |
| host->SetIsNeverSuitableForReuse(); |
| return host; |
| } |
| @@ -496,9 +621,8 @@ 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, false /* is for guests only */); |
| host_->SetIsNeverSuitableForReuse(); |
| host_->AddObserver(this); |
| @@ -517,7 +641,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( |
| @@ -972,6 +1096,11 @@ RenderProcessHostImpl::GetInProcessRendererThreadForTesting() { |
| return g_in_process_thread; |
| } |
| +bool RenderProcessHostImpl::HostHasNotBeenUsed() { |
|
Charlie Reis
2017/06/30 21:42:16
nit: This should be put in the same order as in th
mattcary
2017/07/03 08:03:02
Done.
|
| + return IsUnused() && listeners_.IsEmpty() && GetWorkerRefCount() == 0 && |
| + pending_views_ == 0; |
| +} |
| + |
| // static |
| size_t RenderProcessHost::GetMaxRendererProcessCount() { |
| if (g_max_renderer_count_override) |
| @@ -1029,6 +1158,38 @@ void RenderProcessHost::SetMaxRendererProcessCount(size_t count) { |
| g_max_renderer_count_override = count; |
| } |
| +// static |
| +RenderProcessHost* RenderProcessHostImpl::CreateRenderProcessHost( |
| + BrowserContext* browser_context, |
| + StoragePartitionImpl* storage_partition_impl, |
| + bool is_for_guests_only) { |
| + if (g_render_process_host_factory_) { |
| + return g_render_process_host_factory_->CreateRenderProcessHost( |
| + browser_context); |
| + } |
| + |
| + return new RenderProcessHostImpl(browser_context, storage_partition_impl, |
| + is_for_guests_only); |
| +} |
| + |
| +// static |
| +RenderProcessHost* RenderProcessHostImpl::CreateOrUseSpareRenderProcessHost( |
| + BrowserContext* browser_context, |
| + StoragePartitionImpl* storage_partition_impl, |
| + bool is_for_guests_only) { |
| + RenderProcessHost* render_process_host = |
| + g_spare_render_process_host_manager.Get().MaybeTakeSpareRenderProcessHost( |
| + browser_context, storage_partition_impl, is_for_guests_only); |
| + |
| + if (!render_process_host) { |
| + render_process_host = CreateRenderProcessHost( |
| + browser_context, storage_partition_impl, is_for_guests_only); |
| + } |
| + |
| + DCHECK(render_process_host); |
| + return render_process_host; |
| +} |
| + |
| RenderProcessHostImpl::RenderProcessHostImpl( |
| BrowserContext* browser_context, |
| StoragePartitionImpl* storage_partition_impl, |
| @@ -1882,6 +2043,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(); |
| } |
| @@ -2113,6 +2275,12 @@ void RenderProcessHostImpl::RemoveExpectedNavigationToSite( |
| tracker->DecrementSiteProcessCount(site_url, render_process_host->GetID()); |
| } |
| +// static |
| +RenderProcessHost* |
| +RenderProcessHostImpl::GetSpareRenderProcessHostForTesting() { |
| + return g_spare_render_process_host_manager.Get().spare_render_process_host(); |
| +} |
| + |
| bool RenderProcessHostImpl::IsForGuestsOnly() const { |
| return is_for_guests_only_; |
| } |
| @@ -3066,6 +3234,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_; |
| @@ -3144,6 +3319,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. |
| + g_spare_render_process_host_manager.Get().DropSpareRenderProcessHost( |
| + suitable_renderers[random_index]); |
| return suitable_renderers[random_index]; |
| } |
| @@ -3270,18 +3449,11 @@ RenderProcessHost* RenderProcessHostImpl::GetProcessHostForSiteInstance( |
| render_process_host = GetExistingProcessHost(browser_context, site_url); |
| } |
| - // Otherwise (or if that fails), create a new one. |
| if (!render_process_host) { |
|
Charlie Reis
2017/06/30 21:42:16
Let's keep a comment here, like:
// Otherwise, us
mattcary
2017/07/03 08:03:02
Done.
|
| - 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); |
| - } |
| + StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>( |
| + BrowserContext::GetStoragePartition(browser_context, site_instance)); |
| + render_process_host = CreateOrUseSpareRenderProcessHost( |
| + browser_context, partition, is_for_guests_only); |
| } |
| if (is_unmatched_service_worker) { |