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

Side by Side Diff: components/scheduler/child/idle_helper.cc

Issue 2118903002: scheduler: Move the Blink scheduler into Blink (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Another GYP fix Created 4 years, 5 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 "components/scheduler/child/idle_helper.h"
6
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/base/real_time_domain.h"
11 #include "components/scheduler/base/task_queue.h"
12 #include "components/scheduler/base/task_queue_manager.h"
13 #include "components/scheduler/child/scheduler_helper.h"
14 #include "components/scheduler/child/scheduler_tqm_delegate.h"
15
16 namespace scheduler {
17
18 IdleHelper::IdleHelper(
19 SchedulerHelper* helper,
20 Delegate* delegate,
21 const char* tracing_category,
22 const char* disabled_by_default_tracing_category,
23 const char* idle_period_tracing_name,
24 base::TimeDelta required_quiescence_duration_before_long_idle_period)
25 : helper_(helper),
26 delegate_(delegate),
27 idle_queue_(
28 helper_->NewTaskQueue(TaskQueue::Spec("idle_tq").SetPumpPolicy(
29 TaskQueue::PumpPolicy::MANUAL))),
30 state_(helper,
31 delegate,
32 tracing_category,
33 disabled_by_default_tracing_category,
34 idle_period_tracing_name),
35 required_quiescence_duration_before_long_idle_period_(
36 required_quiescence_duration_before_long_idle_period),
37 disabled_by_default_tracing_category_(
38 disabled_by_default_tracing_category),
39 weak_factory_(this) {
40 weak_idle_helper_ptr_ = weak_factory_.GetWeakPtr();
41 enable_next_long_idle_period_closure_.Reset(
42 base::Bind(&IdleHelper::EnableLongIdlePeriod, weak_idle_helper_ptr_));
43 on_idle_task_posted_closure_.Reset(base::Bind(
44 &IdleHelper::OnIdleTaskPostedOnMainThread, weak_idle_helper_ptr_));
45
46 idle_task_runner_ = make_scoped_refptr(new SingleThreadIdleTaskRunner(
47 idle_queue_, helper_->ControlAfterWakeUpTaskRunner(), this,
48 tracing_category));
49
50 idle_queue_->SetQueueEnabled(false);
51 idle_queue_->SetQueuePriority(TaskQueue::BEST_EFFORT_PRIORITY);
52
53 helper_->AddTaskObserver(this);
54 }
55
56 IdleHelper::~IdleHelper() {
57 helper_->RemoveTaskObserver(this);
58 }
59
60 IdleHelper::Delegate::Delegate() {
61 }
62
63 IdleHelper::Delegate::~Delegate() {
64 }
65
66 scoped_refptr<SingleThreadIdleTaskRunner> IdleHelper::IdleTaskRunner() {
67 helper_->CheckOnValidThread();
68 return idle_task_runner_;
69 }
70
71 IdleHelper::IdlePeriodState IdleHelper::ComputeNewLongIdlePeriodState(
72 const base::TimeTicks now,
73 base::TimeDelta* next_long_idle_period_delay_out) {
74 helper_->CheckOnValidThread();
75
76 if (!delegate_->CanEnterLongIdlePeriod(now,
77 next_long_idle_period_delay_out)) {
78 return IdlePeriodState::NOT_IN_IDLE_PERIOD;
79 }
80
81 base::TimeTicks next_pending_delayed_task;
82 base::TimeDelta max_long_idle_period_duration =
83 base::TimeDelta::FromMilliseconds(kMaximumIdlePeriodMillis);
84 base::TimeDelta long_idle_period_duration;
85 if (helper_->real_time_domain()->NextScheduledRunTime(
86 &next_pending_delayed_task)) {
87 // Limit the idle period duration to be before the next pending task.
88 long_idle_period_duration = std::min(next_pending_delayed_task - now,
89 max_long_idle_period_duration);
90 } else {
91 long_idle_period_duration = max_long_idle_period_duration;
92 }
93
94 if (long_idle_period_duration >=
95 base::TimeDelta::FromMilliseconds(kMinimumIdlePeriodDurationMillis)) {
96 *next_long_idle_period_delay_out = long_idle_period_duration;
97 if (!idle_queue_->HasPendingImmediateWork()) {
98 return IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED;
99 } else if (long_idle_period_duration == max_long_idle_period_duration) {
100 return IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE;
101 } else {
102 return IdlePeriodState::IN_LONG_IDLE_PERIOD;
103 }
104 } else {
105 // If we can't start the idle period yet then try again after wakeup.
106 *next_long_idle_period_delay_out = base::TimeDelta::FromMilliseconds(
107 kRetryEnableLongIdlePeriodDelayMillis);
108 return IdlePeriodState::NOT_IN_IDLE_PERIOD;
109 }
110 }
111
112 bool IdleHelper::ShouldWaitForQuiescence() {
113 helper_->CheckOnValidThread();
114
115 if (helper_->IsShutdown())
116 return false;
117
118 if (required_quiescence_duration_before_long_idle_period_ ==
119 base::TimeDelta())
120 return false;
121
122 bool system_is_quiescent = helper_->GetAndClearSystemIsQuiescentBit();
123 TRACE_EVENT1(disabled_by_default_tracing_category_, "ShouldWaitForQuiescence",
124 "system_is_quiescent", system_is_quiescent);
125 return !system_is_quiescent;
126 }
127
128 void IdleHelper::EnableLongIdlePeriod() {
129 TRACE_EVENT0(disabled_by_default_tracing_category_, "EnableLongIdlePeriod");
130 helper_->CheckOnValidThread();
131 if (helper_->IsShutdown())
132 return;
133
134 // End any previous idle period.
135 EndIdlePeriod();
136
137 if (ShouldWaitForQuiescence()) {
138 helper_->ControlTaskRunner()->PostDelayedTask(
139 FROM_HERE, enable_next_long_idle_period_closure_.callback(),
140 required_quiescence_duration_before_long_idle_period_);
141 delegate_->IsNotQuiescent();
142 return;
143 }
144
145 base::TimeTicks now(helper_->scheduler_tqm_delegate()->NowTicks());
146 base::TimeDelta next_long_idle_period_delay;
147 IdlePeriodState new_idle_period_state =
148 ComputeNewLongIdlePeriodState(now, &next_long_idle_period_delay);
149 if (IsInIdlePeriod(new_idle_period_state)) {
150 StartIdlePeriod(new_idle_period_state, now,
151 now + next_long_idle_period_delay);
152 } else {
153 // Otherwise wait for the next long idle period delay before trying again.
154 helper_->ControlTaskRunner()->PostDelayedTask(
155 FROM_HERE, enable_next_long_idle_period_closure_.callback(),
156 next_long_idle_period_delay);
157 }
158 }
159
160 void IdleHelper::StartIdlePeriod(IdlePeriodState new_state,
161 base::TimeTicks now,
162 base::TimeTicks idle_period_deadline) {
163 DCHECK_GT(idle_period_deadline, now);
164 helper_->CheckOnValidThread();
165 DCHECK(IsInIdlePeriod(new_state));
166
167 base::TimeDelta idle_period_duration(idle_period_deadline - now);
168 if (idle_period_duration <
169 base::TimeDelta::FromMilliseconds(kMinimumIdlePeriodDurationMillis)) {
170 TRACE_EVENT1(disabled_by_default_tracing_category_,
171 "NotStartingIdlePeriodBecauseDeadlineIsTooClose",
172 "idle_period_duration_ms",
173 idle_period_duration.InMillisecondsF());
174 return;
175 }
176
177 TRACE_EVENT0(disabled_by_default_tracing_category_, "StartIdlePeriod");
178 idle_queue_->SetQueueEnabled(true);
179 idle_queue_->PumpQueue(true);
180
181 state_.UpdateState(new_state, idle_period_deadline, now);
182 }
183
184 void IdleHelper::EndIdlePeriod() {
185 helper_->CheckOnValidThread();
186 TRACE_EVENT0(disabled_by_default_tracing_category_, "EndIdlePeriod");
187
188 enable_next_long_idle_period_closure_.Cancel();
189 on_idle_task_posted_closure_.Cancel();
190
191 // If we weren't already within an idle period then early-out.
192 if (!IsInIdlePeriod(state_.idle_period_state()))
193 return;
194
195 idle_queue_->SetQueueEnabled(false);
196 state_.UpdateState(IdlePeriodState::NOT_IN_IDLE_PERIOD, base::TimeTicks(),
197 base::TimeTicks());
198 }
199
200 void IdleHelper::WillProcessTask(const base::PendingTask& pending_task) {
201 }
202
203 void IdleHelper::DidProcessTask(const base::PendingTask& pending_task) {
204 helper_->CheckOnValidThread();
205 TRACE_EVENT0(disabled_by_default_tracing_category_, "DidProcessTask");
206 if (IsInIdlePeriod(state_.idle_period_state()) &&
207 state_.idle_period_state() !=
208 IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED &&
209 helper_->scheduler_tqm_delegate()->NowTicks() >=
210 state_.idle_period_deadline()) {
211 // If the idle period deadline has now been reached, either end the idle
212 // period or trigger a new long-idle period.
213 if (IsInLongIdlePeriod(state_.idle_period_state())) {
214 EnableLongIdlePeriod();
215 } else {
216 DCHECK(IdlePeriodState::IN_SHORT_IDLE_PERIOD ==
217 state_.idle_period_state());
218 EndIdlePeriod();
219 }
220 }
221 }
222
223 void IdleHelper::UpdateLongIdlePeriodStateAfterIdleTask() {
224 helper_->CheckOnValidThread();
225 DCHECK(IsInLongIdlePeriod(state_.idle_period_state()));
226 TRACE_EVENT0(disabled_by_default_tracing_category_,
227 "UpdateLongIdlePeriodStateAfterIdleTask");
228
229 if (!idle_queue_->HasPendingImmediateWork()) {
230 // If there are no more idle tasks then pause long idle period ticks until a
231 // new idle task is posted.
232 state_.UpdateState(IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED,
233 state_.idle_period_deadline(), base::TimeTicks());
234 } else if (idle_queue_->NeedsPumping()) {
235 // If there is still idle work to do then just start the next idle period.
236 base::TimeDelta next_long_idle_period_delay;
237 if (state_.idle_period_state() ==
238 IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE) {
239 // If we are in a max deadline long idle period then start the next
240 // idle period immediately.
241 next_long_idle_period_delay = base::TimeDelta();
242 } else {
243 // Otherwise ensure that we kick the scheduler at the right time to
244 // initiate the next idle period.
245 next_long_idle_period_delay = std::max(
246 base::TimeDelta(), state_.idle_period_deadline() -
247 helper_->scheduler_tqm_delegate()->NowTicks());
248 }
249 if (next_long_idle_period_delay.is_zero()) {
250 EnableLongIdlePeriod();
251 } else {
252 helper_->ControlTaskRunner()->PostDelayedTask(
253 FROM_HERE, enable_next_long_idle_period_closure_.callback(),
254 next_long_idle_period_delay);
255 }
256 }
257 }
258
259 base::TimeTicks IdleHelper::CurrentIdleTaskDeadline() const {
260 helper_->CheckOnValidThread();
261 return state_.idle_period_deadline();
262 }
263
264 void IdleHelper::OnIdleTaskPosted() {
265 TRACE_EVENT0(disabled_by_default_tracing_category_, "OnIdleTaskPosted");
266 if (idle_task_runner_->RunsTasksOnCurrentThread()) {
267 OnIdleTaskPostedOnMainThread();
268 } else {
269 helper_->ControlTaskRunner()->PostTask(
270 FROM_HERE, on_idle_task_posted_closure_.callback());
271 }
272 }
273
274 void IdleHelper::OnIdleTaskPostedOnMainThread() {
275 TRACE_EVENT0(disabled_by_default_tracing_category_,
276 "OnIdleTaskPostedOnMainThread");
277 if (state_.idle_period_state() ==
278 IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED) {
279 // Restart long idle period ticks.
280 helper_->ControlTaskRunner()->PostTask(
281 FROM_HERE, enable_next_long_idle_period_closure_.callback());
282 }
283 }
284
285 base::TimeTicks IdleHelper::WillProcessIdleTask() {
286 helper_->CheckOnValidThread();
287 state_.TraceIdleIdleTaskStart();
288 return CurrentIdleTaskDeadline();
289 }
290
291 void IdleHelper::DidProcessIdleTask() {
292 helper_->CheckOnValidThread();
293 state_.TraceIdleIdleTaskEnd();
294 if (IsInLongIdlePeriod(state_.idle_period_state())) {
295 UpdateLongIdlePeriodStateAfterIdleTask();
296 }
297 }
298
299 // static
300 bool IdleHelper::IsInIdlePeriod(IdlePeriodState state) {
301 return state != IdlePeriodState::NOT_IN_IDLE_PERIOD;
302 }
303
304 // static
305 bool IdleHelper::IsInLongIdlePeriod(IdlePeriodState state) {
306 return state == IdlePeriodState::IN_LONG_IDLE_PERIOD ||
307 state == IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE ||
308 state == IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED;
309 }
310
311 bool IdleHelper::CanExceedIdleDeadlineIfRequired() const {
312 TRACE_EVENT0(disabled_by_default_tracing_category_,
313 "CanExceedIdleDeadlineIfRequired");
314 helper_->CheckOnValidThread();
315 return state_.idle_period_state() ==
316 IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE;
317 }
318
319 IdleHelper::IdlePeriodState IdleHelper::SchedulerIdlePeriodState() const {
320 return state_.idle_period_state();
321 }
322
323 IdleHelper::State::State(SchedulerHelper* helper,
324 Delegate* delegate,
325 const char* tracing_category,
326 const char* disabled_by_default_tracing_category,
327 const char* idle_period_tracing_name)
328 : helper_(helper),
329 delegate_(delegate),
330 idle_period_state_(IdlePeriodState::NOT_IN_IDLE_PERIOD),
331 idle_period_trace_event_started_(false),
332 running_idle_task_for_tracing_(false),
333 tracing_category_(tracing_category),
334 disabled_by_default_tracing_category_(
335 disabled_by_default_tracing_category),
336 idle_period_tracing_name_(idle_period_tracing_name) {
337 }
338
339 IdleHelper::State::~State() {
340 }
341
342 IdleHelper::IdlePeriodState IdleHelper::State::idle_period_state() const {
343 helper_->CheckOnValidThread();
344 return idle_period_state_;
345 }
346
347 base::TimeTicks IdleHelper::State::idle_period_deadline() const {
348 helper_->CheckOnValidThread();
349 return idle_period_deadline_;
350 }
351
352 void IdleHelper::State::UpdateState(IdlePeriodState new_state,
353 base::TimeTicks new_deadline,
354 base::TimeTicks optional_now) {
355 IdlePeriodState old_idle_period_state = idle_period_state_;
356
357 helper_->CheckOnValidThread();
358 if (new_state == idle_period_state_) {
359 DCHECK_EQ(new_deadline, idle_period_deadline_);
360 return;
361 }
362
363 bool is_tracing;
364 TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
365 if (is_tracing) {
366 base::TimeTicks now(optional_now.is_null()
367 ? helper_->scheduler_tqm_delegate()->NowTicks()
368 : optional_now);
369 TraceEventIdlePeriodStateChange(
370 new_state, running_idle_task_for_tracing_, idle_period_deadline_, now);
371 }
372
373 idle_period_state_ = new_state;
374 idle_period_deadline_ = new_deadline;
375
376 // Inform the delegate if we are starting or ending an idle period.
377 if (IsInIdlePeriod(new_state) && !IsInIdlePeriod(old_idle_period_state)) {
378 delegate_->OnIdlePeriodStarted();
379 } else if (!IsInIdlePeriod(new_state) &&
380 IsInIdlePeriod(old_idle_period_state)) {
381 delegate_->OnIdlePeriodEnded();
382 }
383 }
384
385 void IdleHelper::State::TraceIdleIdleTaskStart() {
386 helper_->CheckOnValidThread();
387
388 bool is_tracing;
389 TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
390 if (is_tracing) {
391 TraceEventIdlePeriodStateChange(
392 idle_period_state_, true, idle_period_deadline_,
393 base::TimeTicks::Now());
394 }
395 }
396
397 void IdleHelper::State::TraceIdleIdleTaskEnd() {
398 helper_->CheckOnValidThread();
399
400 bool is_tracing;
401 TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
402 if (is_tracing) {
403 TraceEventIdlePeriodStateChange(
404 idle_period_state_, false, idle_period_deadline_,
405 base::TimeTicks::Now());
406 }
407 }
408
409 void IdleHelper::State::TraceEventIdlePeriodStateChange(
410 IdlePeriodState new_state,
411 bool new_running_idle_task,
412 base::TimeTicks new_deadline,
413 base::TimeTicks now) {
414 TRACE_EVENT2(disabled_by_default_tracing_category_, "SetIdlePeriodState",
415 "old_state",
416 IdleHelper::IdlePeriodStateToString(idle_period_state_),
417 "new_state", IdleHelper::IdlePeriodStateToString(new_state));
418
419 if (idle_period_trace_event_started_ && running_idle_task_for_tracing_ &&
420 !new_running_idle_task) {
421 running_idle_task_for_tracing_ = false;
422 if (!idle_period_deadline_.is_null() && now > idle_period_deadline_) {
423 TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(
424 tracing_category_, idle_period_tracing_name_, this,
425 "DeadlineOverrun",
426 std::max(idle_period_deadline_,
427 last_idle_task_trace_time_).ToInternalValue());
428 }
429 }
430
431 if (IsInIdlePeriod(new_state)) {
432 if (!idle_period_trace_event_started_) {
433 idle_period_trace_event_started_ = true;
434 TRACE_EVENT_ASYNC_BEGIN1(
435 tracing_category_, idle_period_tracing_name_, this,
436 "idle_period_length_ms", (new_deadline - now).ToInternalValue());
437 }
438
439 if (new_running_idle_task) {
440 last_idle_task_trace_time_ = now;
441 running_idle_task_for_tracing_ = true;
442 TRACE_EVENT_ASYNC_STEP_INTO0(
443 tracing_category_, idle_period_tracing_name_, this,
444 "RunningIdleTask");
445 } else if (new_state == IdlePeriodState::IN_SHORT_IDLE_PERIOD) {
446 TRACE_EVENT_ASYNC_STEP_INTO0(
447 tracing_category_, idle_period_tracing_name_, this,
448 "ShortIdlePeriod");
449 } else if (IsInLongIdlePeriod(new_state) &&
450 new_state != IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED) {
451 TRACE_EVENT_ASYNC_STEP_INTO0(
452 tracing_category_, idle_period_tracing_name_, this,
453 "LongIdlePeriod");
454 } else if (new_state == IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED) {
455 TRACE_EVENT_ASYNC_STEP_INTO0(
456 tracing_category_, idle_period_tracing_name_, this,
457 "LongIdlePeriodPaused");
458 }
459 } else if (idle_period_trace_event_started_) {
460 idle_period_trace_event_started_ = false;
461 TRACE_EVENT_ASYNC_END0(tracing_category_, idle_period_tracing_name_, this);
462 }
463 }
464
465 // static
466 const char* IdleHelper::IdlePeriodStateToString(
467 IdlePeriodState idle_period_state) {
468 switch (idle_period_state) {
469 case IdlePeriodState::NOT_IN_IDLE_PERIOD:
470 return "not_in_idle_period";
471 case IdlePeriodState::IN_SHORT_IDLE_PERIOD:
472 return "in_short_idle_period";
473 case IdlePeriodState::IN_LONG_IDLE_PERIOD:
474 return "in_long_idle_period";
475 case IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE:
476 return "in_long_idle_period_with_max_deadline";
477 case IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED:
478 return "in_long_idle_period_paused";
479 default:
480 NOTREACHED();
481 return nullptr;
482 }
483 }
484
485 } // namespace scheduler
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698