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/threading/thread_task_runner_handle.h" | 7 #include "base/threading/thread_task_runner_handle.h" |
| 8 #include "base/trace_event/trace_event.h" | 8 #include "base/trace_event/trace_event.h" |
| 9 #include "content/browser/memory/memory_monitor.h" | 9 #include "content/browser/memory/memory_monitor.h" |
| 10 #include "content/public/browser/notification_service.h" | 10 #include "content/public/browser/notification_service.h" |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 69 return nullptr; | 69 return nullptr; |
| 70 return base::Singleton<MemoryCoordinator, | 70 return base::Singleton<MemoryCoordinator, |
| 71 MemoryCoordinatorSingletonTraits>::get(); | 71 MemoryCoordinatorSingletonTraits>::get(); |
| 72 } | 72 } |
| 73 | 73 |
| 74 MemoryCoordinatorImpl::MemoryCoordinatorImpl( | 74 MemoryCoordinatorImpl::MemoryCoordinatorImpl( |
| 75 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | 75 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| 76 std::unique_ptr<MemoryMonitor> memory_monitor) | 76 std::unique_ptr<MemoryMonitor> memory_monitor) |
| 77 : task_runner_(task_runner), | 77 : task_runner_(task_runner), |
| 78 memory_monitor_(std::move(memory_monitor)), | 78 memory_monitor_(std::move(memory_monitor)), |
| 79 remaining_global_budget_mb_(0), | |
| 79 weak_ptr_factory_(this) { | 80 weak_ptr_factory_(this) { |
| 80 DCHECK(memory_monitor_.get()); | 81 DCHECK(memory_monitor_.get()); |
| 81 update_state_callback_ = base::Bind(&MemoryCoordinatorImpl::UpdateState, | 82 update_state_callback_ = base::Bind(&MemoryCoordinatorImpl::UpdateState, |
| 82 weak_ptr_factory_.GetWeakPtr()); | 83 weak_ptr_factory_.GetWeakPtr()); |
| 83 | 84 |
| 84 // Set initial parameters for calculating the global state. | 85 // Set initial parameters for calculating the global state. |
| 85 expected_renderer_size_ = kDefaultExpectedRendererSizeMB; | 86 expected_renderer_size_ = kDefaultExpectedRendererSizeMB; |
|
Hannes Payer (out of office)
2016/11/11 12:55:19
Unrelated to this CL: How did you come up with kDe
bashi
2016/11/12 01:30:53
From UMA (median of average). This is just an esti
| |
| 86 new_renderers_until_throttled_ = kDefaultNewRenderersUntilThrottled; | 87 new_renderers_until_throttled_ = kDefaultNewRenderersUntilThrottled; |
| 87 new_renderers_until_suspended_ = kDefaultNewRenderersUntilSuspended; | 88 new_renderers_until_suspended_ = kDefaultNewRenderersUntilSuspended; |
| 88 new_renderers_back_to_normal_ = kDefaultNewRenderersBackToNormal; | 89 new_renderers_back_to_normal_ = kDefaultNewRenderersBackToNormal; |
| 89 new_renderers_back_to_throttled_ = kDefaultNewRenderersBackToThrottled; | 90 new_renderers_back_to_throttled_ = kDefaultNewRenderersBackToThrottled; |
| 90 minimum_transition_period_ = | 91 minimum_transition_period_ = |
| 91 base::TimeDelta::FromSeconds(kDefaultMinimumTransitionPeriodSeconds); | 92 base::TimeDelta::FromSeconds(kDefaultMinimumTransitionPeriodSeconds); |
| 92 monitoring_interval_ = | 93 monitoring_interval_ = |
| 93 base::TimeDelta::FromSeconds(kDefaultMonitoringIntervalSeconds); | 94 base::TimeDelta::FromSeconds(kDefaultMonitoringIntervalSeconds); |
| 94 } | 95 } |
| 95 | 96 |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 125 // updated later by the task posted at ScheduleUpdateState. | 126 // updated later by the task posted at ScheduleUpdateState. |
| 126 DCHECK(memory_state != MemoryState::UNKNOWN); | 127 DCHECK(memory_state != MemoryState::UNKNOWN); |
| 127 base::MemoryState prev_state = current_state_; | 128 base::MemoryState prev_state = current_state_; |
| 128 current_state_ = memory_state; | 129 current_state_ = memory_state; |
| 129 if (prev_state != current_state_) { | 130 if (prev_state != current_state_) { |
| 130 NotifyStateToClients(); | 131 NotifyStateToClients(); |
| 131 NotifyStateToChildren(); | 132 NotifyStateToChildren(); |
| 132 } | 133 } |
| 133 } | 134 } |
| 134 | 135 |
| 136 int64_t MemoryCoordinatorImpl::GetRemainingGlobalBudget() const { | |
| 137 return remaining_global_budget_mb_; | |
| 138 } | |
| 139 | |
| 135 void MemoryCoordinatorImpl::Observe(int type, | 140 void MemoryCoordinatorImpl::Observe(int type, |
| 136 const NotificationSource& source, | 141 const NotificationSource& source, |
| 137 const NotificationDetails& details) { | 142 const NotificationDetails& details) { |
| 138 DCHECK(type == NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED); | 143 DCHECK(type == NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED); |
| 139 RenderWidgetHost* render_widget_host = Source<RenderWidgetHost>(source).ptr(); | 144 RenderWidgetHost* render_widget_host = Source<RenderWidgetHost>(source).ptr(); |
| 140 RenderProcessHost* process = render_widget_host->GetProcess(); | 145 RenderProcessHost* process = render_widget_host->GetProcess(); |
| 141 if (!process) | 146 if (!process) |
| 142 return; | 147 return; |
| 143 auto iter = children().find(process->GetID()); | 148 auto iter = children().find(process->GetID()); |
| 144 if (iter == children().end()) | 149 if (iter == children().end()) |
| 145 return; | 150 return; |
| 146 bool is_visible = *Details<bool>(details).ptr(); | 151 bool is_visible = *Details<bool>(details).ptr(); |
| 147 // We don't throttle/suspend a visible renderer for now. | 152 // We don't throttle/suspend a visible renderer for now. |
| 148 auto new_state = is_visible ? mojom::MemoryState::NORMAL | 153 auto new_state = is_visible ? mojom::MemoryState::NORMAL |
| 149 : ToMojomMemoryState(current_state_); | 154 : ToMojomMemoryState(current_state_); |
| 150 SetMemoryState(iter->first, new_state); | 155 SetMemoryState(iter->first, new_state); |
| 151 } | 156 } |
| 152 | 157 |
| 153 base::MemoryState MemoryCoordinatorImpl::CalculateNextState() { | 158 base::MemoryState MemoryCoordinatorImpl::CalculateNextState( |
| 159 int free_memory_until_critical_mb) { | |
| 154 using MemoryState = base::MemoryState; | 160 using MemoryState = base::MemoryState; |
| 155 | 161 |
| 156 int available = memory_monitor_->GetFreeMemoryUntilCriticalMB(); | 162 // If memory is already critical then immediately go to the most aggressive |
| 157 if (available <= 0) | 163 // state. |
| 164 if (free_memory_until_critical_mb <= 0) | |
| 158 return MemoryState::SUSPENDED; | 165 return MemoryState::SUSPENDED; |
| 159 | 166 |
| 160 int expected_renderer_count = available / expected_renderer_size_; | 167 // Determine the next state. |
| 161 | 168 int expected_renderer_count = |
| 169 free_memory_until_critical_mb / expected_renderer_size_; | |
| 162 switch (current_state_) { | 170 switch (current_state_) { |
| 163 case MemoryState::NORMAL: | 171 case MemoryState::NORMAL: |
| 164 if (expected_renderer_count <= new_renderers_until_suspended_) | 172 if (expected_renderer_count <= new_renderers_until_suspended_) |
| 165 return MemoryState::SUSPENDED; | 173 return MemoryState::SUSPENDED; |
| 166 if (expected_renderer_count <= new_renderers_until_throttled_) | 174 if (expected_renderer_count <= new_renderers_until_throttled_) |
| 167 return MemoryState::THROTTLED; | 175 return MemoryState::THROTTLED; |
| 168 return MemoryState::NORMAL; | 176 return MemoryState::NORMAL; |
| 169 case MemoryState::THROTTLED: | 177 case MemoryState::THROTTLED: |
| 170 if (expected_renderer_count <= new_renderers_until_suspended_) | 178 if (expected_renderer_count <= new_renderers_until_suspended_) |
| 171 return MemoryState::SUSPENDED; | 179 return MemoryState::SUSPENDED; |
| 172 if (expected_renderer_count >= new_renderers_back_to_normal_) | 180 if (expected_renderer_count >= new_renderers_back_to_normal_) |
| 173 return MemoryState::NORMAL; | 181 return MemoryState::NORMAL; |
| 174 return MemoryState::THROTTLED; | 182 return MemoryState::THROTTLED; |
| 175 case MemoryState::SUSPENDED: | 183 case MemoryState::SUSPENDED: |
| 176 if (expected_renderer_count >= new_renderers_back_to_normal_) | 184 if (expected_renderer_count >= new_renderers_back_to_normal_) |
| 177 return MemoryState::NORMAL; | 185 return MemoryState::NORMAL; |
| 178 if (expected_renderer_count >= new_renderers_back_to_throttled_) | 186 if (expected_renderer_count >= new_renderers_back_to_throttled_) |
| 179 return MemoryState::THROTTLED; | 187 return MemoryState::THROTTLED; |
| 180 return MemoryState::SUSPENDED; | 188 return MemoryState::SUSPENDED; |
| 181 case MemoryState::UNKNOWN: | 189 case MemoryState::UNKNOWN: |
| 182 // Fall through | 190 // Fall through. |
| 183 default: | 191 default: |
| 184 NOTREACHED(); | 192 NOTREACHED(); |
| 185 return MemoryState::UNKNOWN; | 193 return MemoryState::UNKNOWN; |
| 186 } | 194 } |
| 187 } | 195 } |
| 188 | 196 |
| 197 int64_t MemoryCoordinatorImpl::CalculateRemainingGlobalBudget( | |
| 198 int free_memory_until_critical_mb) { | |
| 199 int renderer_count = new_renderers_until_throttled_; | |
|
Hannes Payer (out of office)
2016/11/11 12:55:19
I do not understand why you use kDefaultNewRendere
bashi
2016/11/12 01:30:53
Because we don't know what an appropriate value fo
| |
| 200 if (current_state_ != MemoryState::NORMAL) | |
| 201 renderer_count = new_renderers_back_to_normal_; | |
|
haraken
2016/11/05 12:52:21
Shouldn't new_renderers_back_to_normal_ be always
bashi
2016/11/07 00:16:37
We have different parameters for # of renderers to
chrisha
2016/11/07 19:33:41
Yeah, this enables a "dead band" to prevent thrash
| |
| 202 | |
| 203 return free_memory_until_critical_mb - | |
| 204 renderer_count * expected_renderer_size_; | |
| 205 } | |
| 206 | |
| 189 void MemoryCoordinatorImpl::UpdateState() { | 207 void MemoryCoordinatorImpl::UpdateState() { |
| 190 base::TimeTicks now = base::TimeTicks::Now(); | 208 base::TimeTicks now = base::TimeTicks::Now(); |
| 209 | |
| 210 int available = memory_monitor_->GetFreeMemoryUntilCriticalMB(); | |
| 211 | |
| 191 MemoryState prev_state = current_state_; | 212 MemoryState prev_state = current_state_; |
| 192 MemoryState next_state = CalculateNextState(); | 213 MemoryState next_state = CalculateNextState(available); |
| 193 | 214 |
| 194 if (last_state_change_.is_null() || current_state_ != next_state) { | 215 if (last_state_change_.is_null() || current_state_ != next_state) { |
| 195 current_state_ = next_state; | 216 current_state_ = next_state; |
| 196 last_state_change_ = now; | 217 last_state_change_ = now; |
| 197 } | 218 } |
| 198 | 219 |
| 199 if (next_state != prev_state) { | 220 if (next_state != prev_state) { |
| 200 TRACE_EVENT2("memory-infra", "MemoryCoordinatorImpl::UpdateState", | 221 TRACE_EVENT2("memory-infra", "MemoryCoordinatorImpl::UpdateState", |
| 201 "prev", MemoryStateToString(prev_state), | 222 "prev", MemoryStateToString(prev_state), |
| 202 "next", MemoryStateToString(next_state)); | 223 "next", MemoryStateToString(next_state)); |
| 203 | 224 |
| 204 NotifyStateToClients(); | 225 NotifyStateToClients(); |
| 205 NotifyStateToChildren(); | 226 NotifyStateToChildren(); |
| 206 ScheduleUpdateState(minimum_transition_period_); | 227 ScheduleUpdateState(minimum_transition_period_); |
| 207 } else { | 228 } else { |
| 208 ScheduleUpdateState(monitoring_interval_); | 229 ScheduleUpdateState(monitoring_interval_); |
| 209 } | 230 } |
| 231 | |
| 232 // Update the global budget. This is used by some passive clients of the | |
| 233 // memory coordinator to smoothly scale their own resource consumption in | |
| 234 // sync with the memory coordinator. | |
| 235 int64_t prev_remaining = remaining_global_budget_mb_; | |
| 236 int64_t next_remaining = CalculateRemainingGlobalBudget(available); | |
| 237 if (prev_remaining != next_remaining) { | |
|
bashi
2016/11/07 00:16:37
How likely does this condition become true? |free_
chrisha
2016/11/07 19:33:41
Very unlikely to remain true, but this is a cheap
bashi
2016/11/07 23:17:08
Acknowledged.
chrisha
2016/12/15 21:10:24
(This is a moot point with a "pull" model.)
| |
| 238 remaining_global_budget_mb_ = next_remaining; | |
| 239 NotifyRemainingGlobalBudgetToChildren(); | |
|
haraken
2016/11/05 12:52:21
How frequently will this IPC be called?
bashi
2016/11/07 00:16:37
My guess is that it's almost the same as how frequ
chrisha
2016/11/07 19:33:41
Yup. Although we should likely still continue to c
bashi
2016/11/07 23:17:08
Yeah, as haraken@ said, async API may not be usefu
Hannes Payer (out of office)
2016/11/11 12:55:19
Currently, I am only planning to take advantage of
chrisha
2016/12/15 21:10:24
Moved to a "pull" model.
| |
| 240 } | |
| 210 } | 241 } |
| 211 | 242 |
| 212 void MemoryCoordinatorImpl::NotifyStateToClients() { | 243 void MemoryCoordinatorImpl::NotifyStateToClients() { |
| 213 auto state = GetCurrentMemoryState(); | 244 auto state = GetCurrentMemoryState(); |
| 214 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); | 245 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); |
| 215 } | 246 } |
| 216 | 247 |
| 217 void MemoryCoordinatorImpl::NotifyStateToChildren() { | 248 void MemoryCoordinatorImpl::NotifyStateToChildren() { |
| 218 auto mojo_state = ToMojomMemoryState(current_state_); | 249 auto mojo_state = ToMojomMemoryState(current_state_); |
| 219 // It's OK to call SetMemoryState() unconditionally because it checks whether | 250 // It's OK to call SetMemoryState() unconditionally because it checks whether |
| 220 // this state transition is valid. | 251 // this state transition is valid. |
| 221 for (auto& iter : children()) | 252 for (auto& iter : children()) |
| 222 SetMemoryState(iter.first, mojo_state); | 253 SetMemoryState(iter.first, mojo_state); |
| 223 } | 254 } |
| 224 | 255 |
| 256 void MemoryCoordinatorImpl::NotifyRemainingGlobalBudgetToChildren() { | |
| 257 for (auto& iter : children()) | |
| 258 SetRemainingGlobalBudget(iter.first, remaining_global_budget_mb_); | |
| 259 } | |
| 260 | |
| 225 void MemoryCoordinatorImpl::ScheduleUpdateState(base::TimeDelta delta) { | 261 void MemoryCoordinatorImpl::ScheduleUpdateState(base::TimeDelta delta) { |
| 226 task_runner_->PostDelayedTask(FROM_HERE, update_state_callback_, delta); | 262 task_runner_->PostDelayedTask(FROM_HERE, update_state_callback_, delta); |
| 227 } | 263 } |
| 228 | 264 |
| 229 bool MemoryCoordinatorImpl::ValidateParameters() { | 265 bool MemoryCoordinatorImpl::ValidateParameters() { |
| 230 return (new_renderers_until_throttled_ > new_renderers_until_suspended_) && | 266 return (new_renderers_until_throttled_ > new_renderers_until_suspended_) && |
| 231 (new_renderers_back_to_normal_ > new_renderers_back_to_throttled_) && | 267 (new_renderers_back_to_normal_ > new_renderers_back_to_throttled_) && |
| 232 (new_renderers_back_to_normal_ > new_renderers_until_throttled_) && | 268 (new_renderers_back_to_normal_ > new_renderers_until_throttled_) && |
| 233 (new_renderers_back_to_throttled_ > new_renderers_until_suspended_); | 269 (new_renderers_back_to_throttled_ > new_renderers_until_suspended_); |
| 234 } | 270 } |
| 235 | 271 |
| 236 } // namespace content | 272 } // namespace content |
| OLD | NEW |