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" | |
10 #include "base/logging.h" | |
11 #include "base/memory/ptr_util.h" | 9 #include "base/memory/ptr_util.h" |
12 #include "base/optional.h" | 10 #include "base/optional.h" |
13 #include "base/strings/stringprintf.h" | |
14 #include "platform/WebFrameScheduler.h" | |
15 #include "platform/scheduler/base/real_time_domain.h" | |
16 #include "platform/scheduler/child/scheduler_tqm_delegate.h" | |
17 #include "platform/scheduler/renderer/renderer_scheduler_impl.h" | |
18 #include "platform/scheduler/renderer/task_queue_throttler.h" | 11 #include "platform/scheduler/renderer/task_queue_throttler.h" |
19 #include "platform/scheduler/renderer/throttled_time_domain.h" | |
20 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h" | |
21 | 12 |
22 namespace blink { | 13 namespace blink { |
23 namespace scheduler { | 14 namespace scheduler { |
24 | 15 |
25 namespace { | |
26 | |
27 std::string PointerToId(void* pointer) { | |
28 return base::StringPrintf( | |
29 "0x%" PRIx64, | |
30 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(pointer))); | |
31 } | |
32 | |
33 } // namespace | |
34 | |
35 BudgetPool::BudgetPool(const char* name, | 16 BudgetPool::BudgetPool(const char* name, |
36 BudgetPoolController* budget_pool_controller) | 17 BudgetPoolController* budget_pool_controller) |
37 : name_(name), | 18 : name_(name), |
38 budget_pool_controller_(budget_pool_controller), | 19 budget_pool_controller_(budget_pool_controller), |
39 is_enabled_(true) {} | 20 is_enabled_(true) {} |
40 | 21 |
41 BudgetPool::~BudgetPool() {} | 22 BudgetPool::~BudgetPool() {} |
42 | 23 |
43 const char* BudgetPool::Name() const { | 24 const char* BudgetPool::Name() const { |
44 return name_; | 25 return name_; |
45 } | 26 } |
46 | 27 |
47 void BudgetPool::AddQueue(base::TimeTicks now, TaskQueue* queue) { | 28 void BudgetPool::AddQueue(base::TimeTicks now, TaskQueue* queue) { |
48 budget_pool_controller_->AddQueueToBudgetPool(queue, this); | 29 budget_pool_controller_->AddQueueToBudgetPool(queue, this); |
49 associated_task_queues_.insert(queue); | 30 associated_task_queues_.insert(queue); |
50 | 31 |
51 if (!is_enabled_ || !budget_pool_controller_->IsThrottled(queue)) | 32 if (!is_enabled_) |
52 return; | 33 return; |
| 34 budget_pool_controller_->UpdateQueueThrottlingState(now, queue); |
| 35 } |
53 | 36 |
54 budget_pool_controller_->BlockQueue(now, queue); | 37 void BudgetPool::UnregisterQueue(TaskQueue* queue) { |
| 38 DissociateQueue(queue); |
55 } | 39 } |
56 | 40 |
57 void BudgetPool::RemoveQueue(base::TimeTicks now, TaskQueue* queue) { | 41 void BudgetPool::RemoveQueue(base::TimeTicks now, TaskQueue* queue) { |
| 42 DissociateQueue(queue); |
| 43 |
| 44 if (!is_enabled_) |
| 45 return; |
| 46 |
| 47 budget_pool_controller_->UpdateQueueThrottlingState(now, queue); |
| 48 } |
| 49 |
| 50 void BudgetPool::DissociateQueue(TaskQueue* queue) { |
58 budget_pool_controller_->RemoveQueueFromBudgetPool(queue, this); | 51 budget_pool_controller_->RemoveQueueFromBudgetPool(queue, this); |
59 associated_task_queues_.erase(queue); | 52 associated_task_queues_.erase(queue); |
60 | |
61 if (!is_enabled_ || !budget_pool_controller_->IsThrottled(queue)) | |
62 return; | |
63 | |
64 budget_pool_controller_->UnblockQueue(now, queue); | |
65 } | 53 } |
66 | 54 |
67 void BudgetPool::EnableThrottling(LazyNow* lazy_now) { | 55 void BudgetPool::EnableThrottling(LazyNow* lazy_now) { |
68 if (is_enabled_) | 56 if (is_enabled_) |
69 return; | 57 return; |
70 is_enabled_ = true; | 58 is_enabled_ = true; |
71 | 59 |
72 TRACE_EVENT0("renderer.scheduler", "BudgetPool_EnableThrottling"); | 60 TRACE_EVENT0("renderer.scheduler", "BudgetPool_EnableThrottling"); |
73 | 61 |
74 BlockThrottledQueues(lazy_now->Now()); | 62 BlockThrottledQueues(lazy_now->Now()); |
75 } | 63 } |
76 | 64 |
77 void BudgetPool::DisableThrottling(LazyNow* lazy_now) { | 65 void BudgetPool::DisableThrottling(LazyNow* lazy_now) { |
78 if (!is_enabled_) | 66 if (!is_enabled_) |
79 return; | 67 return; |
80 is_enabled_ = false; | 68 is_enabled_ = false; |
81 | 69 |
82 TRACE_EVENT0("renderer.scheduler", "BudgetPool_DisableThrottling"); | 70 TRACE_EVENT0("renderer.scheduler", "BudgetPool_DisableThrottling"); |
83 | 71 |
84 for (TaskQueue* queue : associated_task_queues_) { | 72 for (TaskQueue* queue : associated_task_queues_) { |
85 if (!budget_pool_controller_->IsThrottled(queue)) | 73 budget_pool_controller_->UpdateQueueThrottlingState(lazy_now->Now(), queue); |
86 continue; | |
87 | |
88 budget_pool_controller_->UnblockQueue(lazy_now->Now(), queue); | |
89 } | 74 } |
90 | 75 |
91 // TODO(altimin): We need to disable TimeBudgetQueues here or they will | 76 // TODO(altimin): We need to disable TimeBudgetQueues here or they will |
92 // regenerate extra time budget when they are disabled. | 77 // regenerate extra time budget when they are disabled. |
93 } | 78 } |
94 | 79 |
95 bool BudgetPool::IsThrottlingEnabled() const { | 80 bool BudgetPool::IsThrottlingEnabled() const { |
96 return is_enabled_; | 81 return is_enabled_; |
97 } | 82 } |
98 | 83 |
99 void BudgetPool::Close() { | 84 void BudgetPool::Close() { |
100 DCHECK_EQ(0u, associated_task_queues_.size()); | 85 DCHECK_EQ(0u, associated_task_queues_.size()); |
101 | 86 |
102 budget_pool_controller_->UnregisterBudgetPool(this); | 87 budget_pool_controller_->UnregisterBudgetPool(this); |
103 } | 88 } |
104 | 89 |
105 void BudgetPool::BlockThrottledQueues(base::TimeTicks now) { | 90 void BudgetPool::BlockThrottledQueues(base::TimeTicks now) { |
106 for (TaskQueue* queue : associated_task_queues_) | 91 for (TaskQueue* queue : associated_task_queues_) |
107 budget_pool_controller_->BlockQueue(now, queue); | 92 budget_pool_controller_->UpdateQueueThrottlingState(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 | |
150 void CPUTimeBudgetPool::GrantAdditionalBudget(base::TimeTicks now, | |
151 base::TimeDelta budget_level) { | |
152 Advance(now); | |
153 current_budget_level_ += budget_level; | |
154 EnforceBudgetLevelRestrictions(); | |
155 } | |
156 | |
157 void CPUTimeBudgetPool::SetReportingCallback( | |
158 base::Callback<void(base::TimeDelta)> reporting_callback) { | |
159 reporting_callback_ = reporting_callback; | |
160 } | |
161 | |
162 bool CPUTimeBudgetPool::HasEnoughBudgetToRun(base::TimeTicks now) { | |
163 return now >= GetNextAllowedRunTime(); | |
164 } | |
165 | |
166 base::TimeTicks CPUTimeBudgetPool::GetNextAllowedRunTime() { | |
167 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) { | |
168 return last_checkpoint_; | |
169 } else { | |
170 // Subtract because current_budget is negative. | |
171 return last_checkpoint_ + | |
172 (-current_budget_level_ + min_budget_level_to_run_) / | |
173 cpu_percentage_; | |
174 } | |
175 } | |
176 | |
177 void CPUTimeBudgetPool::RecordTaskRunTime(base::TimeTicks start_time, | |
178 base::TimeTicks end_time) { | |
179 DCHECK_LE(start_time, end_time); | |
180 Advance(end_time); | |
181 if (is_enabled_) { | |
182 base::TimeDelta old_budget_level = current_budget_level_; | |
183 current_budget_level_ -= (end_time - start_time); | |
184 EnforceBudgetLevelRestrictions(); | |
185 | |
186 if (!reporting_callback_.is_null() && old_budget_level.InSecondsF() > 0 && | |
187 current_budget_level_.InSecondsF() < 0) { | |
188 reporting_callback_.Run(-current_budget_level_ / cpu_percentage_); | |
189 } | |
190 } | |
191 } | |
192 | |
193 void CPUTimeBudgetPool::AsValueInto(base::trace_event::TracedValue* state, | |
194 base::TimeTicks now) const { | |
195 state->BeginDictionary(name_); | |
196 | |
197 state->SetString("name", name_); | |
198 state->SetDouble("time_budget", cpu_percentage_); | |
199 state->SetDouble("time_budget_level_in_seconds", | |
200 current_budget_level_.InSecondsF()); | |
201 state->SetDouble("last_checkpoint_seconds_ago", | |
202 (now - last_checkpoint_).InSecondsF()); | |
203 state->SetBoolean("is_enabled", is_enabled_); | |
204 state->SetDouble("min_budget_level_to_run_in_seconds", | |
205 min_budget_level_to_run_.InSecondsF()); | |
206 | |
207 if (max_throttling_delay_) { | |
208 state->SetDouble("max_throttling_delay_in_seconds", | |
209 max_throttling_delay_.value().InSecondsF()); | |
210 } | |
211 if (max_budget_level_) { | |
212 state->SetDouble("max_budget_level_in_seconds", | |
213 max_budget_level_.value().InSecondsF()); | |
214 } | |
215 | |
216 state->BeginArray("task_queues"); | |
217 for (TaskQueue* queue : associated_task_queues_) { | |
218 state->AppendString(PointerToId(queue)); | |
219 } | |
220 state->EndArray(); | |
221 | |
222 state->EndDictionary(); | |
223 } | |
224 | |
225 void CPUTimeBudgetPool::Advance(base::TimeTicks now) { | |
226 if (now > last_checkpoint_) { | |
227 if (is_enabled_) { | |
228 current_budget_level_ += cpu_percentage_ * (now - last_checkpoint_); | |
229 EnforceBudgetLevelRestrictions(); | |
230 } | |
231 last_checkpoint_ = now; | |
232 } | |
233 } | |
234 | |
235 void CPUTimeBudgetPool::EnforceBudgetLevelRestrictions() { | |
236 if (max_budget_level_) { | |
237 current_budget_level_ = | |
238 std::min(current_budget_level_, max_budget_level_.value()); | |
239 } | |
240 if (max_throttling_delay_) { | |
241 // Current budget level may be negative. | |
242 current_budget_level_ = | |
243 std::max(current_budget_level_, | |
244 -max_throttling_delay_.value() * cpu_percentage_); | |
245 } | |
246 } | 93 } |
247 | 94 |
248 } // namespace scheduler | 95 } // namespace scheduler |
249 } // namespace blink | 96 } // namespace blink |
OLD | NEW |