Add an UMA stat to be able to see if the User pods are show on start screen,
[chromium-blink-merge.git] / content / child / scheduler / scheduler_helper.cc
blob493335a5693df2eafe0c0c134df86469e290a242
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 "content/child/scheduler/scheduler_helper.h"
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"
12 namespace content {
14 SchedulerHelper::SchedulerHelper(
15 scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner,
16 SchedulerHelperDelegate* scheduler_helper_delegate,
17 const char* tracing_category,
18 const char* disabled_by_default_tracing_category,
19 size_t total_task_queue_count)
20 : task_queue_selector_(new PrioritizingTaskQueueSelector()),
21 task_queue_manager_(
22 new TaskQueueManager(total_task_queue_count,
23 main_task_runner,
24 task_queue_selector_.get(),
25 disabled_by_default_tracing_category)),
26 idle_period_state_(IdlePeriodState::NOT_IN_IDLE_PERIOD),
27 scheduler_helper_delegate_(scheduler_helper_delegate),
28 control_task_runner_(
29 task_queue_manager_->TaskRunnerForQueue(QueueId::CONTROL_TASK_QUEUE)),
30 control_task_after_wakeup_runner_(task_queue_manager_->TaskRunnerForQueue(
31 QueueId::CONTROL_TASK_AFTER_WAKEUP_QUEUE)),
32 default_task_runner_(
33 task_queue_manager_->TaskRunnerForQueue(QueueId::DEFAULT_TASK_QUEUE)),
34 tracing_category_(tracing_category),
35 disabled_by_default_tracing_category_(
36 disabled_by_default_tracing_category),
37 weak_factory_(this) {
38 DCHECK_GE(total_task_queue_count,
39 static_cast<size_t>(QueueId::TASK_QUEUE_COUNT));
40 weak_scheduler_ptr_ = weak_factory_.GetWeakPtr();
41 end_idle_period_closure_.Reset(
42 base::Bind(&SchedulerHelper::EndIdlePeriod, weak_scheduler_ptr_));
43 initiate_next_long_idle_period_closure_.Reset(base::Bind(
44 &SchedulerHelper::InitiateLongIdlePeriod, weak_scheduler_ptr_));
45 initiate_next_long_idle_period_after_wakeup_closure_.Reset(
46 base::Bind(&SchedulerHelper::InitiateLongIdlePeriodAfterWakeup,
47 weak_scheduler_ptr_));
49 idle_task_runner_ = make_scoped_refptr(new SingleThreadIdleTaskRunner(
50 task_queue_manager_->TaskRunnerForQueue(QueueId::IDLE_TASK_QUEUE),
51 control_task_after_wakeup_runner_,
52 base::Bind(&SchedulerHelper::CurrentIdleTaskDeadlineCallback,
53 weak_scheduler_ptr_),
54 tracing_category));
56 task_queue_selector_->SetQueuePriority(
57 QueueId::CONTROL_TASK_QUEUE,
58 PrioritizingTaskQueueSelector::CONTROL_PRIORITY);
60 task_queue_selector_->SetQueuePriority(
61 QueueId::CONTROL_TASK_AFTER_WAKEUP_QUEUE,
62 PrioritizingTaskQueueSelector::CONTROL_PRIORITY);
63 task_queue_manager_->SetPumpPolicy(
64 QueueId::CONTROL_TASK_AFTER_WAKEUP_QUEUE,
65 TaskQueueManager::PumpPolicy::AFTER_WAKEUP);
67 task_queue_selector_->DisableQueue(QueueId::IDLE_TASK_QUEUE);
68 task_queue_manager_->SetPumpPolicy(QueueId::IDLE_TASK_QUEUE,
69 TaskQueueManager::PumpPolicy::MANUAL);
71 for (size_t i = 0; i < TASK_QUEUE_COUNT; i++) {
72 task_queue_manager_->SetQueueName(
73 i, TaskQueueIdToString(static_cast<QueueId>(i)));
76 // TODO(skyostil): Increase this to 4 (crbug.com/444764).
77 task_queue_manager_->SetWorkBatchSize(1);
80 SchedulerHelper::~SchedulerHelper() {
83 SchedulerHelper::SchedulerHelperDelegate::SchedulerHelperDelegate() {
86 SchedulerHelper::SchedulerHelperDelegate::~SchedulerHelperDelegate() {
89 void SchedulerHelper::Shutdown() {
90 CheckOnValidThread();
91 task_queue_manager_.reset();
94 scoped_refptr<base::SingleThreadTaskRunner>
95 SchedulerHelper::DefaultTaskRunner() {
96 CheckOnValidThread();
97 return default_task_runner_;
100 scoped_refptr<SingleThreadIdleTaskRunner> SchedulerHelper::IdleTaskRunner() {
101 CheckOnValidThread();
102 return idle_task_runner_;
105 scoped_refptr<base::SingleThreadTaskRunner>
106 SchedulerHelper::ControlTaskRunner() {
107 return control_task_runner_;
110 void SchedulerHelper::CurrentIdleTaskDeadlineCallback(
111 base::TimeTicks* deadline_out) const {
112 CheckOnValidThread();
113 *deadline_out = idle_period_deadline_;
116 SchedulerHelper::IdlePeriodState SchedulerHelper::ComputeNewLongIdlePeriodState(
117 const base::TimeTicks now,
118 base::TimeDelta* next_long_idle_period_delay_out) {
119 CheckOnValidThread();
121 if (!scheduler_helper_delegate_->CanEnterLongIdlePeriod(
122 now, next_long_idle_period_delay_out)) {
123 return IdlePeriodState::NOT_IN_IDLE_PERIOD;
126 base::TimeTicks next_pending_delayed_task =
127 task_queue_manager_->NextPendingDelayedTaskRunTime();
128 base::TimeDelta max_long_idle_period_duration =
129 base::TimeDelta::FromMilliseconds(kMaximumIdlePeriodMillis);
130 base::TimeDelta long_idle_period_duration;
131 if (next_pending_delayed_task.is_null()) {
132 long_idle_period_duration = max_long_idle_period_duration;
133 } else {
134 // Limit the idle period duration to be before the next pending task.
135 long_idle_period_duration = std::min(next_pending_delayed_task - now,
136 max_long_idle_period_duration);
139 if (long_idle_period_duration > base::TimeDelta()) {
140 *next_long_idle_period_delay_out = long_idle_period_duration;
141 return long_idle_period_duration == max_long_idle_period_duration
142 ? IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE
143 : IdlePeriodState::IN_LONG_IDLE_PERIOD;
144 } else {
145 // If we can't start the idle period yet then try again after wakeup.
146 *next_long_idle_period_delay_out = base::TimeDelta::FromMilliseconds(
147 kRetryInitiateLongIdlePeriodDelayMillis);
148 return IdlePeriodState::NOT_IN_IDLE_PERIOD;
152 void SchedulerHelper::InitiateLongIdlePeriod() {
153 TRACE_EVENT0(disabled_by_default_tracing_category_, "InitiateLongIdlePeriod");
154 CheckOnValidThread();
156 // End any previous idle period.
157 EndIdlePeriod();
159 base::TimeTicks now(Now());
160 base::TimeDelta next_long_idle_period_delay;
161 IdlePeriodState new_idle_period_state =
162 ComputeNewLongIdlePeriodState(now, &next_long_idle_period_delay);
163 if (IsInIdlePeriod(new_idle_period_state)) {
164 StartIdlePeriod(new_idle_period_state, now,
165 now + next_long_idle_period_delay,
166 false);
169 if (task_queue_manager_->IsQueueEmpty(QueueId::IDLE_TASK_QUEUE)) {
170 // If there are no current idle tasks then post the call to initiate the
171 // next idle for execution after wakeup (at which point after-wakeup idle
172 // tasks might be eligible to run or more idle tasks posted).
173 control_task_after_wakeup_runner_->PostDelayedTask(
174 FROM_HERE,
175 initiate_next_long_idle_period_after_wakeup_closure_.callback(),
176 next_long_idle_period_delay);
177 } else {
178 // Otherwise post on the normal control task queue.
179 control_task_runner_->PostDelayedTask(
180 FROM_HERE, initiate_next_long_idle_period_closure_.callback(),
181 next_long_idle_period_delay);
185 void SchedulerHelper::InitiateLongIdlePeriodAfterWakeup() {
186 TRACE_EVENT0(disabled_by_default_tracing_category_,
187 "InitiateLongIdlePeriodAfterWakeup");
188 CheckOnValidThread();
190 if (IsInIdlePeriod(idle_period_state_)) {
191 // Since we were asleep until now, end the async idle period trace event at
192 // the time when it would have ended were we awake.
193 TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0(
194 tracing_category_, "RendererSchedulerIdlePeriod", this,
195 std::min(idle_period_deadline_, Now()).ToInternalValue());
196 idle_period_state_ = IdlePeriodState::ENDING_LONG_IDLE_PERIOD;
197 EndIdlePeriod();
200 // Post a task to initiate the next long idle period rather than calling it
201 // directly to allow all pending PostIdleTaskAfterWakeup tasks to get enqueued
202 // on the idle task queue before the next idle period starts so they are
203 // eligible to be run during the new idle period.
204 control_task_runner_->PostTask(
205 FROM_HERE, initiate_next_long_idle_period_closure_.callback());
208 void SchedulerHelper::StartIdlePeriod(IdlePeriodState new_state,
209 base::TimeTicks now,
210 base::TimeTicks idle_period_deadline,
211 bool post_end_idle_period) {
212 DCHECK_GT(idle_period_deadline, now);
213 TRACE_EVENT_ASYNC_BEGIN0(tracing_category_, "RendererSchedulerIdlePeriod",
214 this);
215 CheckOnValidThread();
216 DCHECK(IsInIdlePeriod(new_state));
218 task_queue_selector_->EnableQueue(
219 QueueId::IDLE_TASK_QUEUE,
220 PrioritizingTaskQueueSelector::BEST_EFFORT_PRIORITY);
221 task_queue_manager_->PumpQueue(QueueId::IDLE_TASK_QUEUE);
222 idle_period_state_ = new_state;
224 idle_period_deadline_ = idle_period_deadline;
225 if (post_end_idle_period) {
226 control_task_runner_->PostDelayedTask(
227 FROM_HERE,
228 end_idle_period_closure_.callback(),
229 idle_period_deadline_ - now);
233 void SchedulerHelper::EndIdlePeriod() {
234 CheckOnValidThread();
236 end_idle_period_closure_.Cancel();
237 initiate_next_long_idle_period_closure_.Cancel();
238 initiate_next_long_idle_period_after_wakeup_closure_.Cancel();
240 // If we weren't already within an idle period then early-out.
241 if (!IsInIdlePeriod(idle_period_state_))
242 return;
244 // If we are in the ENDING_LONG_IDLE_PERIOD state we have already logged the
245 // trace event.
246 if (idle_period_state_ != IdlePeriodState::ENDING_LONG_IDLE_PERIOD) {
247 bool is_tracing;
248 TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
249 if (is_tracing && !idle_period_deadline_.is_null() &&
250 base::TimeTicks::Now() > idle_period_deadline_) {
251 TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(
252 tracing_category_, "RendererSchedulerIdlePeriod", this,
253 "DeadlineOverrun", idle_period_deadline_.ToInternalValue());
255 TRACE_EVENT_ASYNC_END0(tracing_category_, "RendererSchedulerIdlePeriod",
256 this);
259 task_queue_selector_->DisableQueue(QueueId::IDLE_TASK_QUEUE);
260 idle_period_state_ = IdlePeriodState::NOT_IN_IDLE_PERIOD;
261 idle_period_deadline_ = base::TimeTicks();
264 // static
265 bool SchedulerHelper::IsInIdlePeriod(IdlePeriodState state) {
266 return state != IdlePeriodState::NOT_IN_IDLE_PERIOD;
269 bool SchedulerHelper::CanExceedIdleDeadlineIfRequired() const {
270 TRACE_EVENT0(tracing_category_, "CanExceedIdleDeadlineIfRequired");
271 CheckOnValidThread();
272 return idle_period_state_ ==
273 IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE;
276 void SchedulerHelper::SetTimeSourceForTesting(
277 scoped_refptr<cc::TestNowSource> time_source) {
278 CheckOnValidThread();
279 time_source_ = time_source;
280 task_queue_manager_->SetTimeSourceForTesting(time_source);
283 void SchedulerHelper::SetWorkBatchSizeForTesting(size_t work_batch_size) {
284 CheckOnValidThread();
285 task_queue_manager_->SetWorkBatchSize(work_batch_size);
288 base::TimeTicks SchedulerHelper::Now() const {
289 return UNLIKELY(time_source_) ? time_source_->Now() : base::TimeTicks::Now();
292 SchedulerHelper::IdlePeriodState
293 SchedulerHelper::SchedulerIdlePeriodState() const {
294 return idle_period_state_;
297 PrioritizingTaskQueueSelector*
298 SchedulerHelper::SchedulerTaskQueueSelector() const {
299 return task_queue_selector_.get();
302 TaskQueueManager* SchedulerHelper::SchedulerTaskQueueManager() const {
303 return task_queue_manager_.get();
306 // static
307 const char* SchedulerHelper::TaskQueueIdToString(QueueId queue_id) {
308 switch (queue_id) {
309 case DEFAULT_TASK_QUEUE:
310 return "default_tq";
311 case IDLE_TASK_QUEUE:
312 return "idle_tq";
313 case CONTROL_TASK_QUEUE:
314 return "control_tq";
315 case CONTROL_TASK_AFTER_WAKEUP_QUEUE:
316 return "control_after_wakeup_tq";
317 default:
318 NOTREACHED();
319 return nullptr;
323 // static
324 const char* SchedulerHelper::IdlePeriodStateToString(
325 IdlePeriodState idle_period_state) {
326 switch (idle_period_state) {
327 case IdlePeriodState::NOT_IN_IDLE_PERIOD:
328 return "not_in_idle_period";
329 case IdlePeriodState::IN_SHORT_IDLE_PERIOD:
330 return "in_short_idle_period";
331 case IdlePeriodState::IN_LONG_IDLE_PERIOD:
332 return "in_long_idle_period";
333 case IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE:
334 return "in_long_idle_period_with_max_deadline";
335 case IdlePeriodState::ENDING_LONG_IDLE_PERIOD:
336 return "ending_long_idle_period";
337 default:
338 NOTREACHED();
339 return nullptr;
343 void SchedulerHelper::AddTaskObserver(
344 base::MessageLoop::TaskObserver* task_observer) {
345 CheckOnValidThread();
346 if (task_queue_manager_)
347 task_queue_manager_->AddTaskObserver(task_observer);
350 void SchedulerHelper::RemoveTaskObserver(
351 base::MessageLoop::TaskObserver* task_observer) {
352 CheckOnValidThread();
353 if (task_queue_manager_)
354 task_queue_manager_->RemoveTaskObserver(task_observer);
357 } // namespace content