Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(36)

Side by Side Diff: content/child/scheduler/scheduler_helper.cc

Issue 1058873010: Move blink scheduler implementation into a component (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: updates Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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 "content/child/scheduler/scheduler_helper.h"
6
7 #include "base/trace_event/trace_event.h"
8 #include "base/trace_event/trace_event_argument.h"
9 #include "content/child/scheduler/nestable_single_thread_task_runner.h"
10 #include "content/child/scheduler/prioritizing_task_queue_selector.h"
11 #include "content/child/scheduler/time_source.h"
12
13 namespace content {
14
15 SchedulerHelper::SchedulerHelper(
16 scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner,
17 SchedulerHelperDelegate* scheduler_helper_delegate,
18 const char* tracing_category,
19 const char* disabled_by_default_tracing_category,
20 const char* idle_period_tracing_name,
21 size_t total_task_queue_count,
22 base::TimeDelta required_quiescence_duration_before_long_idle_period)
23 : task_queue_selector_(new PrioritizingTaskQueueSelector()),
24 task_queue_manager_(
25 new TaskQueueManager(total_task_queue_count,
26 main_task_runner,
27 task_queue_selector_.get(),
28 disabled_by_default_tracing_category)),
29 idle_period_state_(IdlePeriodState::NOT_IN_IDLE_PERIOD),
30 scheduler_helper_delegate_(scheduler_helper_delegate),
31 control_task_runner_(
32 task_queue_manager_->TaskRunnerForQueue(QueueId::CONTROL_TASK_QUEUE)),
33 control_task_after_wakeup_runner_(task_queue_manager_->TaskRunnerForQueue(
34 QueueId::CONTROL_TASK_AFTER_WAKEUP_QUEUE)),
35 default_task_runner_(
36 task_queue_manager_->TaskRunnerForQueue(QueueId::DEFAULT_TASK_QUEUE)),
37 quiescence_monitored_task_queue_mask_(
38 ((1ull << total_task_queue_count) - 1ull) &
39 ~(1ull << QueueId::IDLE_TASK_QUEUE) &
40 ~(1ull << QueueId::CONTROL_TASK_QUEUE) &
41 ~(1ull << QueueId::CONTROL_TASK_AFTER_WAKEUP_QUEUE)),
42 required_quiescence_duration_before_long_idle_period_(
43 required_quiescence_duration_before_long_idle_period),
44 time_source_(new TimeSource),
45 tracing_category_(tracing_category),
46 disabled_by_default_tracing_category_(
47 disabled_by_default_tracing_category),
48 idle_period_tracing_name_(idle_period_tracing_name),
49 weak_factory_(this) {
50 DCHECK_GE(total_task_queue_count,
51 static_cast<size_t>(QueueId::TASK_QUEUE_COUNT));
52 weak_scheduler_ptr_ = weak_factory_.GetWeakPtr();
53 end_idle_period_closure_.Reset(
54 base::Bind(&SchedulerHelper::EndIdlePeriod, weak_scheduler_ptr_));
55 enable_next_long_idle_period_closure_.Reset(
56 base::Bind(&SchedulerHelper::EnableLongIdlePeriod, weak_scheduler_ptr_));
57 enable_next_long_idle_period_after_wakeup_closure_.Reset(base::Bind(
58 &SchedulerHelper::EnableLongIdlePeriodAfterWakeup, weak_scheduler_ptr_));
59
60 idle_task_runner_ = make_scoped_refptr(new SingleThreadIdleTaskRunner(
61 task_queue_manager_->TaskRunnerForQueue(QueueId::IDLE_TASK_QUEUE),
62 control_task_after_wakeup_runner_,
63 base::Bind(&SchedulerHelper::CurrentIdleTaskDeadlineCallback,
64 weak_scheduler_ptr_),
65 tracing_category));
66
67 task_queue_selector_->SetQueuePriority(
68 QueueId::CONTROL_TASK_QUEUE,
69 PrioritizingTaskQueueSelector::CONTROL_PRIORITY);
70
71 task_queue_selector_->SetQueuePriority(
72 QueueId::CONTROL_TASK_AFTER_WAKEUP_QUEUE,
73 PrioritizingTaskQueueSelector::CONTROL_PRIORITY);
74 task_queue_manager_->SetPumpPolicy(
75 QueueId::CONTROL_TASK_AFTER_WAKEUP_QUEUE,
76 TaskQueueManager::PumpPolicy::AFTER_WAKEUP);
77
78 task_queue_selector_->DisableQueue(QueueId::IDLE_TASK_QUEUE);
79 task_queue_manager_->SetPumpPolicy(QueueId::IDLE_TASK_QUEUE,
80 TaskQueueManager::PumpPolicy::MANUAL);
81
82 for (size_t i = 0; i < TASK_QUEUE_COUNT; i++) {
83 task_queue_manager_->SetQueueName(
84 i, TaskQueueIdToString(static_cast<QueueId>(i)));
85 }
86
87 // TODO(skyostil): Increase this to 4 (crbug.com/444764).
88 task_queue_manager_->SetWorkBatchSize(1);
89 }
90
91 SchedulerHelper::~SchedulerHelper() {
92 }
93
94 SchedulerHelper::SchedulerHelperDelegate::SchedulerHelperDelegate() {
95 }
96
97 SchedulerHelper::SchedulerHelperDelegate::~SchedulerHelperDelegate() {
98 }
99
100 void SchedulerHelper::Shutdown() {
101 CheckOnValidThread();
102 task_queue_manager_.reset();
103 }
104
105 scoped_refptr<base::SingleThreadTaskRunner>
106 SchedulerHelper::DefaultTaskRunner() {
107 CheckOnValidThread();
108 return default_task_runner_;
109 }
110
111 scoped_refptr<SingleThreadIdleTaskRunner> SchedulerHelper::IdleTaskRunner() {
112 CheckOnValidThread();
113 return idle_task_runner_;
114 }
115
116 scoped_refptr<base::SingleThreadTaskRunner>
117 SchedulerHelper::ControlTaskRunner() {
118 return control_task_runner_;
119 }
120
121 void SchedulerHelper::CurrentIdleTaskDeadlineCallback(
122 base::TimeTicks* deadline_out) const {
123 CheckOnValidThread();
124 *deadline_out = idle_period_deadline_;
125 }
126
127 SchedulerHelper::IdlePeriodState SchedulerHelper::ComputeNewLongIdlePeriodState(
128 const base::TimeTicks now,
129 base::TimeDelta* next_long_idle_period_delay_out) {
130 CheckOnValidThread();
131
132 if (!scheduler_helper_delegate_->CanEnterLongIdlePeriod(
133 now, next_long_idle_period_delay_out)) {
134 return IdlePeriodState::NOT_IN_IDLE_PERIOD;
135 }
136
137 base::TimeTicks next_pending_delayed_task =
138 task_queue_manager_->NextPendingDelayedTaskRunTime();
139 base::TimeDelta max_long_idle_period_duration =
140 base::TimeDelta::FromMilliseconds(kMaximumIdlePeriodMillis);
141 base::TimeDelta long_idle_period_duration;
142 if (next_pending_delayed_task.is_null()) {
143 long_idle_period_duration = max_long_idle_period_duration;
144 } else {
145 // Limit the idle period duration to be before the next pending task.
146 long_idle_period_duration = std::min(next_pending_delayed_task - now,
147 max_long_idle_period_duration);
148 }
149
150 if (long_idle_period_duration > base::TimeDelta()) {
151 *next_long_idle_period_delay_out = long_idle_period_duration;
152 return long_idle_period_duration == max_long_idle_period_duration
153 ? IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE
154 : IdlePeriodState::IN_LONG_IDLE_PERIOD;
155 } else {
156 // If we can't start the idle period yet then try again after wakeup.
157 *next_long_idle_period_delay_out = base::TimeDelta::FromMilliseconds(
158 kRetryEnableLongIdlePeriodDelayMillis);
159 return IdlePeriodState::NOT_IN_IDLE_PERIOD;
160 }
161 }
162
163 bool SchedulerHelper::ShouldWaitForQuiescence() {
164 CheckOnValidThread();
165
166 if (!task_queue_manager_)
167 return false;
168
169 if (required_quiescence_duration_before_long_idle_period_ ==
170 base::TimeDelta())
171 return false;
172
173 uint64 task_queues_run_since_last_check_bitmap =
174 task_queue_manager_->GetAndClearTaskWasRunOnQueueBitmap() &
175 quiescence_monitored_task_queue_mask_;
176
177 TRACE_EVENT1(disabled_by_default_tracing_category_, "ShouldWaitForQuiescence",
178 "task_queues_run_since_last_check_bitmap",
179 task_queues_run_since_last_check_bitmap);
180
181 // If anything was run on the queues we care about, then we're not quiescent
182 // and we should wait.
183 return task_queues_run_since_last_check_bitmap != 0;
184 }
185
186 void SchedulerHelper::EnableLongIdlePeriod() {
187 TRACE_EVENT0(disabled_by_default_tracing_category_, "EnableLongIdlePeriod");
188 CheckOnValidThread();
189
190 // End any previous idle period.
191 EndIdlePeriod();
192
193 if (ShouldWaitForQuiescence()) {
194 control_task_runner_->PostDelayedTask(
195 FROM_HERE, enable_next_long_idle_period_closure_.callback(),
196 required_quiescence_duration_before_long_idle_period_);
197 scheduler_helper_delegate_->IsNotQuiescent();
198 return;
199 }
200
201 base::TimeTicks now(Now());
202 base::TimeDelta next_long_idle_period_delay;
203 IdlePeriodState new_idle_period_state =
204 ComputeNewLongIdlePeriodState(now, &next_long_idle_period_delay);
205 if (IsInIdlePeriod(new_idle_period_state)) {
206 StartIdlePeriod(new_idle_period_state, now,
207 now + next_long_idle_period_delay,
208 false);
209 }
210
211 if (task_queue_manager_->IsQueueEmpty(QueueId::IDLE_TASK_QUEUE)) {
212 // If there are no current idle tasks then post the call to initiate the
213 // next idle for execution after wakeup (at which point after-wakeup idle
214 // tasks might be eligible to run or more idle tasks posted).
215 control_task_after_wakeup_runner_->PostDelayedTask(
216 FROM_HERE,
217 enable_next_long_idle_period_after_wakeup_closure_.callback(),
218 next_long_idle_period_delay);
219 } else {
220 // Otherwise post on the normal control task queue.
221 control_task_runner_->PostDelayedTask(
222 FROM_HERE, enable_next_long_idle_period_closure_.callback(),
223 next_long_idle_period_delay);
224 }
225 }
226
227 void SchedulerHelper::EnableLongIdlePeriodAfterWakeup() {
228 TRACE_EVENT0(disabled_by_default_tracing_category_,
229 "EnableLongIdlePeriodAfterWakeup");
230 CheckOnValidThread();
231
232 if (IsInIdlePeriod(idle_period_state_)) {
233 // Since we were asleep until now, end the async idle period trace event at
234 // the time when it would have ended were we awake.
235 TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0(
236 tracing_category_, idle_period_tracing_name_, this,
237 std::min(idle_period_deadline_, Now()).ToInternalValue());
238 idle_period_state_ = IdlePeriodState::ENDING_LONG_IDLE_PERIOD;
239 EndIdlePeriod();
240 }
241
242 // Post a task to initiate the next long idle period rather than calling it
243 // directly to allow all pending PostIdleTaskAfterWakeup tasks to get enqueued
244 // on the idle task queue before the next idle period starts so they are
245 // eligible to be run during the new idle period.
246 control_task_runner_->PostTask(
247 FROM_HERE, enable_next_long_idle_period_closure_.callback());
248 }
249
250 void SchedulerHelper::StartIdlePeriod(IdlePeriodState new_state,
251 base::TimeTicks now,
252 base::TimeTicks idle_period_deadline,
253 bool post_end_idle_period) {
254 DCHECK_GT(idle_period_deadline, now);
255 TRACE_EVENT_ASYNC_BEGIN0(tracing_category_, idle_period_tracing_name_, this);
256 CheckOnValidThread();
257 DCHECK(IsInIdlePeriod(new_state));
258
259 task_queue_selector_->EnableQueue(
260 QueueId::IDLE_TASK_QUEUE,
261 PrioritizingTaskQueueSelector::BEST_EFFORT_PRIORITY);
262 task_queue_manager_->PumpQueue(QueueId::IDLE_TASK_QUEUE);
263 idle_period_state_ = new_state;
264
265 idle_period_deadline_ = idle_period_deadline;
266 if (post_end_idle_period) {
267 control_task_runner_->PostDelayedTask(
268 FROM_HERE,
269 end_idle_period_closure_.callback(),
270 idle_period_deadline_ - now);
271 }
272 }
273
274 void SchedulerHelper::EndIdlePeriod() {
275 CheckOnValidThread();
276
277 end_idle_period_closure_.Cancel();
278 enable_next_long_idle_period_closure_.Cancel();
279 enable_next_long_idle_period_after_wakeup_closure_.Cancel();
280
281 // If we weren't already within an idle period then early-out.
282 if (!IsInIdlePeriod(idle_period_state_))
283 return;
284
285 // If we are in the ENDING_LONG_IDLE_PERIOD state we have already logged the
286 // trace event.
287 if (idle_period_state_ != IdlePeriodState::ENDING_LONG_IDLE_PERIOD) {
288 bool is_tracing;
289 TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
290 if (is_tracing && !idle_period_deadline_.is_null() &&
291 base::TimeTicks::Now() > idle_period_deadline_) {
292 TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(
293 tracing_category_, idle_period_tracing_name_, this, "DeadlineOverrun",
294 idle_period_deadline_.ToInternalValue());
295 }
296 TRACE_EVENT_ASYNC_END0(tracing_category_, idle_period_tracing_name_, this);
297 }
298
299 task_queue_selector_->DisableQueue(QueueId::IDLE_TASK_QUEUE);
300 idle_period_state_ = IdlePeriodState::NOT_IN_IDLE_PERIOD;
301 idle_period_deadline_ = base::TimeTicks();
302 }
303
304 // static
305 bool SchedulerHelper::IsInIdlePeriod(IdlePeriodState state) {
306 return state != IdlePeriodState::NOT_IN_IDLE_PERIOD;
307 }
308
309 bool SchedulerHelper::CanExceedIdleDeadlineIfRequired() const {
310 TRACE_EVENT0(tracing_category_, "CanExceedIdleDeadlineIfRequired");
311 CheckOnValidThread();
312 return idle_period_state_ ==
313 IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE;
314 }
315
316 void SchedulerHelper::SetTimeSourceForTesting(
317 scoped_ptr<TimeSource> time_source) {
318 CheckOnValidThread();
319 time_source_ = time_source.Pass();
320 }
321
322 void SchedulerHelper::SetWorkBatchSizeForTesting(size_t work_batch_size) {
323 CheckOnValidThread();
324 task_queue_manager_->SetWorkBatchSize(work_batch_size);
325 }
326
327 TaskQueueManager* SchedulerHelper::GetTaskQueueManagerForTesting() {
328 CheckOnValidThread();
329 return task_queue_manager_.get();
330 }
331
332 base::TimeTicks SchedulerHelper::Now() const {
333 return time_source_->Now();
334 }
335
336 SchedulerHelper::IdlePeriodState
337 SchedulerHelper::SchedulerIdlePeriodState() const {
338 return idle_period_state_;
339 }
340
341 PrioritizingTaskQueueSelector*
342 SchedulerHelper::SchedulerTaskQueueSelector() const {
343 return task_queue_selector_.get();
344 }
345
346 scoped_refptr<base::SingleThreadTaskRunner> SchedulerHelper::TaskRunnerForQueue(
347 size_t queue_index) const {
348 CheckOnValidThread();
349 return task_queue_manager_->TaskRunnerForQueue(queue_index);
350 }
351
352 void SchedulerHelper::SetQueueName(size_t queue_index, const char* name) {
353 CheckOnValidThread();
354 task_queue_manager_->SetQueueName(queue_index, name);
355 }
356
357 bool SchedulerHelper::IsQueueEmpty(size_t queue_index) const {
358 CheckOnValidThread();
359 return task_queue_manager_->IsQueueEmpty(queue_index);
360 }
361
362 // static
363 const char* SchedulerHelper::TaskQueueIdToString(QueueId queue_id) {
364 switch (queue_id) {
365 case DEFAULT_TASK_QUEUE:
366 return "default_tq";
367 case IDLE_TASK_QUEUE:
368 return "idle_tq";
369 case CONTROL_TASK_QUEUE:
370 return "control_tq";
371 case CONTROL_TASK_AFTER_WAKEUP_QUEUE:
372 return "control_after_wakeup_tq";
373 default:
374 NOTREACHED();
375 return nullptr;
376 }
377 }
378
379 // static
380 const char* SchedulerHelper::IdlePeriodStateToString(
381 IdlePeriodState idle_period_state) {
382 switch (idle_period_state) {
383 case IdlePeriodState::NOT_IN_IDLE_PERIOD:
384 return "not_in_idle_period";
385 case IdlePeriodState::IN_SHORT_IDLE_PERIOD:
386 return "in_short_idle_period";
387 case IdlePeriodState::IN_LONG_IDLE_PERIOD:
388 return "in_long_idle_period";
389 case IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE:
390 return "in_long_idle_period_with_max_deadline";
391 case IdlePeriodState::ENDING_LONG_IDLE_PERIOD:
392 return "ending_long_idle_period";
393 default:
394 NOTREACHED();
395 return nullptr;
396 }
397 }
398
399 void SchedulerHelper::AddTaskObserver(
400 base::MessageLoop::TaskObserver* task_observer) {
401 CheckOnValidThread();
402 if (task_queue_manager_)
403 task_queue_manager_->AddTaskObserver(task_observer);
404 }
405
406 void SchedulerHelper::RemoveTaskObserver(
407 base::MessageLoop::TaskObserver* task_observer) {
408 CheckOnValidThread();
409 if (task_queue_manager_)
410 task_queue_manager_->RemoveTaskObserver(task_observer);
411 }
412
413 } // namespace content
OLDNEW
« no previous file with comments | « content/child/scheduler/scheduler_helper.h ('k') | content/child/scheduler/scheduler_helper_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698