| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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 "platform/scheduler/renderer/budget_pool.h" | 5 #include "platform/scheduler/renderer/budget_pool.h" |
| 6 | 6 |
| 7 #include <cstdint> | 7 #include <cstdint> |
| 8 | 8 |
| 9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 namespace { | 25 namespace { |
| 26 | 26 |
| 27 std::string PointerToId(void* pointer) { | 27 std::string PointerToId(void* pointer) { |
| 28 return base::StringPrintf( | 28 return base::StringPrintf( |
| 29 "0x%" PRIx64, | 29 "0x%" PRIx64, |
| 30 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(pointer))); | 30 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(pointer))); |
| 31 } | 31 } |
| 32 | 32 |
| 33 } // namespace | 33 } // namespace |
| 34 | 34 |
| 35 BudgetPool::BudgetPool(const char* name, |
| 36 BudgetPoolController* budget_pool_controller) |
| 37 : name_(name), |
| 38 budget_pool_controller_(budget_pool_controller), |
| 39 is_enabled_(true) {} |
| 40 |
| 35 BudgetPool::~BudgetPool() {} | 41 BudgetPool::~BudgetPool() {} |
| 36 | 42 |
| 37 CPUTimeBudgetPool::CPUTimeBudgetPool( | 43 const char* BudgetPool::Name() const { |
| 38 const char* name, | 44 return name_; |
| 39 BudgetPoolController* budget_pool_controller, | |
| 40 base::TimeTicks now) | |
| 41 : name_(name), | |
| 42 budget_pool_controller_(budget_pool_controller), | |
| 43 last_checkpoint_(now), | |
| 44 cpu_percentage_(1), | |
| 45 is_enabled_(true) {} | |
| 46 | |
| 47 CPUTimeBudgetPool::~CPUTimeBudgetPool() {} | |
| 48 | |
| 49 void CPUTimeBudgetPool::SetMaxBudgetLevel( | |
| 50 base::TimeTicks now, | |
| 51 base::Optional<base::TimeDelta> max_budget_level) { | |
| 52 Advance(now); | |
| 53 max_budget_level_ = max_budget_level; | |
| 54 EnforceBudgetLevelRestrictions(); | |
| 55 } | 45 } |
| 56 | 46 |
| 57 void CPUTimeBudgetPool::SetMaxThrottlingDelay( | 47 void BudgetPool::AddQueue(base::TimeTicks now, TaskQueue* queue) { |
| 58 base::TimeTicks now, | |
| 59 base::Optional<base::TimeDelta> max_throttling_delay) { | |
| 60 Advance(now); | |
| 61 max_throttling_delay_ = max_throttling_delay; | |
| 62 EnforceBudgetLevelRestrictions(); | |
| 63 } | |
| 64 | |
| 65 void CPUTimeBudgetPool::SetMinBudgetLevelToRun( | |
| 66 base::TimeTicks now, | |
| 67 base::TimeDelta min_budget_level_to_run) { | |
| 68 Advance(now); | |
| 69 min_budget_level_to_run_ = min_budget_level_to_run; | |
| 70 } | |
| 71 | |
| 72 void CPUTimeBudgetPool::SetTimeBudgetRecoveryRate(base::TimeTicks now, | |
| 73 double cpu_percentage) { | |
| 74 Advance(now); | |
| 75 cpu_percentage_ = cpu_percentage; | |
| 76 EnforceBudgetLevelRestrictions(); | |
| 77 } | |
| 78 | |
| 79 void CPUTimeBudgetPool::AddQueue(base::TimeTicks now, TaskQueue* queue) { | |
| 80 budget_pool_controller_->AddQueueToBudgetPool(queue, this); | 48 budget_pool_controller_->AddQueueToBudgetPool(queue, this); |
| 81 associated_task_queues_.insert(queue); | 49 associated_task_queues_.insert(queue); |
| 82 | 50 |
| 83 if (!is_enabled_ || !budget_pool_controller_->IsThrottled(queue)) | 51 if (!is_enabled_ || !budget_pool_controller_->IsThrottled(queue)) |
| 84 return; | 52 return; |
| 85 | 53 |
| 86 budget_pool_controller_->BlockQueue(now, queue); | 54 budget_pool_controller_->BlockQueue(now, queue); |
| 87 } | 55 } |
| 88 | 56 |
| 89 void CPUTimeBudgetPool::RemoveQueue(base::TimeTicks now, TaskQueue* queue) { | 57 void BudgetPool::RemoveQueue(base::TimeTicks now, TaskQueue* queue) { |
| 90 budget_pool_controller_->RemoveQueueFromBudgetPool(queue, this); | 58 budget_pool_controller_->RemoveQueueFromBudgetPool(queue, this); |
| 91 associated_task_queues_.erase(queue); | 59 associated_task_queues_.erase(queue); |
| 92 | 60 |
| 93 if (!is_enabled_ || !budget_pool_controller_->IsThrottled(queue)) | 61 if (!is_enabled_ || !budget_pool_controller_->IsThrottled(queue)) |
| 94 return; | 62 return; |
| 95 | 63 |
| 96 budget_pool_controller_->UnblockQueue(now, queue); | 64 budget_pool_controller_->UnblockQueue(now, queue); |
| 97 } | 65 } |
| 98 | 66 |
| 99 void CPUTimeBudgetPool::EnableThrottling(LazyNow* lazy_now) { | 67 void BudgetPool::EnableThrottling(LazyNow* lazy_now) { |
| 100 if (is_enabled_) | 68 if (is_enabled_) |
| 101 return; | 69 return; |
| 102 is_enabled_ = true; | 70 is_enabled_ = true; |
| 103 | 71 |
| 104 TRACE_EVENT0("renderer.scheduler", "CPUTimeBudgetPool_EnableThrottling"); | 72 TRACE_EVENT0("renderer.scheduler", "BudgetPool_EnableThrottling"); |
| 105 | 73 |
| 106 BlockThrottledQueues(lazy_now->Now()); | 74 BlockThrottledQueues(lazy_now->Now()); |
| 107 } | 75 } |
| 108 | 76 |
| 109 void CPUTimeBudgetPool::DisableThrottling(LazyNow* lazy_now) { | 77 void BudgetPool::DisableThrottling(LazyNow* lazy_now) { |
| 110 if (!is_enabled_) | 78 if (!is_enabled_) |
| 111 return; | 79 return; |
| 112 is_enabled_ = false; | 80 is_enabled_ = false; |
| 113 | 81 |
| 114 TRACE_EVENT0("renderer.scheduler", "CPUTimeBudgetPool_DisableThrottling"); | 82 TRACE_EVENT0("renderer.scheduler", "BudgetPool_DisableThrottling"); |
| 115 | 83 |
| 116 for (TaskQueue* queue : associated_task_queues_) { | 84 for (TaskQueue* queue : associated_task_queues_) { |
| 117 if (!budget_pool_controller_->IsThrottled(queue)) | 85 if (!budget_pool_controller_->IsThrottled(queue)) |
| 118 continue; | 86 continue; |
| 119 | 87 |
| 120 budget_pool_controller_->UnblockQueue(lazy_now->Now(), queue); | 88 budget_pool_controller_->UnblockQueue(lazy_now->Now(), queue); |
| 121 } | 89 } |
| 122 | 90 |
| 123 // TODO(altimin): We need to disable TimeBudgetQueues here or they will | 91 // TODO(altimin): We need to disable TimeBudgetQueues here or they will |
| 124 // regenerate extra time budget when they are disabled. | 92 // regenerate extra time budget when they are disabled. |
| 125 } | 93 } |
| 126 | 94 |
| 127 bool CPUTimeBudgetPool::IsThrottlingEnabled() const { | 95 bool BudgetPool::IsThrottlingEnabled() const { |
| 128 return is_enabled_; | 96 return is_enabled_; |
| 129 } | 97 } |
| 130 | 98 |
| 99 void BudgetPool::Close() { |
| 100 DCHECK_EQ(0u, associated_task_queues_.size()); |
| 101 |
| 102 budget_pool_controller_->UnregisterBudgetPool(this); |
| 103 } |
| 104 |
| 105 void BudgetPool::BlockThrottledQueues(base::TimeTicks now) { |
| 106 for (TaskQueue* queue : associated_task_queues_) |
| 107 budget_pool_controller_->BlockQueue(now, queue); |
| 108 } |
| 109 |
| 110 CPUTimeBudgetPool::CPUTimeBudgetPool( |
| 111 const char* name, |
| 112 BudgetPoolController* budget_pool_controller, |
| 113 base::TimeTicks now) |
| 114 : BudgetPool(name, budget_pool_controller), |
| 115 last_checkpoint_(now), |
| 116 cpu_percentage_(1) {} |
| 117 |
| 118 CPUTimeBudgetPool::~CPUTimeBudgetPool() {} |
| 119 |
| 120 void CPUTimeBudgetPool::SetMaxBudgetLevel( |
| 121 base::TimeTicks now, |
| 122 base::Optional<base::TimeDelta> max_budget_level) { |
| 123 Advance(now); |
| 124 max_budget_level_ = max_budget_level; |
| 125 EnforceBudgetLevelRestrictions(); |
| 126 } |
| 127 |
| 128 void CPUTimeBudgetPool::SetMaxThrottlingDelay( |
| 129 base::TimeTicks now, |
| 130 base::Optional<base::TimeDelta> max_throttling_delay) { |
| 131 Advance(now); |
| 132 max_throttling_delay_ = max_throttling_delay; |
| 133 EnforceBudgetLevelRestrictions(); |
| 134 } |
| 135 |
| 136 void CPUTimeBudgetPool::SetMinBudgetLevelToRun( |
| 137 base::TimeTicks now, |
| 138 base::TimeDelta min_budget_level_to_run) { |
| 139 Advance(now); |
| 140 min_budget_level_to_run_ = min_budget_level_to_run; |
| 141 } |
| 142 |
| 143 void CPUTimeBudgetPool::SetTimeBudgetRecoveryRate(base::TimeTicks now, |
| 144 double cpu_percentage) { |
| 145 Advance(now); |
| 146 cpu_percentage_ = cpu_percentage; |
| 147 EnforceBudgetLevelRestrictions(); |
| 148 } |
| 149 |
| 131 void CPUTimeBudgetPool::GrantAdditionalBudget(base::TimeTicks now, | 150 void CPUTimeBudgetPool::GrantAdditionalBudget(base::TimeTicks now, |
| 132 base::TimeDelta budget_level) { | 151 base::TimeDelta budget_level) { |
| 133 Advance(now); | 152 Advance(now); |
| 134 current_budget_level_ += budget_level; | 153 current_budget_level_ += budget_level; |
| 135 EnforceBudgetLevelRestrictions(); | 154 EnforceBudgetLevelRestrictions(); |
| 136 } | 155 } |
| 137 | 156 |
| 138 void CPUTimeBudgetPool::SetReportingCallback( | 157 void CPUTimeBudgetPool::SetReportingCallback( |
| 139 base::Callback<void(base::TimeDelta)> reporting_callback) { | 158 base::Callback<void(base::TimeDelta)> reporting_callback) { |
| 140 reporting_callback_ = reporting_callback; | 159 reporting_callback_ = reporting_callback; |
| 141 } | 160 } |
| 142 | 161 |
| 143 void CPUTimeBudgetPool::Close() { | |
| 144 DCHECK_EQ(0u, associated_task_queues_.size()); | |
| 145 | |
| 146 budget_pool_controller_->UnregisterBudgetPool(this); | |
| 147 } | |
| 148 | |
| 149 bool CPUTimeBudgetPool::HasEnoughBudgetToRun(base::TimeTicks now) { | 162 bool CPUTimeBudgetPool::HasEnoughBudgetToRun(base::TimeTicks now) { |
| 150 return now >= GetNextAllowedRunTime(); | 163 return now >= GetNextAllowedRunTime(); |
| 151 } | 164 } |
| 152 | 165 |
| 153 base::TimeTicks CPUTimeBudgetPool::GetNextAllowedRunTime() { | 166 base::TimeTicks CPUTimeBudgetPool::GetNextAllowedRunTime() { |
| 154 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) { | 167 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) { |
| 155 return last_checkpoint_; | 168 return last_checkpoint_; |
| 156 } else { | 169 } else { |
| 157 // Subtract because current_budget is negative. | 170 // Subtract because current_budget is negative. |
| 158 return last_checkpoint_ + | 171 return last_checkpoint_ + |
| (...skipping 11 matching lines...) Expand all Loading... |
| 170 current_budget_level_ -= (end_time - start_time); | 183 current_budget_level_ -= (end_time - start_time); |
| 171 EnforceBudgetLevelRestrictions(); | 184 EnforceBudgetLevelRestrictions(); |
| 172 | 185 |
| 173 if (!reporting_callback_.is_null() && old_budget_level.InSecondsF() > 0 && | 186 if (!reporting_callback_.is_null() && old_budget_level.InSecondsF() > 0 && |
| 174 current_budget_level_.InSecondsF() < 0) { | 187 current_budget_level_.InSecondsF() < 0) { |
| 175 reporting_callback_.Run(-current_budget_level_ / cpu_percentage_); | 188 reporting_callback_.Run(-current_budget_level_ / cpu_percentage_); |
| 176 } | 189 } |
| 177 } | 190 } |
| 178 } | 191 } |
| 179 | 192 |
| 180 const char* CPUTimeBudgetPool::Name() const { | |
| 181 return name_; | |
| 182 } | |
| 183 | |
| 184 void CPUTimeBudgetPool::AsValueInto(base::trace_event::TracedValue* state, | 193 void CPUTimeBudgetPool::AsValueInto(base::trace_event::TracedValue* state, |
| 185 base::TimeTicks now) const { | 194 base::TimeTicks now) const { |
| 186 state->BeginDictionary(name_); | 195 state->BeginDictionary(name_); |
| 187 | 196 |
| 188 state->SetString("name", name_); | 197 state->SetString("name", name_); |
| 189 state->SetDouble("time_budget", cpu_percentage_); | 198 state->SetDouble("time_budget", cpu_percentage_); |
| 190 state->SetDouble("time_budget_level_in_seconds", | 199 state->SetDouble("time_budget_level_in_seconds", |
| 191 current_budget_level_.InSecondsF()); | 200 current_budget_level_.InSecondsF()); |
| 192 state->SetDouble("last_checkpoint_seconds_ago", | 201 state->SetDouble("last_checkpoint_seconds_ago", |
| 193 (now - last_checkpoint_).InSecondsF()); | 202 (now - last_checkpoint_).InSecondsF()); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 216 void CPUTimeBudgetPool::Advance(base::TimeTicks now) { | 225 void CPUTimeBudgetPool::Advance(base::TimeTicks now) { |
| 217 if (now > last_checkpoint_) { | 226 if (now > last_checkpoint_) { |
| 218 if (is_enabled_) { | 227 if (is_enabled_) { |
| 219 current_budget_level_ += cpu_percentage_ * (now - last_checkpoint_); | 228 current_budget_level_ += cpu_percentage_ * (now - last_checkpoint_); |
| 220 EnforceBudgetLevelRestrictions(); | 229 EnforceBudgetLevelRestrictions(); |
| 221 } | 230 } |
| 222 last_checkpoint_ = now; | 231 last_checkpoint_ = now; |
| 223 } | 232 } |
| 224 } | 233 } |
| 225 | 234 |
| 226 void CPUTimeBudgetPool::BlockThrottledQueues(base::TimeTicks now) { | |
| 227 for (TaskQueue* queue : associated_task_queues_) | |
| 228 budget_pool_controller_->BlockQueue(now, queue); | |
| 229 } | |
| 230 | |
| 231 void CPUTimeBudgetPool::EnforceBudgetLevelRestrictions() { | 235 void CPUTimeBudgetPool::EnforceBudgetLevelRestrictions() { |
| 232 if (max_budget_level_) { | 236 if (max_budget_level_) { |
| 233 current_budget_level_ = | 237 current_budget_level_ = |
| 234 std::min(current_budget_level_, max_budget_level_.value()); | 238 std::min(current_budget_level_, max_budget_level_.value()); |
| 235 } | 239 } |
| 236 if (max_throttling_delay_) { | 240 if (max_throttling_delay_) { |
| 237 // Current budget level may be negative. | 241 // Current budget level may be negative. |
| 238 current_budget_level_ = | 242 current_budget_level_ = |
| 239 std::max(current_budget_level_, | 243 std::max(current_budget_level_, |
| 240 -max_throttling_delay_.value() * cpu_percentage_); | 244 -max_throttling_delay_.value() * cpu_percentage_); |
| 241 } | 245 } |
| 242 } | 246 } |
| 243 | 247 |
| 244 } // namespace scheduler | 248 } // namespace scheduler |
| 245 } // namespace blink | 249 } // namespace blink |
| OLD | NEW |