Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/memory/memory_coordinator_impl.h" | 5 #include "content/browser/memory/memory_coordinator_impl.h" |
| 6 | 6 |
| 7 #include "base/memory/memory_coordinator_client_registry.h" | 7 #include "base/memory/memory_coordinator_client_registry.h" |
| 8 #include "base/memory/ptr_util.h" | 8 #include "base/memory/ptr_util.h" |
| 9 #include "base/metrics/histogram_macros.h" | 9 #include "base/metrics/histogram_macros.h" |
| 10 #include "base/process/process_handle.h" | 10 #include "base/process/process_handle.h" |
| 11 #include "base/threading/thread_task_runner_handle.h" | 11 #include "base/threading/thread_task_runner_handle.h" |
| 12 #include "base/trace_event/trace_event.h" | 12 #include "base/trace_event/trace_event.h" |
| 13 #include "content/browser/memory/memory_condition_observer.h" | |
| 13 #include "content/browser/memory/memory_monitor.h" | 14 #include "content/browser/memory/memory_monitor.h" |
| 14 #include "content/browser/memory/memory_state_updater.h" | |
| 15 #include "content/public/browser/content_browser_client.h" | 15 #include "content/public/browser/content_browser_client.h" |
| 16 #include "content/public/browser/notification_service.h" | 16 #include "content/public/browser/notification_service.h" |
| 17 #include "content/public/browser/notification_types.h" | 17 #include "content/public/browser/notification_types.h" |
| 18 #include "content/public/browser/render_process_host.h" | 18 #include "content/public/browser/render_process_host.h" |
| 19 #include "content/public/browser/render_widget_host.h" | 19 #include "content/public/browser/render_widget_host.h" |
| 20 #include "content/public/common/content_features.h" | 20 #include "content/public/common/content_features.h" |
| 21 #include "mojo/public/cpp/bindings/binding.h" | 21 #include "mojo/public/cpp/bindings/binding.h" |
| 22 | 22 |
| 23 namespace content { | 23 namespace content { |
| 24 | 24 |
| 25 namespace { | 25 namespace { |
| 26 | 26 |
| 27 const int kDefaultMinimumTransitionPeriodSeconds = 30; | |
| 28 | |
| 27 mojom::MemoryState ToMojomMemoryState(base::MemoryState state) { | 29 mojom::MemoryState ToMojomMemoryState(base::MemoryState state) { |
| 28 switch (state) { | 30 switch (state) { |
| 29 case base::MemoryState::UNKNOWN: | 31 case base::MemoryState::UNKNOWN: |
| 30 return mojom::MemoryState::UNKNOWN; | 32 return mojom::MemoryState::UNKNOWN; |
| 31 case base::MemoryState::NORMAL: | 33 case base::MemoryState::NORMAL: |
| 32 return mojom::MemoryState::NORMAL; | 34 return mojom::MemoryState::NORMAL; |
| 33 case base::MemoryState::THROTTLED: | 35 case base::MemoryState::THROTTLED: |
| 34 return mojom::MemoryState::THROTTLED; | 36 return mojom::MemoryState::THROTTLED; |
| 35 case base::MemoryState::SUSPENDED: | 37 case base::MemoryState::SUSPENDED: |
| 36 return mojom::MemoryState::SUSPENDED; | 38 return mojom::MemoryState::SUSPENDED; |
| 37 default: | 39 default: |
| 38 NOTREACHED(); | 40 NOTREACHED(); |
| 39 return mojom::MemoryState::UNKNOWN; | 41 return mojom::MemoryState::UNKNOWN; |
| 40 } | 42 } |
| 41 } | 43 } |
| 42 | 44 |
| 45 const char* MemoryConditionToString(MemoryCondition condition) { | |
| 46 switch (condition) { | |
| 47 case MemoryCondition::NORMAL: | |
| 48 return "normal"; | |
| 49 case MemoryCondition::WARNING: | |
| 50 return "warning"; | |
| 51 case MemoryCondition::CRITICAL: | |
| 52 return "critical"; | |
| 53 } | |
| 54 NOTREACHED(); | |
| 55 return "N/A"; | |
| 56 } | |
| 57 | |
| 43 } // namespace | 58 } // namespace |
| 44 | 59 |
| 45 // The implementation of MemoryCoordinatorHandle. See memory_coordinator.mojom | 60 // The implementation of MemoryCoordinatorHandle. See memory_coordinator.mojom |
| 46 // for the role of this class. | 61 // for the role of this class. |
| 47 class MemoryCoordinatorHandleImpl : public mojom::MemoryCoordinatorHandle { | 62 class MemoryCoordinatorHandleImpl : public mojom::MemoryCoordinatorHandle { |
| 48 public: | 63 public: |
| 49 MemoryCoordinatorHandleImpl(mojom::MemoryCoordinatorHandleRequest request, | 64 MemoryCoordinatorHandleImpl(mojom::MemoryCoordinatorHandleRequest request, |
| 50 MemoryCoordinatorImpl* coordinator, | 65 MemoryCoordinatorImpl* coordinator, |
| 51 int render_process_id); | 66 int render_process_id); |
| 52 ~MemoryCoordinatorHandleImpl() override; | 67 ~MemoryCoordinatorHandleImpl() override; |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 106 return nullptr; | 121 return nullptr; |
| 107 return base::Singleton<MemoryCoordinatorImpl, | 122 return base::Singleton<MemoryCoordinatorImpl, |
| 108 MemoryCoordinatorImplSingletonTraits>::get(); | 123 MemoryCoordinatorImplSingletonTraits>::get(); |
| 109 } | 124 } |
| 110 | 125 |
| 111 MemoryCoordinatorImpl::MemoryCoordinatorImpl( | 126 MemoryCoordinatorImpl::MemoryCoordinatorImpl( |
| 112 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | 127 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| 113 std::unique_ptr<MemoryMonitor> memory_monitor) | 128 std::unique_ptr<MemoryMonitor> memory_monitor) |
| 114 : delegate_(GetContentClient()->browser()->GetMemoryCoordinatorDelegate()), | 129 : delegate_(GetContentClient()->browser()->GetMemoryCoordinatorDelegate()), |
| 115 memory_monitor_(std::move(memory_monitor)), | 130 memory_monitor_(std::move(memory_monitor)), |
| 116 state_updater_(base::MakeUnique<MemoryStateUpdater>(this, task_runner)) { | 131 condition_observer_( |
| 132 base::MakeUnique<MemoryConditionObserver>(this, task_runner)), | |
| 133 minimum_state_transition_period_(base::TimeDelta::FromSeconds( | |
| 134 kDefaultMinimumTransitionPeriodSeconds)) { | |
| 117 DCHECK(memory_monitor_.get()); | 135 DCHECK(memory_monitor_.get()); |
| 118 base::MemoryCoordinatorProxy::SetMemoryCoordinator(this); | 136 base::MemoryCoordinatorProxy::SetMemoryCoordinator(this); |
| 119 } | 137 } |
| 120 | 138 |
| 121 MemoryCoordinatorImpl::~MemoryCoordinatorImpl() {} | 139 MemoryCoordinatorImpl::~MemoryCoordinatorImpl() {} |
| 122 | 140 |
| 123 void MemoryCoordinatorImpl::Start() { | 141 void MemoryCoordinatorImpl::Start() { |
| 124 DCHECK(CalledOnValidThread()); | 142 DCHECK(CalledOnValidThread()); |
| 125 DCHECK(last_state_change_.is_null()); | 143 DCHECK(last_state_change_.is_null()); |
| 126 | 144 |
| 127 notification_registrar_.Add( | 145 notification_registrar_.Add( |
| 128 this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | 146 this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
| 129 NotificationService::AllBrowserContextsAndSources()); | 147 NotificationService::AllBrowserContextsAndSources()); |
| 130 last_state_change_ = base::TimeTicks::Now(); | 148 condition_observer_->ScheduleUpdateCondition(base::TimeDelta()); |
| 131 state_updater_->ScheduleUpdateState(base::TimeDelta()); | |
| 132 } | 149 } |
| 133 | 150 |
| 134 void MemoryCoordinatorImpl::CreateHandle( | 151 void MemoryCoordinatorImpl::CreateHandle( |
| 135 int render_process_id, | 152 int render_process_id, |
| 136 mojom::MemoryCoordinatorHandleRequest request) { | 153 mojom::MemoryCoordinatorHandleRequest request) { |
| 137 std::unique_ptr<MemoryCoordinatorHandleImpl> handle( | 154 std::unique_ptr<MemoryCoordinatorHandleImpl> handle( |
| 138 new MemoryCoordinatorHandleImpl(std::move(request), this, | 155 new MemoryCoordinatorHandleImpl(std::move(request), this, |
| 139 render_process_id)); | 156 render_process_id)); |
| 140 handle->binding().set_connection_error_handler( | 157 handle->binding().set_connection_error_handler( |
| 141 base::Bind(&MemoryCoordinatorImpl::OnConnectionError, | 158 base::Bind(&MemoryCoordinatorImpl::OnConnectionError, |
| 142 base::Unretained(this), render_process_id)); | 159 base::Unretained(this), render_process_id)); |
| 143 CreateChildInfoMapEntry(render_process_id, std::move(handle)); | 160 CreateChildInfoMapEntry(render_process_id, std::move(handle)); |
| 144 } | 161 } |
| 145 | 162 |
| 146 bool MemoryCoordinatorImpl::SetChildMemoryState(int render_process_id, | 163 bool MemoryCoordinatorImpl::SetChildMemoryState(int render_process_id, |
| 147 MemoryState memory_state) { | 164 MemoryState memory_state) { |
| 148 // Can't set an invalid memory state. | 165 // Can't set an invalid memory state. |
| 149 if (memory_state == MemoryState::UNKNOWN) | 166 if (memory_state == MemoryState::UNKNOWN) |
| 150 return false; | 167 return false; |
| 151 | 168 |
| 152 // Can't send a message to a child that doesn't exist. | 169 // Can't send a message to a child that doesn't exist. |
| 153 auto iter = children_.find(render_process_id); | 170 auto iter = children_.find(render_process_id); |
| 154 if (iter == children_.end()) | 171 if (iter == children_.end()) |
| 155 return false; | 172 return false; |
| 156 | 173 |
| 157 // Can't send a message to a child that isn't bound. | 174 // Can't send a message to a child that isn't bound. |
| 158 if (!iter->second.handle->child().is_bound()) | 175 if (!iter->second.handle->child().is_bound()) |
| 159 return false; | 176 return false; |
| 160 | 177 |
| 161 memory_state = OverrideGlobalState(memory_state, iter->second); | 178 memory_state = OverrideState(memory_state, iter->second); |
| 162 | 179 |
| 163 // A nop doesn't need to be sent, but is considered successful. | 180 // A nop doesn't need to be sent, but is considered successful. |
| 164 if (iter->second.memory_state == memory_state) | 181 if (iter->second.memory_state == memory_state) |
| 165 return true; | 182 return true; |
| 166 | 183 |
| 167 // Can't suspend the given renderer. | 184 // Can't suspend the given renderer. |
| 168 if (memory_state == MemoryState::SUSPENDED && | 185 if (memory_state == MemoryState::SUSPENDED && |
| 169 !CanSuspendRenderer(render_process_id)) | 186 !CanSuspendRenderer(render_process_id)) |
| 170 return false; | 187 return false; |
| 171 | 188 |
| 172 // Update the internal state and send the message. | 189 // Update the internal state and send the message. |
| 173 iter->second.memory_state = memory_state; | 190 iter->second.memory_state = memory_state; |
| 174 iter->second.handle->child()->OnStateChange(ToMojomMemoryState(memory_state)); | 191 iter->second.handle->child()->OnStateChange(ToMojomMemoryState(memory_state)); |
| 175 return true; | 192 return true; |
| 176 } | 193 } |
| 177 | 194 |
| 178 base::MemoryState MemoryCoordinatorImpl::GetChildMemoryState( | 195 base::MemoryState MemoryCoordinatorImpl::GetChildMemoryState( |
| 179 int render_process_id) const { | 196 int render_process_id) const { |
| 180 auto iter = children_.find(render_process_id); | 197 auto iter = children_.find(render_process_id); |
| 181 if (iter == children_.end()) | 198 if (iter == children_.end()) |
| 182 return base::MemoryState::UNKNOWN; | 199 return base::MemoryState::UNKNOWN; |
| 183 return iter->second.memory_state; | 200 return iter->second.memory_state; |
| 184 } | 201 } |
| 185 | 202 |
| 186 void MemoryCoordinatorImpl::RecordMemoryPressure( | 203 void MemoryCoordinatorImpl::RecordMemoryPressure( |
| 187 base::MemoryPressureMonitor::MemoryPressureLevel level) { | 204 base::MemoryPressureMonitor::MemoryPressureLevel level) { |
| 188 // TODO(bashi): Record memory pressure level. | 205 // TODO(bashi): Record memory pressure level. |
| 189 } | 206 } |
| 190 | 207 |
| 191 base::MemoryState MemoryCoordinatorImpl::GetGlobalMemoryState() const { | 208 base::MemoryState MemoryCoordinatorImpl::GetBrowserMemoryState() const { |
| 192 return current_state_; | 209 return browser_memory_state_; |
| 193 } | 210 } |
| 194 | 211 |
| 195 base::MemoryState MemoryCoordinatorImpl::GetCurrentMemoryState() const { | 212 base::MemoryState MemoryCoordinatorImpl::GetCurrentMemoryState() const { |
| 196 // SUSPENDED state may not make sense to the browser process. Use THROTTLED | 213 return GetBrowserMemoryState(); |
| 197 // instead when the global state is SUSPENDED. | |
| 198 // TODO(bashi): Maybe worth considering another state for the browser. | |
| 199 return current_state_ == MemoryState::SUSPENDED ? MemoryState::THROTTLED | |
| 200 : current_state_; | |
| 201 } | 214 } |
| 202 | 215 |
| 203 void MemoryCoordinatorImpl::SetCurrentMemoryStateForTesting( | 216 void MemoryCoordinatorImpl::SetCurrentMemoryStateForTesting( |
| 204 base::MemoryState memory_state) { | 217 base::MemoryState memory_state) { |
| 205 // This changes the current state temporariy for testing. The state will be | 218 // Resets |last_state_change_| so that NotifyStateToBrowser() to set |
| 206 // updated 1 minute later. | 219 // memory state forcibly. |
| 207 ForceSetGlobalState(memory_state, base::TimeDelta::FromMinutes(1)); | 220 last_state_change_ = base::TimeTicks(); |
| 221 NotifyStateToBrowser(memory_state); | |
| 208 } | 222 } |
| 209 | 223 |
| 210 void MemoryCoordinatorImpl::ForceSetGlobalState(base::MemoryState new_state, | 224 void MemoryCoordinatorImpl::ForceSetMemoryCondition(MemoryCondition condition, |
| 211 base::TimeDelta duration) { | 225 base::TimeDelta duration) { |
| 212 DCHECK(new_state != MemoryState::UNKNOWN); | 226 UpdateConditionIfNeeded(condition); |
| 213 ChangeStateIfNeeded(current_state_, new_state); | 227 condition_observer_->ScheduleUpdateCondition(duration); |
| 214 state_updater_->ScheduleUpdateState(duration); | |
| 215 } | 228 } |
| 216 | 229 |
| 217 void MemoryCoordinatorImpl::Observe(int type, | 230 void MemoryCoordinatorImpl::Observe(int type, |
| 218 const NotificationSource& source, | 231 const NotificationSource& source, |
| 219 const NotificationDetails& details) { | 232 const NotificationDetails& details) { |
| 220 DCHECK(type == NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED); | 233 DCHECK(type == NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED); |
| 221 RenderWidgetHost* render_widget_host = Source<RenderWidgetHost>(source).ptr(); | 234 RenderWidgetHost* render_widget_host = Source<RenderWidgetHost>(source).ptr(); |
| 222 RenderProcessHost* process = render_widget_host->GetProcess(); | 235 RenderProcessHost* process = render_widget_host->GetProcess(); |
| 223 if (!process) | 236 if (!process) |
| 224 return; | 237 return; |
| 225 auto iter = children().find(process->GetID()); | 238 auto iter = children().find(process->GetID()); |
| 226 if (iter == children().end()) | 239 if (iter == children().end()) |
| 227 return; | 240 return; |
| 241 | |
| 228 iter->second.is_visible = *Details<bool>(details).ptr(); | 242 iter->second.is_visible = *Details<bool>(details).ptr(); |
| 229 auto new_state = GetGlobalMemoryState(); | 243 // The current heuristics for state calculation: |
| 244 // - Foregrounded renderers: THROTTLED when condition is CRITICAL, otherwise | |
| 245 // NORMAL. | |
| 246 // - Backgrounded renderers: THROTTLED when condition is WARNING/CRITICAL, | |
| 247 // otherwise NORMAL. | |
| 248 MemoryState new_state = MemoryState::NORMAL; | |
| 249 MemoryCondition condition = GetMemoryCondition(); | |
| 250 if (condition == MemoryCondition::WARNING) { | |
| 251 new_state = | |
| 252 iter->second.is_visible ? MemoryState::NORMAL : MemoryState::THROTTLED; | |
| 253 } else if (condition == MemoryCondition::CRITICAL) { | |
| 254 new_state = MemoryState::THROTTLED; | |
| 255 } | |
| 230 SetChildMemoryState(iter->first, new_state); | 256 SetChildMemoryState(iter->first, new_state); |
| 231 } | 257 } |
| 232 | 258 |
| 233 base::MemoryState MemoryCoordinatorImpl::GetStateForProcess( | 259 base::MemoryState MemoryCoordinatorImpl::GetStateForProcess( |
| 234 base::ProcessHandle handle) { | 260 base::ProcessHandle handle) { |
| 235 DCHECK(CalledOnValidThread()); | 261 DCHECK(CalledOnValidThread()); |
| 236 if (handle == base::kNullProcessHandle) | 262 if (handle == base::kNullProcessHandle) |
| 237 return MemoryState::UNKNOWN; | 263 return MemoryState::UNKNOWN; |
| 238 if (handle == base::GetCurrentProcessHandle()) | 264 if (handle == base::GetCurrentProcessHandle()) |
| 239 return GetCurrentMemoryState(); | 265 return browser_memory_state_; |
| 240 | 266 |
| 241 for (auto& iter : children()) { | 267 for (auto& iter : children()) { |
| 242 auto* render_process_host = GetRenderProcessHost(iter.first); | 268 auto* render_process_host = GetRenderProcessHost(iter.first); |
| 243 if (render_process_host && render_process_host->GetHandle() == handle) | 269 if (render_process_host && render_process_host->GetHandle() == handle) |
| 244 return iter.second.memory_state; | 270 return iter.second.memory_state; |
| 245 } | 271 } |
| 246 return MemoryState::UNKNOWN; | 272 return MemoryState::UNKNOWN; |
| 247 } | 273 } |
| 248 | 274 |
| 249 bool MemoryCoordinatorImpl::ChangeStateIfNeeded(base::MemoryState prev_state, | 275 void MemoryCoordinatorImpl::UpdateConditionIfNeeded( |
| 250 base::MemoryState next_state) { | 276 MemoryCondition next_condition) { |
| 251 DCHECK(CalledOnValidThread()); | 277 DCHECK(CalledOnValidThread()); |
| 252 if (prev_state == next_state) | 278 if (memory_condition_ == next_condition) |
| 253 return false; | 279 return; |
| 254 | 280 |
| 255 last_state_change_ = base::TimeTicks::Now(); | 281 MemoryCondition prev_condition = memory_condition_; |
| 256 current_state_ = next_state; | 282 memory_condition_ = next_condition; |
| 257 | 283 |
| 258 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("memory-infra"), | 284 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("memory-infra"), |
| 259 "MemoryCoordinatorImpl::ChangeStateIfNeeded", "prev", | 285 "MemoryCoordinatorImpl::UpdateConditionIfNeeded", "prev", |
| 260 MemoryStateToString(prev_state), "next", | 286 MemoryConditionToString(prev_condition), "next", |
| 261 MemoryStateToString(next_state)); | 287 MemoryConditionToString(next_condition)); |
| 262 NotifyStateToClients(); | 288 |
| 263 NotifyStateToChildren(); | 289 // TODO(bashi): Following actions are tentative. We might want to prioritize |
| 264 return true; | 290 // processes and handle them one-by-one. |
| 291 | |
| 292 if (next_condition == MemoryCondition::NORMAL) { | |
| 293 // Set NORMAL state to all clients/processes. | |
| 294 NotifyStateToBrowser(MemoryState::NORMAL); | |
| 295 NotifyStateToChildren(MemoryState::NORMAL); | |
| 296 } else if (next_condition == MemoryCondition::WARNING) { | |
| 297 // Set NORMAL state to foreground proceses and clients in the browser | |
| 298 // process. Set THROTTLED state to background processes. | |
| 299 NotifyStateToBrowser(MemoryState::NORMAL); | |
| 300 for (auto& iter : children()) { | |
| 301 auto state = | |
| 302 iter.second.is_visible ? MemoryState::NORMAL : MemoryState::THROTTLED; | |
| 303 SetChildMemoryState(iter.first, state); | |
| 304 } | |
| 305 // Idea: Purge memory from background processes. | |
| 306 } else if (next_condition == MemoryCondition::CRITICAL) { | |
| 307 // Set THROTTLED state to all clients/processes. | |
| 308 NotifyStateToBrowser(MemoryState::THROTTLED); | |
| 309 NotifyStateToChildren(MemoryState::THROTTLED); | |
| 310 // Idea: Start discarding tabs. | |
| 311 } | |
| 265 } | 312 } |
| 266 | 313 |
| 267 void MemoryCoordinatorImpl::DiscardTab() { | 314 void MemoryCoordinatorImpl::DiscardTab() { |
| 268 if (delegate_) | 315 if (delegate_) |
| 269 delegate_->DiscardTab(); | 316 delegate_->DiscardTab(); |
| 270 } | 317 } |
| 271 | 318 |
| 272 RenderProcessHost* MemoryCoordinatorImpl::GetRenderProcessHost( | 319 RenderProcessHost* MemoryCoordinatorImpl::GetRenderProcessHost( |
| 273 int render_process_id) { | 320 int render_process_id) { |
| 274 return RenderProcessHost::FromID(render_process_id); | 321 return RenderProcessHost::FromID(render_process_id); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 301 return false; | 348 return false; |
| 302 if (render_process_host->GetWorkerRefCount() > 0) | 349 if (render_process_host->GetWorkerRefCount() > 0) |
| 303 return false; | 350 return false; |
| 304 // Assumes that we can't suspend renderers if there is no delegate. | 351 // Assumes that we can't suspend renderers if there is no delegate. |
| 305 if (!delegate_) | 352 if (!delegate_) |
| 306 return false; | 353 return false; |
| 307 return delegate_->CanSuspendBackgroundedRenderer(render_process_id); | 354 return delegate_->CanSuspendBackgroundedRenderer(render_process_id); |
| 308 } | 355 } |
| 309 | 356 |
| 310 void MemoryCoordinatorImpl::OnChildAdded(int render_process_id) { | 357 void MemoryCoordinatorImpl::OnChildAdded(int render_process_id) { |
| 311 // Populate the global state as an initial state of a newly created process. | 358 // Populate an initial state of a newly created process, assuming it's |
| 312 auto new_state = GetGlobalMemoryState(); | 359 // foregrounded. |
|
haraken
2017/03/03 02:08:00
You don't need to fix this in this CL but this ass
bashi
2017/03/03 02:41:32
Agreed. Added TODO.
| |
| 360 auto new_state = GetMemoryCondition() == MemoryCondition::CRITICAL | |
| 361 ? MemoryState::THROTTLED | |
| 362 : MemoryState::NORMAL; | |
| 313 SetChildMemoryState(render_process_id, new_state); | 363 SetChildMemoryState(render_process_id, new_state); |
| 314 } | 364 } |
| 315 | 365 |
| 316 base::MemoryState MemoryCoordinatorImpl::OverrideGlobalState( | 366 base::MemoryState MemoryCoordinatorImpl::OverrideState(MemoryState memory_state, |
| 317 MemoryState memory_state, | 367 const ChildInfo& child) { |
| 318 const ChildInfo& child) { | |
| 319 // We don't suspend foreground renderers. Throttle them instead. | 368 // We don't suspend foreground renderers. Throttle them instead. |
| 320 if (child.is_visible && memory_state == MemoryState::SUSPENDED) | 369 if (child.is_visible && memory_state == MemoryState::SUSPENDED) |
| 321 return MemoryState::THROTTLED; | 370 return MemoryState::THROTTLED; |
| 322 #if defined(OS_ANDROID) | 371 #if defined(OS_ANDROID) |
| 323 // On Android, we throttle background renderers immediately. | 372 // On Android, we throttle background renderers immediately. |
| 324 // TODO(bashi): Create a specialized class of MemoryCoordinator for Android | 373 // TODO(bashi): Create a specialized class of MemoryCoordinator for Android |
| 325 // and move this ifdef to the class. | 374 // and move this ifdef to the class. |
| 326 if (!child.is_visible && memory_state == MemoryState::NORMAL) | 375 if (!child.is_visible && memory_state == MemoryState::NORMAL) |
| 327 return MemoryState::THROTTLED; | 376 return MemoryState::THROTTLED; |
| 328 // TODO(bashi): Suspend background renderers after a certain period of time. | 377 // TODO(bashi): Suspend background renderers after a certain period of time. |
| 329 #endif // defined(OS_ANDROID) | 378 #endif // defined(OS_ANDROID) |
| 330 return memory_state; | 379 return memory_state; |
| 331 } | 380 } |
| 332 | 381 |
| 333 void MemoryCoordinatorImpl::CreateChildInfoMapEntry( | 382 void MemoryCoordinatorImpl::CreateChildInfoMapEntry( |
| 334 int render_process_id, | 383 int render_process_id, |
| 335 std::unique_ptr<MemoryCoordinatorHandleImpl> handle) { | 384 std::unique_ptr<MemoryCoordinatorHandleImpl> handle) { |
| 336 auto& child_info = children_[render_process_id]; | 385 auto& child_info = children_[render_process_id]; |
| 337 // Process always start with normal memory state. | 386 // Process always start with normal memory state. |
| 338 // We'll set renderer's memory state to the current global state when the | 387 // We'll set renderer's memory state to the current global state when the |
| 339 // corresponding renderer process is ready to communicate. Renderer processes | 388 // corresponding renderer process is ready to communicate. Renderer processes |
| 340 // call AddChild() when they are ready. | 389 // call AddChild() when they are ready. |
| 341 child_info.memory_state = MemoryState::NORMAL; | 390 child_info.memory_state = MemoryState::NORMAL; |
| 342 child_info.is_visible = true; | 391 child_info.is_visible = true; |
| 343 child_info.handle = std::move(handle); | 392 child_info.handle = std::move(handle); |
| 344 } | 393 } |
| 345 | 394 |
| 346 void MemoryCoordinatorImpl::NotifyStateToClients() { | 395 void MemoryCoordinatorImpl::NotifyStateToBrowser( |
| 347 auto state = GetCurrentMemoryState(); | 396 base::MemoryState memory_state) { |
| 397 if (memory_state == browser_memory_state_) | |
|
bashi
2017/03/02 08:59:31
unittests catched the bug. I should have added thi
| |
| 398 return; | |
| 399 | |
| 400 base::TimeTicks now = base::TimeTicks::Now(); | |
| 401 if (!last_state_change_.is_null() && | |
| 402 (now - last_state_change_ < minimum_state_transition_period_)) | |
| 403 return; | |
| 404 | |
| 405 last_state_change_ = now; | |
| 406 browser_memory_state_ = memory_state; | |
| 407 NotifyStateToClients(memory_state); | |
| 408 } | |
| 409 | |
| 410 void MemoryCoordinatorImpl::NotifyStateToClients(MemoryState state) { | |
| 348 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); | 411 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); |
| 349 } | 412 } |
| 350 | 413 |
| 351 void MemoryCoordinatorImpl::NotifyStateToChildren() { | 414 void MemoryCoordinatorImpl::NotifyStateToChildren(MemoryState state) { |
| 352 // It's OK to call SetChildMemoryState() unconditionally because it checks | 415 // It's OK to call SetChildMemoryState() unconditionally because it checks |
| 353 // whether this state transition is valid. | 416 // whether this state transition is valid. |
| 354 for (auto& iter : children()) | 417 for (auto& iter : children()) |
| 355 SetChildMemoryState(iter.first, current_state_); | 418 SetChildMemoryState(iter.first, state); |
| 356 } | 419 } |
| 357 | 420 |
| 358 MemoryCoordinatorImpl::ChildInfo::ChildInfo() {} | 421 MemoryCoordinatorImpl::ChildInfo::ChildInfo() {} |
| 359 | 422 |
| 360 MemoryCoordinatorImpl::ChildInfo::ChildInfo(const ChildInfo& rhs) { | 423 MemoryCoordinatorImpl::ChildInfo::ChildInfo(const ChildInfo& rhs) { |
| 361 // This is a nop, but exists for compatibility with STL containers. | 424 // This is a nop, but exists for compatibility with STL containers. |
| 362 } | 425 } |
| 363 | 426 |
| 364 MemoryCoordinatorImpl::ChildInfo::~ChildInfo() {} | 427 MemoryCoordinatorImpl::ChildInfo::~ChildInfo() {} |
| 365 | 428 |
| 366 } // namespace content | 429 } // namespace content |
| OLD | NEW |