OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "platform/scheduler/renderer/budget_pool.h" | |
6 | |
7 #include <cstdint> | |
8 | |
9 #include "base/format_macros.h" | |
10 #include "base/logging.h" | |
11 #include "base/memory/ptr_util.h" | |
12 #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" | |
19 #include "platform/scheduler/renderer/throttled_time_domain.h" | |
20 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h" | |
21 | |
22 namespace blink { | |
23 namespace scheduler { | |
24 | |
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() {} | |
36 | |
37 TimeBudgetPool::TimeBudgetPool( | |
Sami
2017/03/08 16:18:01
Could we extract some unit tests for this too? (I
altimin
2017/03/08 16:50:32
Done.
| |
38 const char* name, | |
39 TaskQueueThrottler* task_queue_throttler, | |
40 base::TimeTicks now, | |
41 base::Optional<base::TimeDelta> max_budget_level, | |
42 base::Optional<base::TimeDelta> max_throttling_duration) | |
43 : name_(name), | |
44 task_queue_throttler_(task_queue_throttler), | |
45 max_budget_level_(max_budget_level), | |
46 max_throttling_duration_(max_throttling_duration), | |
47 last_checkpoint_(now), | |
48 cpu_percentage_(1), | |
49 is_enabled_(true) {} | |
50 | |
51 TimeBudgetPool::~TimeBudgetPool() {} | |
52 | |
53 void TimeBudgetPool::SetTimeBudgetRecoveryRate( | |
54 base::TimeTicks now, | |
55 double cpu_percentage) { | |
56 Advance(now); | |
57 cpu_percentage_ = cpu_percentage; | |
58 EnforceBudgetLevelRestrictions(); | |
59 } | |
60 | |
61 void TimeBudgetPool::AddQueue(base::TimeTicks now, TaskQueue* queue) { | |
62 std::pair<TaskQueueThrottler::TaskQueueMap::iterator, bool> insert_result = | |
63 task_queue_throttler_->queue_details_.insert( | |
64 std::make_pair(queue, TaskQueueThrottler::Metadata())); | |
65 TaskQueueThrottler::Metadata& metadata = insert_result.first->second; | |
66 DCHECK(!metadata.time_budget_pool); | |
67 metadata.time_budget_pool = this; | |
68 | |
69 associated_task_queues_.insert(queue); | |
70 | |
71 if (!is_enabled_ || !task_queue_throttler_->IsThrottled(queue)) | |
72 return; | |
73 | |
74 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); | |
75 | |
76 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, | |
77 GetNextAllowedRunTime()); | |
78 } | |
79 | |
80 void TimeBudgetPool::RemoveQueue(base::TimeTicks now, | |
81 TaskQueue* queue) { | |
82 auto find_it = task_queue_throttler_->queue_details_.find(queue); | |
83 DCHECK(find_it != task_queue_throttler_->queue_details_.end() && | |
84 find_it->second.time_budget_pool == this); | |
85 find_it->second.time_budget_pool = nullptr; | |
86 bool is_throttled = task_queue_throttler_->IsThrottled(queue); | |
87 | |
88 task_queue_throttler_->MaybeDeleteQueueMetadata(find_it); | |
89 associated_task_queues_.erase(queue); | |
90 | |
91 if (!is_enabled_ || !is_throttled) | |
92 return; | |
93 | |
94 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, | |
95 base::nullopt); | |
96 } | |
97 | |
98 void TimeBudgetPool::EnableThrottling(LazyNow* lazy_now) { | |
99 if (is_enabled_) | |
100 return; | |
101 is_enabled_ = true; | |
102 | |
103 TRACE_EVENT0(task_queue_throttler_->tracing_category_, | |
104 "TaskQueueThrottler_TimeBudgetPool_EnableThrottling"); | |
Sami
2017/03/08 16:18:00
Please remove TaskQueueThrottler from these trace
altimin
2017/03/08 16:50:32
Done.
| |
105 | |
106 BlockThrottledQueues(lazy_now->Now()); | |
107 } | |
108 | |
109 void TimeBudgetPool::DisableThrottling(LazyNow* lazy_now) { | |
110 if (!is_enabled_) | |
111 return; | |
112 is_enabled_ = false; | |
113 | |
114 TRACE_EVENT0(task_queue_throttler_->tracing_category_, | |
115 "TaskQueueThrottler_TimeBudgetPool_DisableThrottling"); | |
116 | |
117 for (TaskQueue* queue : associated_task_queues_) { | |
118 if (!task_queue_throttler_->IsThrottled(queue)) | |
119 continue; | |
120 | |
121 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, lazy_now->Now(), | |
122 queue, base::nullopt); | |
123 } | |
124 | |
125 // TODO(altimin): We need to disable TimeBudgetQueues here or they will | |
126 // regenerate extra time budget when they are disabled. | |
127 } | |
128 | |
129 bool TimeBudgetPool::IsThrottlingEnabled() const { | |
130 return is_enabled_; | |
131 } | |
132 | |
133 void TimeBudgetPool::GrantAdditionalBudget( | |
134 base::TimeTicks now, | |
135 base::TimeDelta budget_level) { | |
136 Advance(now); | |
137 current_budget_level_ += budget_level; | |
138 EnforceBudgetLevelRestrictions(); | |
139 } | |
140 | |
141 void TimeBudgetPool::SetReportingCallback( | |
142 base::Callback<void(base::TimeDelta)> reporting_callback) { | |
143 reporting_callback_ = reporting_callback; | |
144 } | |
145 | |
146 void TimeBudgetPool::Close() { | |
147 DCHECK_EQ(0u, associated_task_queues_.size()); | |
148 | |
149 task_queue_throttler_->time_budget_pools_.erase(this); | |
150 } | |
151 | |
152 bool TimeBudgetPool::HasEnoughBudgetToRun( | |
153 base::TimeTicks now) { | |
154 Advance(now); | |
155 return !is_enabled_ || current_budget_level_.InMicroseconds() >= 0; | |
156 } | |
157 | |
158 base::TimeTicks TimeBudgetPool::GetNextAllowedRunTime() { | |
159 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) { | |
160 return last_checkpoint_; | |
161 } else { | |
162 // Subtract because current_budget is negative. | |
163 return last_checkpoint_ - current_budget_level_ / cpu_percentage_; | |
164 } | |
165 } | |
166 | |
167 void TimeBudgetPool::RecordTaskRunTime( | |
168 base::TimeTicks start_time, | |
169 base::TimeTicks end_time) { | |
170 DCHECK_LE(start_time, end_time); | |
171 Advance(end_time); | |
172 if (is_enabled_) { | |
173 base::TimeDelta old_budget_level = current_budget_level_; | |
174 current_budget_level_ -= (end_time - start_time); | |
175 EnforceBudgetLevelRestrictions(); | |
176 | |
177 if (!reporting_callback_.is_null() && old_budget_level.InSecondsF() > 0 && | |
178 current_budget_level_.InSecondsF() < 0) { | |
179 reporting_callback_.Run(-current_budget_level_ / cpu_percentage_); | |
180 } | |
181 } | |
182 } | |
183 | |
184 const char* TimeBudgetPool::Name() const { | |
185 return name_; | |
186 } | |
187 | |
188 void TimeBudgetPool::AsValueInto( | |
189 base::trace_event::TracedValue* state, | |
190 base::TimeTicks now) const { | |
191 state->BeginDictionary(name_); | |
192 | |
193 state->SetString("name", name_); | |
194 state->SetDouble("time_budget", cpu_percentage_); | |
195 state->SetDouble("time_budget_level_in_seconds", | |
196 current_budget_level_.InSecondsF()); | |
197 state->SetDouble("last_checkpoint_seconds_ago", | |
198 (now - last_checkpoint_).InSecondsF()); | |
199 state->SetBoolean("is_enabled", is_enabled_); | |
200 | |
201 state->BeginArray("task_queues"); | |
202 for (TaskQueue* queue : associated_task_queues_) { | |
203 state->AppendString(PointerToId(queue)); | |
204 } | |
205 state->EndArray(); | |
206 | |
207 state->EndDictionary(); | |
208 } | |
209 | |
210 void TimeBudgetPool::Advance(base::TimeTicks now) { | |
211 if (now > last_checkpoint_) { | |
212 if (is_enabled_) { | |
213 current_budget_level_ += cpu_percentage_ * (now - last_checkpoint_); | |
214 EnforceBudgetLevelRestrictions(); | |
215 } | |
216 last_checkpoint_ = now; | |
217 } | |
218 } | |
219 | |
220 void TimeBudgetPool::BlockThrottledQueues( | |
221 base::TimeTicks now) { | |
222 for (TaskQueue* queue : associated_task_queues_) { | |
223 if (!task_queue_throttler_->IsThrottled(queue)) | |
224 continue; | |
225 | |
226 queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); | |
227 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, | |
228 base::nullopt); | |
229 } | |
230 } | |
231 | |
232 void TimeBudgetPool::EnforceBudgetLevelRestrictions() { | |
233 if (max_budget_level_) { | |
234 current_budget_level_ = | |
235 std::min(current_budget_level_, max_budget_level_.value()); | |
236 } | |
237 if (max_throttling_duration_) { | |
238 // Current budget level may be negative. | |
239 current_budget_level_ = | |
240 std::max(current_budget_level_, | |
241 -max_throttling_duration_.value() * cpu_percentage_); | |
242 } | |
243 } | |
244 | |
245 } // namespace scheduler | |
246 } // namespace blink | |
OLD | NEW |