OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/browser/memory/memory_coordinator.h" | |
6 | |
7 #include "base/memory/memory_coordinator_client_registry.h" | |
8 #include "base/metrics/histogram_macros.h" | |
9 #include "content/public/browser/content_browser_client.h" | |
10 #include "content/public/browser/render_process_host.h" | |
11 #include "content/public/common/content_client.h" | |
12 | |
13 namespace content { | |
14 | |
15 // The implementation of MemoryCoordinatorHandle. See memory_coordinator.mojom | |
16 // for the role of this class. | |
17 class MemoryCoordinatorHandleImpl : public mojom::MemoryCoordinatorHandle { | |
18 public: | |
19 MemoryCoordinatorHandleImpl(mojom::MemoryCoordinatorHandleRequest request, | |
20 MemoryCoordinator* coordinator, | |
21 int render_process_id); | |
22 ~MemoryCoordinatorHandleImpl() override; | |
23 | |
24 // mojom::MemoryCoordinatorHandle: | |
25 void AddChild(mojom::ChildMemoryCoordinatorPtr child) override; | |
26 | |
27 mojom::ChildMemoryCoordinatorPtr& child() { return child_; } | |
28 mojo::Binding<mojom::MemoryCoordinatorHandle>& binding() { return binding_; } | |
29 | |
30 private: | |
31 MemoryCoordinator* coordinator_; | |
32 int render_process_id_; | |
33 mojom::ChildMemoryCoordinatorPtr child_; | |
34 mojo::Binding<mojom::MemoryCoordinatorHandle> binding_; | |
35 | |
36 DISALLOW_COPY_AND_ASSIGN(MemoryCoordinatorHandleImpl); | |
37 }; | |
38 | |
39 MemoryCoordinatorHandleImpl::MemoryCoordinatorHandleImpl( | |
40 mojom::MemoryCoordinatorHandleRequest request, | |
41 MemoryCoordinator* coordinator, | |
42 int render_process_id) | |
43 : coordinator_(coordinator), | |
44 render_process_id_(render_process_id), | |
45 binding_(this, std::move(request)) { | |
46 DCHECK(coordinator_); | |
47 } | |
48 | |
49 MemoryCoordinatorHandleImpl::~MemoryCoordinatorHandleImpl() {} | |
50 | |
51 void MemoryCoordinatorHandleImpl::AddChild( | |
52 mojom::ChildMemoryCoordinatorPtr child) { | |
53 DCHECK(!child_.is_bound()); | |
54 child_ = std::move(child); | |
55 coordinator_->OnChildAdded(render_process_id_); | |
56 } | |
57 | |
58 MemoryCoordinator::MemoryCoordinator() | |
59 : delegate_(GetContentClient()->browser()->GetMemoryCoordinatorDelegate()) { | |
60 } | |
61 | |
62 MemoryCoordinator::~MemoryCoordinator() {} | |
63 | |
64 void MemoryCoordinator::CreateHandle( | |
65 int render_process_id, | |
66 mojom::MemoryCoordinatorHandleRequest request) { | |
67 std::unique_ptr<MemoryCoordinatorHandleImpl> handle( | |
68 new MemoryCoordinatorHandleImpl(std::move(request), this, | |
69 render_process_id)); | |
70 handle->binding().set_connection_error_handler( | |
71 base::Bind(&MemoryCoordinator::OnConnectionError, base::Unretained(this), | |
72 render_process_id)); | |
73 CreateChildInfoMapEntry(render_process_id, std::move(handle)); | |
74 } | |
75 | |
76 bool MemoryCoordinator::SetChildMemoryState(int render_process_id, | |
77 mojom::MemoryState memory_state) { | |
78 // Can't set an invalid memory state. | |
79 if (memory_state == mojom::MemoryState::UNKNOWN) | |
80 return false; | |
81 | |
82 // Can't send a message to a child that doesn't exist. | |
83 auto iter = children_.find(render_process_id); | |
84 if (iter == children_.end()) | |
85 return false; | |
86 | |
87 // Can't send a message to a child that isn't bound. | |
88 if (!iter->second.handle->child().is_bound()) | |
89 return false; | |
90 | |
91 memory_state = OverrideGlobalState(memory_state, iter->second); | |
92 | |
93 // A nop doesn't need to be sent, but is considered successful. | |
94 if (iter->second.memory_state == memory_state) | |
95 return true; | |
96 | |
97 // Can't suspend the given renderer. | |
98 if (memory_state == mojom::MemoryState::SUSPENDED && | |
99 !CanSuspendRenderer(render_process_id)) | |
100 return false; | |
101 | |
102 // Update the internal state and send the message. | |
103 iter->second.memory_state = memory_state; | |
104 iter->second.handle->child()->OnStateChange(memory_state); | |
105 return true; | |
106 } | |
107 | |
108 mojom::MemoryState MemoryCoordinator::GetChildMemoryState( | |
109 int render_process_id) const { | |
110 auto iter = children_.find(render_process_id); | |
111 if (iter == children_.end()) | |
112 return mojom::MemoryState::UNKNOWN; | |
113 return iter->second.memory_state; | |
114 } | |
115 | |
116 void MemoryCoordinator::RecordMemoryPressure( | |
117 base::MemoryPressureMonitor::MemoryPressureLevel level) { | |
118 DCHECK(GetGlobalMemoryState() != base::MemoryState::UNKNOWN); | |
119 int state = static_cast<int>(GetGlobalMemoryState()); | |
120 switch (level) { | |
121 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: | |
122 UMA_HISTOGRAM_ENUMERATION( | |
123 "Memory.Coordinator.StateOnModerateNotificationReceived", | |
124 state, base::kMemoryStateMax); | |
125 break; | |
126 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: | |
127 UMA_HISTOGRAM_ENUMERATION( | |
128 "Memory.Coordinator.StateOnCriticalNotificationReceived", | |
129 state, base::kMemoryStateMax); | |
130 break; | |
131 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: | |
132 NOTREACHED(); | |
133 } | |
134 } | |
135 | |
136 base::MemoryState MemoryCoordinator::GetGlobalMemoryState() const { | |
137 return base::MemoryState::UNKNOWN; | |
138 } | |
139 | |
140 base::MemoryState MemoryCoordinator::GetCurrentMemoryState() const { | |
141 return base::MemoryState::UNKNOWN; | |
142 } | |
143 | |
144 void MemoryCoordinator::SetCurrentMemoryStateForTesting( | |
145 base::MemoryState memory_state) { | |
146 } | |
147 | |
148 void MemoryCoordinator::AddChildForTesting( | |
149 int dummy_render_process_id, mojom::ChildMemoryCoordinatorPtr child) { | |
150 mojom::MemoryCoordinatorHandlePtr mch; | |
151 auto request = mojo::GetProxy(&mch); | |
152 std::unique_ptr<MemoryCoordinatorHandleImpl> handle( | |
153 new MemoryCoordinatorHandleImpl(std::move(request), this, | |
154 dummy_render_process_id)); | |
155 handle->AddChild(std::move(child)); | |
156 CreateChildInfoMapEntry(dummy_render_process_id, std::move(handle)); | |
157 } | |
158 | |
159 void MemoryCoordinator::OnConnectionError(int render_process_id) { | |
160 children_.erase(render_process_id); | |
161 } | |
162 | |
163 bool MemoryCoordinator::CanSuspendRenderer(int render_process_id) { | |
164 // If there is no delegate (i.e. unittests), renderers are always suspendable. | |
165 if (!delegate_) | |
166 return true; | |
167 auto* render_process_host = RenderProcessHost::FromID(render_process_id); | |
168 if (!render_process_host || !render_process_host->IsProcessBackgrounded()) | |
169 return false; | |
170 if (render_process_host->GetWorkerRefCount() > 0) | |
171 return false; | |
172 return delegate_->CanSuspendBackgroundedRenderer(render_process_id); | |
173 } | |
174 | |
175 mojom::MemoryState MemoryCoordinator::OverrideGlobalState( | |
176 mojom::MemoryState memory_state, | |
177 const ChildInfo& child) { | |
178 // We don't suspend foreground renderers. Throttle them instead. | |
179 if (child.is_visible && memory_state == mojom::MemoryState::SUSPENDED) | |
180 return mojom::MemoryState::THROTTLED; | |
181 #if defined(OS_ANDROID) | |
182 // On Android, we throttle background renderers immediately. | |
183 // TODO(bashi): Create a specialized class of MemoryCoordinator for Android | |
184 // and move this ifdef to the class. | |
185 if (!child.is_visible && memory_state == mojom::MemoryState::NORMAL) | |
186 return mojom::MemoryState::THROTTLED; | |
187 // TODO(bashi): Suspend background renderers after a certain period of time. | |
188 #endif // defined(OS_ANDROID) | |
189 return memory_state; | |
190 } | |
191 | |
192 void MemoryCoordinator::SetDelegateForTesting( | |
193 std::unique_ptr<MemoryCoordinatorDelegate> delegate) { | |
194 CHECK(!delegate_); | |
195 delegate_ = std::move(delegate); | |
196 } | |
197 | |
198 void MemoryCoordinator::CreateChildInfoMapEntry( | |
199 int render_process_id, | |
200 std::unique_ptr<MemoryCoordinatorHandleImpl> handle) { | |
201 auto& child_info = children_[render_process_id]; | |
202 // Process always start with normal memory state. | |
203 // We'll set renderer's memory state to the current global state when the | |
204 // corresponding renderer process is ready to communicate. Renderer processes | |
205 // call AddChild() when they are ready. | |
206 child_info.memory_state = mojom::MemoryState::NORMAL; | |
207 child_info.is_visible = true; | |
208 child_info.handle = std::move(handle); | |
209 } | |
210 | |
211 MemoryCoordinator::ChildInfo::ChildInfo() {} | |
212 | |
213 MemoryCoordinator::ChildInfo::ChildInfo(const ChildInfo& rhs) { | |
214 // This is a nop, but exists for compatibility with STL containers. | |
215 } | |
216 | |
217 MemoryCoordinator::ChildInfo::~ChildInfo() {} | |
218 | |
219 } // namespace content | |
OLD | NEW |