Index: content/browser/memory/memory_coordinator_impl.cc |
diff --git a/content/browser/memory/memory_coordinator_impl.cc b/content/browser/memory/memory_coordinator_impl.cc |
index 5a24676c9386baf3b7a518f6f7c9240bfa6082e6..92a519f40df9f4f80a47a05a9ac16f523af4806b 100644 |
--- a/content/browser/memory/memory_coordinator_impl.cc |
+++ b/content/browser/memory/memory_coordinator_impl.cc |
@@ -4,18 +4,22 @@ |
#include "content/browser/memory/memory_coordinator_impl.h" |
+#include "base/memory/memory_coordinator_client_registry.h" |
#include "base/metrics/histogram_macros.h" |
+#include "base/process/process_handle.h" |
#include "base/process/process_metrics.h" |
#include "base/strings/string_number_conversions.h" |
#include "base/threading/thread_task_runner_handle.h" |
#include "base/trace_event/trace_event.h" |
#include "content/browser/memory/memory_monitor.h" |
#include "content/browser/memory/memory_state_updater.h" |
+#include "content/public/browser/content_browser_client.h" |
#include "content/public/browser/notification_service.h" |
#include "content/public/browser/notification_types.h" |
#include "content/public/browser/render_process_host.h" |
#include "content/public/browser/render_widget_host.h" |
#include "content/public/common/content_features.h" |
+#include "mojo/public/cpp/bindings/binding.h" |
namespace content { |
@@ -95,28 +99,72 @@ void RecordMetricsOnStateChange(base::MemoryState prev_state, |
} // namespace |
+// The implementation of MemoryCoordinatorHandle. See memory_coordinator.mojom |
+// for the role of this class. |
+class MemoryCoordinatorHandleImpl : public mojom::MemoryCoordinatorHandle { |
+ public: |
+ MemoryCoordinatorHandleImpl(mojom::MemoryCoordinatorHandleRequest request, |
+ MemoryCoordinatorImpl* coordinator, |
+ int render_process_id); |
+ ~MemoryCoordinatorHandleImpl() override; |
+ |
+ // mojom::MemoryCoordinatorHandle: |
+ void AddChild(mojom::ChildMemoryCoordinatorPtr child) override; |
+ |
+ mojom::ChildMemoryCoordinatorPtr& child() { return child_; } |
+ mojo::Binding<mojom::MemoryCoordinatorHandle>& binding() { return binding_; } |
+ |
+ private: |
+ MemoryCoordinatorImpl* coordinator_; |
+ int render_process_id_; |
+ mojom::ChildMemoryCoordinatorPtr child_; |
+ mojo::Binding<mojom::MemoryCoordinatorHandle> binding_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MemoryCoordinatorHandleImpl); |
+}; |
+ |
+MemoryCoordinatorHandleImpl::MemoryCoordinatorHandleImpl( |
+ mojom::MemoryCoordinatorHandleRequest request, |
+ MemoryCoordinatorImpl* coordinator, |
+ int render_process_id) |
+ : coordinator_(coordinator), |
+ render_process_id_(render_process_id), |
+ binding_(this, std::move(request)) { |
+ DCHECK(coordinator_); |
+} |
+ |
+MemoryCoordinatorHandleImpl::~MemoryCoordinatorHandleImpl() {} |
+ |
+void MemoryCoordinatorHandleImpl::AddChild( |
+ mojom::ChildMemoryCoordinatorPtr child) { |
+ DCHECK(!child_.is_bound()); |
+ child_ = std::move(child); |
+ coordinator_->OnChildAdded(render_process_id_); |
+} |
+ |
// SingletonTraits for MemoryCoordinator. Returns MemoryCoordinatorImpl |
// as an actual instance. |
-struct MemoryCoordinatorSingletonTraits |
- : public base::LeakySingletonTraits<MemoryCoordinator> { |
- static MemoryCoordinator* New() { |
+struct MemoryCoordinatorImplSingletonTraits |
+ : public base::LeakySingletonTraits<MemoryCoordinatorImpl> { |
+ static MemoryCoordinatorImpl* New() { |
return new MemoryCoordinatorImpl(base::ThreadTaskRunnerHandle::Get(), |
CreateMemoryMonitor()); |
} |
}; |
// static |
-MemoryCoordinator* MemoryCoordinator::GetInstance() { |
+MemoryCoordinatorImpl* MemoryCoordinatorImpl::GetInstance() { |
if (!base::FeatureList::IsEnabled(features::kMemoryCoordinator)) |
return nullptr; |
- return base::Singleton<MemoryCoordinator, |
- MemoryCoordinatorSingletonTraits>::get(); |
+ return base::Singleton<MemoryCoordinatorImpl, |
+ MemoryCoordinatorImplSingletonTraits>::get(); |
} |
MemoryCoordinatorImpl::MemoryCoordinatorImpl( |
scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
std::unique_ptr<MemoryMonitor> memory_monitor) |
- : memory_monitor_(std::move(memory_monitor)), |
+ : delegate_(GetContentClient()->browser()->GetMemoryCoordinatorDelegate()), |
+ memory_monitor_(std::move(memory_monitor)), |
state_updater_(base::MakeUnique<MemoryStateUpdater>(this, task_runner)) { |
DCHECK(memory_monitor_.get()); |
} |
@@ -134,10 +182,76 @@ void MemoryCoordinatorImpl::Start() { |
state_updater_->ScheduleUpdateState(base::TimeDelta()); |
} |
-void MemoryCoordinatorImpl::OnChildAdded(int render_process_id) { |
- // Populate the global state as an initial state of a newly created process. |
- auto new_state = ToMojomMemoryState(GetGlobalMemoryState()); |
- SetChildMemoryState(render_process_id, new_state); |
+void MemoryCoordinatorImpl::CreateHandle( |
+ int render_process_id, |
+ mojom::MemoryCoordinatorHandleRequest request) { |
+ std::unique_ptr<MemoryCoordinatorHandleImpl> handle( |
+ new MemoryCoordinatorHandleImpl(std::move(request), this, |
+ render_process_id)); |
+ handle->binding().set_connection_error_handler( |
+ base::Bind(&MemoryCoordinatorImpl::OnConnectionError, |
+ base::Unretained(this), render_process_id)); |
+ CreateChildInfoMapEntry(render_process_id, std::move(handle)); |
+} |
+ |
+bool MemoryCoordinatorImpl::SetChildMemoryState(int render_process_id, |
+ mojom::MemoryState memory_state) { |
+ // Can't set an invalid memory state. |
+ if (memory_state == mojom::MemoryState::UNKNOWN) |
+ return false; |
+ |
+ // Can't send a message to a child that doesn't exist. |
+ auto iter = children_.find(render_process_id); |
+ if (iter == children_.end()) |
+ return false; |
+ |
+ // Can't send a message to a child that isn't bound. |
+ if (!iter->second.handle->child().is_bound()) |
+ return false; |
+ |
+ memory_state = OverrideGlobalState(memory_state, iter->second); |
+ |
+ // A nop doesn't need to be sent, but is considered successful. |
+ if (iter->second.memory_state == memory_state) |
+ return true; |
+ |
+ // Can't suspend the given renderer. |
+ if (memory_state == mojom::MemoryState::SUSPENDED && |
+ !CanSuspendRenderer(render_process_id)) |
+ return false; |
+ |
+ // Update the internal state and send the message. |
+ iter->second.memory_state = memory_state; |
+ iter->second.handle->child()->OnStateChange(memory_state); |
+ return true; |
+} |
+ |
+mojom::MemoryState MemoryCoordinatorImpl::GetChildMemoryState( |
+ int render_process_id) const { |
+ auto iter = children_.find(render_process_id); |
+ if (iter == children_.end()) |
+ return mojom::MemoryState::UNKNOWN; |
+ return iter->second.memory_state; |
+} |
+ |
+void MemoryCoordinatorImpl::RecordMemoryPressure( |
+ base::MemoryPressureMonitor::MemoryPressureLevel level) { |
+ DCHECK(GetGlobalMemoryState() != base::MemoryState::UNKNOWN); |
+ int state = static_cast<int>(GetGlobalMemoryState()); |
+ switch (level) { |
+ case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: |
+ UMA_HISTOGRAM_ENUMERATION( |
+ "Memory.Coordinator.StateOnModerateNotificationReceived", |
+ state, base::kMemoryStateMax); |
+ break; |
+ case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: |
+ UMA_HISTOGRAM_ENUMERATION( |
+ "Memory.Coordinator.StateOnCriticalNotificationReceived", |
+ state, base::kMemoryStateMax); |
+ break; |
+ case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: |
+ NOTREACHED(); |
+ } |
} |
base::MemoryState MemoryCoordinatorImpl::GetGlobalMemoryState() const { |
@@ -202,6 +316,75 @@ bool MemoryCoordinatorImpl::ChangeStateIfNeeded(base::MemoryState prev_state, |
return true; |
} |
+void MemoryCoordinatorImpl::AddChildForTesting( |
+ int dummy_render_process_id, mojom::ChildMemoryCoordinatorPtr child) { |
+ mojom::MemoryCoordinatorHandlePtr mch; |
+ auto request = mojo::GetProxy(&mch); |
+ std::unique_ptr<MemoryCoordinatorHandleImpl> handle( |
+ new MemoryCoordinatorHandleImpl(std::move(request), this, |
+ dummy_render_process_id)); |
+ handle->AddChild(std::move(child)); |
+ CreateChildInfoMapEntry(dummy_render_process_id, std::move(handle)); |
+} |
+ |
+void MemoryCoordinatorImpl::OnConnectionError(int render_process_id) { |
+ children_.erase(render_process_id); |
+} |
+ |
+bool MemoryCoordinatorImpl::CanSuspendRenderer(int render_process_id) { |
+ // If there is no delegate (i.e. unittests), renderers are always suspendable. |
+ if (!delegate_) |
+ return true; |
+ auto* render_process_host = RenderProcessHost::FromID(render_process_id); |
+ if (!render_process_host || !render_process_host->IsProcessBackgrounded()) |
+ return false; |
+ if (render_process_host->GetWorkerRefCount() > 0) |
+ return false; |
+ return delegate_->CanSuspendBackgroundedRenderer(render_process_id); |
+} |
+ |
+void MemoryCoordinatorImpl::OnChildAdded(int render_process_id) { |
+ // Populate the global state as an initial state of a newly created process. |
+ auto new_state = ToMojomMemoryState(GetGlobalMemoryState()); |
+ SetChildMemoryState(render_process_id, new_state); |
+} |
+ |
+mojom::MemoryState MemoryCoordinatorImpl::OverrideGlobalState( |
+ mojom::MemoryState memory_state, |
+ const ChildInfo& child) { |
+ // We don't suspend foreground renderers. Throttle them instead. |
+ if (child.is_visible && memory_state == mojom::MemoryState::SUSPENDED) |
+ return mojom::MemoryState::THROTTLED; |
+#if defined(OS_ANDROID) |
+ // On Android, we throttle background renderers immediately. |
+ // TODO(bashi): Create a specialized class of MemoryCoordinator for Android |
+ // and move this ifdef to the class. |
+ if (!child.is_visible && memory_state == mojom::MemoryState::NORMAL) |
+ return mojom::MemoryState::THROTTLED; |
+ // TODO(bashi): Suspend background renderers after a certain period of time. |
+#endif // defined(OS_ANDROID) |
+ return memory_state; |
+} |
+ |
+void MemoryCoordinatorImpl::SetDelegateForTesting( |
+ std::unique_ptr<MemoryCoordinatorDelegate> delegate) { |
+ CHECK(!delegate_); |
+ delegate_ = std::move(delegate); |
+} |
+ |
+void MemoryCoordinatorImpl::CreateChildInfoMapEntry( |
+ int render_process_id, |
+ std::unique_ptr<MemoryCoordinatorHandleImpl> handle) { |
+ auto& child_info = children_[render_process_id]; |
+ // Process always start with normal memory state. |
+ // We'll set renderer's memory state to the current global state when the |
+ // corresponding renderer process is ready to communicate. Renderer processes |
+ // call AddChild() when they are ready. |
+ child_info.memory_state = mojom::MemoryState::NORMAL; |
+ child_info.is_visible = true; |
+ child_info.handle = std::move(handle); |
+} |
+ |
void MemoryCoordinatorImpl::NotifyStateToClients() { |
auto state = GetCurrentMemoryState(); |
base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); |
@@ -244,4 +427,12 @@ void MemoryCoordinatorImpl::RecordStateChange(MemoryState prev_state, |
total_private_kb / 1024); |
} |
+MemoryCoordinatorImpl::ChildInfo::ChildInfo() {} |
+ |
+MemoryCoordinatorImpl::ChildInfo::ChildInfo(const ChildInfo& rhs) { |
+ // This is a nop, but exists for compatibility with STL containers. |
+} |
+ |
+MemoryCoordinatorImpl::ChildInfo::~ChildInfo() {} |
+ |
} // namespace content |