[Media Router] Add integration tests and e2e tests for media router and presentation...
[chromium-blink-merge.git] / components / scheduler / child / idle_helper.cc
blob756fc1b8b2881a8e12ac873881100aa0b593408c
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.
5 #include "components/scheduler/child/idle_helper.h"
7 #include "base/time/time.h"
8 #include "base/trace_event/trace_event.h"
9 #include "base/trace_event/trace_event_argument.h"
10 #include "components/scheduler/child/scheduler_helper.h"
12 namespace scheduler {
14 IdleHelper::IdleHelper(
15 SchedulerHelper* helper,
16 Delegate* delegate,
17 size_t idle_queue_index,
18 const char* tracing_category,
19 const char* disabled_by_default_tracing_category,
20 const char* idle_period_tracing_name,
21 base::TimeDelta required_quiescence_duration_before_long_idle_period)
22 : helper_(helper),
23 delegate_(delegate),
24 idle_queue_index_(idle_queue_index),
25 state_(helper,
26 delegate,
27 tracing_category,
28 disabled_by_default_tracing_category,
29 idle_period_tracing_name),
30 quiescence_monitored_task_queue_mask_(
31 helper_->GetQuiescenceMonitoredTaskQueueMask() &
32 ~(1ull << idle_queue_index_)),
33 required_quiescence_duration_before_long_idle_period_(
34 required_quiescence_duration_before_long_idle_period),
35 disabled_by_default_tracing_category_(
36 disabled_by_default_tracing_category),
37 weak_factory_(this) {
38 weak_idle_helper_ptr_ = weak_factory_.GetWeakPtr();
39 enable_next_long_idle_period_closure_.Reset(
40 base::Bind(&IdleHelper::EnableLongIdlePeriod, weak_idle_helper_ptr_));
41 on_idle_task_posted_closure_.Reset(base::Bind(
42 &IdleHelper::OnIdleTaskPostedOnMainThread, weak_idle_helper_ptr_));
44 idle_task_runner_ = make_scoped_refptr(new SingleThreadIdleTaskRunner(
45 helper_->TaskRunnerForQueue(idle_queue_index_),
46 helper_->ControlAfterWakeUpTaskRunner(), this, tracing_category));
48 helper_->DisableQueue(idle_queue_index_);
49 helper_->SetPumpPolicy(idle_queue_index_,
50 TaskQueueManager::PumpPolicy::MANUAL);
52 helper_->AddTaskObserver(this);
55 IdleHelper::~IdleHelper() {
56 helper_->RemoveTaskObserver(this);
59 IdleHelper::Delegate::Delegate() {
62 IdleHelper::Delegate::~Delegate() {
65 scoped_refptr<SingleThreadIdleTaskRunner> IdleHelper::IdleTaskRunner() {
66 helper_->CheckOnValidThread();
67 return idle_task_runner_;
70 IdleHelper::IdlePeriodState IdleHelper::ComputeNewLongIdlePeriodState(
71 const base::TimeTicks now,
72 base::TimeDelta* next_long_idle_period_delay_out) {
73 helper_->CheckOnValidThread();
75 if (!delegate_->CanEnterLongIdlePeriod(now,
76 next_long_idle_period_delay_out)) {
77 return IdlePeriodState::NOT_IN_IDLE_PERIOD;
80 base::TimeTicks next_pending_delayed_task =
81 helper_->NextPendingDelayedTaskRunTime();
82 base::TimeDelta max_long_idle_period_duration =
83 base::TimeDelta::FromMilliseconds(kMaximumIdlePeriodMillis);
84 base::TimeDelta long_idle_period_duration;
85 if (next_pending_delayed_task.is_null()) {
86 long_idle_period_duration = max_long_idle_period_duration;
87 } else {
88 // Limit the idle period duration to be before the next pending task.
89 long_idle_period_duration = std::min(next_pending_delayed_task - now,
90 max_long_idle_period_duration);
93 if (long_idle_period_duration >=
94 base::TimeDelta::FromMilliseconds(kMinimumIdlePeriodDurationMillis)) {
95 *next_long_idle_period_delay_out = long_idle_period_duration;
96 if (helper_->IsQueueEmpty(idle_queue_index_)) {
97 return IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED;
98 } else if (long_idle_period_duration == max_long_idle_period_duration) {
99 return IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE;
100 } else {
101 return IdlePeriodState::IN_LONG_IDLE_PERIOD;
103 } else {
104 // If we can't start the idle period yet then try again after wakeup.
105 *next_long_idle_period_delay_out = base::TimeDelta::FromMilliseconds(
106 kRetryEnableLongIdlePeriodDelayMillis);
107 return IdlePeriodState::NOT_IN_IDLE_PERIOD;
111 bool IdleHelper::ShouldWaitForQuiescence() {
112 helper_->CheckOnValidThread();
114 if (helper_->IsShutdown())
115 return false;
117 if (required_quiescence_duration_before_long_idle_period_ ==
118 base::TimeDelta())
119 return false;
121 uint64 task_queues_run_since_last_check_bitmap =
122 helper_->GetAndClearTaskWasRunOnQueueBitmap() &
123 quiescence_monitored_task_queue_mask_;
125 TRACE_EVENT1(disabled_by_default_tracing_category_, "ShouldWaitForQuiescence",
126 "task_queues_run_since_last_check_bitmap",
127 task_queues_run_since_last_check_bitmap);
129 // If anything was run on the queues we care about, then we're not quiescent
130 // and we should wait.
131 return task_queues_run_since_last_check_bitmap != 0;
134 void IdleHelper::EnableLongIdlePeriod() {
135 TRACE_EVENT0(disabled_by_default_tracing_category_, "EnableLongIdlePeriod");
136 helper_->CheckOnValidThread();
137 if (helper_->IsShutdown())
138 return;
140 // End any previous idle period.
141 EndIdlePeriod();
143 if (ShouldWaitForQuiescence()) {
144 helper_->ControlTaskRunner()->PostDelayedTask(
145 FROM_HERE, enable_next_long_idle_period_closure_.callback(),
146 required_quiescence_duration_before_long_idle_period_);
147 delegate_->IsNotQuiescent();
148 return;
151 base::TimeTicks now(helper_->Now());
152 base::TimeDelta next_long_idle_period_delay;
153 IdlePeriodState new_idle_period_state =
154 ComputeNewLongIdlePeriodState(now, &next_long_idle_period_delay);
155 if (IsInIdlePeriod(new_idle_period_state)) {
156 StartIdlePeriod(new_idle_period_state, now,
157 now + next_long_idle_period_delay);
158 } else {
159 // Otherwise wait for the next long idle period delay before trying again.
160 helper_->ControlTaskRunner()->PostDelayedTask(
161 FROM_HERE, enable_next_long_idle_period_closure_.callback(),
162 next_long_idle_period_delay);
166 void IdleHelper::StartIdlePeriod(IdlePeriodState new_state,
167 base::TimeTicks now,
168 base::TimeTicks idle_period_deadline) {
169 DCHECK_GT(idle_period_deadline, now);
170 helper_->CheckOnValidThread();
171 DCHECK(IsInIdlePeriod(new_state));
173 base::TimeDelta idle_period_duration(idle_period_deadline - now);
174 if (idle_period_duration <
175 base::TimeDelta::FromMilliseconds(kMinimumIdlePeriodDurationMillis)) {
176 TRACE_EVENT1(disabled_by_default_tracing_category_,
177 "NotStartingIdlePeriodBecauseDeadlineIsTooClose",
178 "idle_period_duration_ms",
179 idle_period_duration.InMillisecondsF());
180 return;
183 TRACE_EVENT0(disabled_by_default_tracing_category_, "StartIdlePeriod");
184 helper_->EnableQueue(idle_queue_index_,
185 PrioritizingTaskQueueSelector::BEST_EFFORT_PRIORITY);
186 helper_->PumpQueue(idle_queue_index_);
188 state_.UpdateState(new_state, idle_period_deadline, now);
191 void IdleHelper::EndIdlePeriod() {
192 helper_->CheckOnValidThread();
193 TRACE_EVENT0(disabled_by_default_tracing_category_, "EndIdlePeriod");
195 enable_next_long_idle_period_closure_.Cancel();
196 on_idle_task_posted_closure_.Cancel();
198 // If we weren't already within an idle period then early-out.
199 if (!IsInIdlePeriod(state_.idle_period_state()))
200 return;
202 helper_->DisableQueue(idle_queue_index_);
203 state_.UpdateState(IdlePeriodState::NOT_IN_IDLE_PERIOD, base::TimeTicks(),
204 base::TimeTicks());
207 void IdleHelper::WillProcessTask(const base::PendingTask& pending_task) {
210 void IdleHelper::DidProcessTask(const base::PendingTask& pending_task) {
211 helper_->CheckOnValidThread();
212 TRACE_EVENT0(disabled_by_default_tracing_category_, "DidProcessTask");
213 if (IsInIdlePeriod(state_.idle_period_state()) &&
214 state_.idle_period_state() !=
215 IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED &&
216 helper_->Now() >= state_.idle_period_deadline()) {
217 // If the idle period deadline has now been reached, either end the idle
218 // period or trigger a new long-idle period.
219 if (IsInLongIdlePeriod(state_.idle_period_state())) {
220 EnableLongIdlePeriod();
221 } else {
222 DCHECK(IdlePeriodState::IN_SHORT_IDLE_PERIOD ==
223 state_.idle_period_state());
224 EndIdlePeriod();
229 void IdleHelper::UpdateLongIdlePeriodStateAfterIdleTask() {
230 helper_->CheckOnValidThread();
231 DCHECK(IsInLongIdlePeriod(state_.idle_period_state()));
232 TRACE_EVENT0(disabled_by_default_tracing_category_,
233 "UpdateLongIdlePeriodStateAfterIdleTask");
234 TaskQueueManager::QueueState queue_state =
235 helper_->GetQueueState(idle_queue_index_);
236 if (queue_state == TaskQueueManager::QueueState::EMPTY) {
237 // If there are no more idle tasks then pause long idle period ticks until a
238 // new idle task is posted.
239 state_.UpdateState(IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED,
240 state_.idle_period_deadline(), base::TimeTicks());
241 } else if (queue_state == TaskQueueManager::QueueState::NEEDS_PUMPING) {
242 // If there is still idle work to do then just start the next idle period.
243 base::TimeDelta next_long_idle_period_delay;
244 if (state_.idle_period_state() ==
245 IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE) {
246 // If we are in a max deadline long idle period then start the next
247 // idle period immediately.
248 next_long_idle_period_delay = base::TimeDelta();
249 } else {
250 // Otherwise ensure that we kick the scheduler at the right time to
251 // initiate the next idle period.
252 next_long_idle_period_delay = std::max(
253 base::TimeDelta(), state_.idle_period_deadline() - helper_->Now());
255 if (next_long_idle_period_delay == base::TimeDelta()) {
256 EnableLongIdlePeriod();
257 } else {
258 helper_->ControlTaskRunner()->PostDelayedTask(
259 FROM_HERE, enable_next_long_idle_period_closure_.callback(),
260 next_long_idle_period_delay);
265 base::TimeTicks IdleHelper::CurrentIdleTaskDeadline() const {
266 helper_->CheckOnValidThread();
267 return state_.idle_period_deadline();
270 void IdleHelper::OnIdleTaskPosted() {
271 TRACE_EVENT0(disabled_by_default_tracing_category_, "OnIdleTaskPosted");
272 if (idle_task_runner_->RunsTasksOnCurrentThread()) {
273 OnIdleTaskPostedOnMainThread();
274 } else {
275 helper_->ControlTaskRunner()->PostTask(
276 FROM_HERE, on_idle_task_posted_closure_.callback());
280 void IdleHelper::OnIdleTaskPostedOnMainThread() {
281 TRACE_EVENT0(disabled_by_default_tracing_category_,
282 "OnIdleTaskPostedOnMainThread");
283 if (state_.idle_period_state() ==
284 IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED) {
285 // Restart long idle period ticks.
286 helper_->ControlTaskRunner()->PostTask(
287 FROM_HERE, enable_next_long_idle_period_closure_.callback());
291 base::TimeTicks IdleHelper::WillProcessIdleTask() {
292 helper_->CheckOnValidThread();
293 DCHECK(IsInIdlePeriod(state_.idle_period_state()));
295 state_.TraceIdleIdleTaskStart();
296 return CurrentIdleTaskDeadline();
299 void IdleHelper::DidProcessIdleTask() {
300 helper_->CheckOnValidThread();
301 state_.TraceIdleIdleTaskEnd();
302 if (IsInLongIdlePeriod(state_.idle_period_state())) {
303 UpdateLongIdlePeriodStateAfterIdleTask();
307 // static
308 bool IdleHelper::IsInIdlePeriod(IdlePeriodState state) {
309 return state != IdlePeriodState::NOT_IN_IDLE_PERIOD;
312 // static
313 bool IdleHelper::IsInLongIdlePeriod(IdlePeriodState state) {
314 return state == IdlePeriodState::IN_LONG_IDLE_PERIOD ||
315 state == IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE ||
316 state == IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED;
319 bool IdleHelper::CanExceedIdleDeadlineIfRequired() const {
320 TRACE_EVENT0(disabled_by_default_tracing_category_,
321 "CanExceedIdleDeadlineIfRequired");
322 helper_->CheckOnValidThread();
323 return state_.idle_period_state() ==
324 IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE;
327 IdleHelper::IdlePeriodState IdleHelper::SchedulerIdlePeriodState() const {
328 return state_.idle_period_state();
331 IdleHelper::State::State(SchedulerHelper* helper,
332 Delegate* delegate,
333 const char* tracing_category,
334 const char* disabled_by_default_tracing_category,
335 const char* idle_period_tracing_name)
336 : helper_(helper),
337 delegate_(delegate),
338 idle_period_state_(IdlePeriodState::NOT_IN_IDLE_PERIOD),
339 nestable_events_started_(false),
340 tracing_category_(tracing_category),
341 disabled_by_default_tracing_category_(
342 disabled_by_default_tracing_category),
343 idle_period_tracing_name_(idle_period_tracing_name) {
346 IdleHelper::State::~State() {
349 IdleHelper::IdlePeriodState IdleHelper::State::idle_period_state() const {
350 helper_->CheckOnValidThread();
351 return idle_period_state_;
354 base::TimeTicks IdleHelper::State::idle_period_deadline() const {
355 helper_->CheckOnValidThread();
356 return idle_period_deadline_;
359 void IdleHelper::State::UpdateState(IdlePeriodState new_state,
360 base::TimeTicks new_deadline,
361 base::TimeTicks optional_now) {
362 IdlePeriodState old_idle_period_state = idle_period_state_;
364 helper_->CheckOnValidThread();
365 if (new_state == idle_period_state_) {
366 DCHECK_EQ(new_deadline, idle_period_deadline_);
367 return;
370 bool is_tracing;
371 TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
372 if (is_tracing) {
373 base::TimeTicks now(optional_now.is_null() ? helper_->Now() : optional_now);
374 TraceEventIdlePeriodStateChange(new_state, new_deadline, now);
375 idle_period_deadline_for_tracing_ =
376 base::TraceTicks::Now() + (new_deadline - now);
379 idle_period_state_ = new_state;
380 idle_period_deadline_ = new_deadline;
382 // Inform the delegate if we are starting or ending an idle period.
383 if (IsInIdlePeriod(new_state) && !IsInIdlePeriod(old_idle_period_state)) {
384 delegate_->OnIdlePeriodStarted();
385 } else if (!IsInIdlePeriod(new_state) &&
386 IsInIdlePeriod(old_idle_period_state)) {
387 delegate_->OnIdlePeriodEnded();
391 void IdleHelper::State::TraceIdleIdleTaskStart() {
392 helper_->CheckOnValidThread();
394 bool is_tracing;
395 TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
396 if (is_tracing && nestable_events_started_) {
397 last_idle_task_trace_time_ = base::TraceTicks::Now();
398 TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
399 tracing_category_, "RunningIdleTask", this,
400 last_idle_task_trace_time_.ToInternalValue());
404 void IdleHelper::State::TraceIdleIdleTaskEnd() {
405 helper_->CheckOnValidThread();
407 bool is_tracing;
408 TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
409 if (is_tracing && nestable_events_started_) {
410 if (!idle_period_deadline_for_tracing_.is_null() &&
411 base::TraceTicks::Now() > idle_period_deadline_for_tracing_) {
412 TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
413 tracing_category_, "DeadlineOverrun", this,
414 std::max(idle_period_deadline_for_tracing_,
415 last_idle_task_trace_time_).ToInternalValue());
416 TRACE_EVENT_NESTABLE_ASYNC_END0(tracing_category_, "DeadlineOverrun",
417 this);
419 TRACE_EVENT_NESTABLE_ASYNC_END0(tracing_category_, "RunningIdleTask", this);
423 void IdleHelper::State::TraceEventIdlePeriodStateChange(
424 IdlePeriodState new_state,
425 base::TimeTicks new_deadline,
426 base::TimeTicks now) {
427 TRACE_EVENT2(disabled_by_default_tracing_category_, "SetIdlePeriodState",
428 "old_state",
429 IdleHelper::IdlePeriodStateToString(idle_period_state_),
430 "new_state", IdleHelper::IdlePeriodStateToString(new_state));
431 if (nestable_events_started_) {
432 // End async tracing events for the state we are leaving.
433 if (idle_period_state_ == IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED) {
434 TRACE_EVENT_NESTABLE_ASYNC_END0(tracing_category_, "LongIdlePeriodPaused",
435 this);
437 if (IsInLongIdlePeriod(idle_period_state_) &&
438 !IsInLongIdlePeriod(new_state)) {
439 TRACE_EVENT_NESTABLE_ASYNC_END0(tracing_category_, "LongIdlePeriod",
440 this);
442 if (idle_period_state_ == IdlePeriodState::IN_SHORT_IDLE_PERIOD) {
443 TRACE_EVENT_NESTABLE_ASYNC_END0(tracing_category_, "ShortIdlePeriod",
444 this);
446 if (IsInIdlePeriod(idle_period_state_) && !IsInIdlePeriod(new_state)) {
447 TRACE_EVENT_NESTABLE_ASYNC_END0(tracing_category_,
448 idle_period_tracing_name_, this);
449 nestable_events_started_ = false;
453 // Start async tracing events for the state we are entering.
454 if (IsInIdlePeriod(new_state) && !IsInIdlePeriod(idle_period_state_)) {
455 nestable_events_started_ = true;
456 TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
457 tracing_category_, idle_period_tracing_name_, this,
458 "idle_period_length_ms", (new_deadline - now).ToInternalValue());
460 if (new_state == IdlePeriodState::IN_SHORT_IDLE_PERIOD) {
461 TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(tracing_category_, "ShortIdlePeriod",
462 this);
464 if (IsInLongIdlePeriod(new_state) &&
465 !IsInLongIdlePeriod(idle_period_state_)) {
466 TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(tracing_category_, "LongIdlePeriod",
467 this);
469 if (new_state == IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED) {
470 TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(tracing_category_, "LongIdlePeriodPaused",
471 this);
475 // static
476 const char* IdleHelper::IdlePeriodStateToString(
477 IdlePeriodState idle_period_state) {
478 switch (idle_period_state) {
479 case IdlePeriodState::NOT_IN_IDLE_PERIOD:
480 return "not_in_idle_period";
481 case IdlePeriodState::IN_SHORT_IDLE_PERIOD:
482 return "in_short_idle_period";
483 case IdlePeriodState::IN_LONG_IDLE_PERIOD:
484 return "in_long_idle_period";
485 case IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE:
486 return "in_long_idle_period_with_max_deadline";
487 case IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED:
488 return "in_long_idle_period_paused";
489 default:
490 NOTREACHED();
491 return nullptr;
495 } // namespace scheduler