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

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: Rebased Created 4 years, 4 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 LazyNow lazy_now(now);
180 idle_queue_->PumpQueue(&lazy_now, true);
181
182 state_.UpdateState(new_state, idle_period_deadline, now);
183 }
184
185 void IdleHelper::EndIdlePeriod() {
186 helper_->CheckOnValidThread();
187 TRACE_EVENT0(disabled_by_default_tracing_category_, "EndIdlePeriod");
188
189 enable_next_long_idle_period_closure_.Cancel();
190 on_idle_task_posted_closure_.Cancel();
191
192 // If we weren't already within an idle period then early-out.
193 if (!IsInIdlePeriod(state_.idle_period_state()))
194 return;
195
196 idle_queue_->SetQueueEnabled(false);
197 state_.UpdateState(IdlePeriodState::NOT_IN_IDLE_PERIOD, base::TimeTicks(),
198 base::TimeTicks());
199 }
200
201 void IdleHelper::WillProcessTask(const base::PendingTask& pending_task) {
202 }
203
204 void IdleHelper::DidProcessTask(const base::PendingTask& pending_task) {
205 helper_->CheckOnValidThread();
206 TRACE_EVENT0(disabled_by_default_tracing_category_, "DidProcessTask");
207 if (IsInIdlePeriod(state_.idle_period_state()) &&
208 state_.idle_period_state() !=
209 IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED &&
210 helper_->scheduler_tqm_delegate()->NowTicks() >=
211 state_.idle_period_deadline()) {
212 // If the idle period deadline has now been reached, either end the idle
213 // period or trigger a new long-idle period.
214 if (IsInLongIdlePeriod(state_.idle_period_state())) {
215 EnableLongIdlePeriod();
216 } else {
217 DCHECK(IdlePeriodState::IN_SHORT_IDLE_PERIOD ==
218 state_.idle_period_state());
219 EndIdlePeriod();
220 }
221 }
222 }
223
224 void IdleHelper::UpdateLongIdlePeriodStateAfterIdleTask() {
225 helper_->CheckOnValidThread();
226 DCHECK(IsInLongIdlePeriod(state_.idle_period_state()));
227 TRACE_EVENT0(disabled_by_default_tracing_category_,
228 "UpdateLongIdlePeriodStateAfterIdleTask");
229
230 if (!idle_queue_->HasPendingImmediateWork()) {
231 // If there are no more idle tasks then pause long idle period ticks until a
232 // new idle task is posted.
233 state_.UpdateState(IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED,
234 state_.idle_period_deadline(), base::TimeTicks());
235 } else if (idle_queue_->NeedsPumping()) {
236 // If there is still idle work to do then just start the next idle period.
237 base::TimeDelta next_long_idle_period_delay;
238 if (state_.idle_period_state() ==
239 IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE) {
240 // If we are in a max deadline long idle period then start the next
241 // idle period immediately.
242 next_long_idle_period_delay = base::TimeDelta();
243 } else {
244 // Otherwise ensure that we kick the scheduler at the right time to
245 // initiate the next idle period.
246 next_long_idle_period_delay = std::max(
247 base::TimeDelta(), state_.idle_period_deadline() -
248 helper_->scheduler_tqm_delegate()->NowTicks());
249 }
250 if (next_long_idle_period_delay.is_zero()) {
251 EnableLongIdlePeriod();
252 } else {
253 helper_->ControlTaskRunner()->PostDelayedTask(
254 FROM_HERE, enable_next_long_idle_period_closure_.callback(),
255 next_long_idle_period_delay);
256 }
257 }
258 }
259
260 base::TimeTicks IdleHelper::CurrentIdleTaskDeadline() const {
261 helper_->CheckOnValidThread();
262 return state_.idle_period_deadline();
263 }
264
265 void IdleHelper::OnIdleTaskPosted() {
266 TRACE_EVENT0(disabled_by_default_tracing_category_, "OnIdleTaskPosted");
267 if (idle_task_runner_->RunsTasksOnCurrentThread()) {
268 OnIdleTaskPostedOnMainThread();
269 } else {
270 helper_->ControlTaskRunner()->PostTask(
271 FROM_HERE, on_idle_task_posted_closure_.callback());
272 }
273 }
274
275 void IdleHelper::OnIdleTaskPostedOnMainThread() {
276 TRACE_EVENT0(disabled_by_default_tracing_category_,
277 "OnIdleTaskPostedOnMainThread");
278 if (state_.idle_period_state() ==
279 IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED) {
280 // Restart long idle period ticks.
281 helper_->ControlTaskRunner()->PostTask(
282 FROM_HERE, enable_next_long_idle_period_closure_.callback());
283 }
284 }
285
286 base::TimeTicks IdleHelper::WillProcessIdleTask() {
287 helper_->CheckOnValidThread();
288 state_.TraceIdleIdleTaskStart();
289 return CurrentIdleTaskDeadline();
290 }
291
292 void IdleHelper::DidProcessIdleTask() {
293 helper_->CheckOnValidThread();
294 state_.TraceIdleIdleTaskEnd();
295 if (IsInLongIdlePeriod(state_.idle_period_state())) {
296 UpdateLongIdlePeriodStateAfterIdleTask();
297 }
298 }
299
300 // static
301 bool IdleHelper::IsInIdlePeriod(IdlePeriodState state) {
302 return state != IdlePeriodState::NOT_IN_IDLE_PERIOD;
303 }
304
305 // static
306 bool IdleHelper::IsInLongIdlePeriod(IdlePeriodState state) {
307 return state == IdlePeriodState::IN_LONG_IDLE_PERIOD ||
308 state == IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE ||
309 state == IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED;
310 }
311
312 bool IdleHelper::CanExceedIdleDeadlineIfRequired() const {
313 TRACE_EVENT0(disabled_by_default_tracing_category_,
314 "CanExceedIdleDeadlineIfRequired");
315 helper_->CheckOnValidThread();
316 return state_.idle_period_state() ==
317 IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE;
318 }
319
320 IdleHelper::IdlePeriodState IdleHelper::SchedulerIdlePeriodState() const {
321 return state_.idle_period_state();
322 }
323
324 IdleHelper::State::State(SchedulerHelper* helper,
325 Delegate* delegate,
326 const char* tracing_category,
327 const char* disabled_by_default_tracing_category,
328 const char* idle_period_tracing_name)
329 : helper_(helper),
330 delegate_(delegate),
331 idle_period_state_(IdlePeriodState::NOT_IN_IDLE_PERIOD),
332 idle_period_trace_event_started_(false),
333 running_idle_task_for_tracing_(false),
334 tracing_category_(tracing_category),
335 disabled_by_default_tracing_category_(
336 disabled_by_default_tracing_category),
337 idle_period_tracing_name_(idle_period_tracing_name) {
338 }
339
340 IdleHelper::State::~State() {
341 }
342
343 IdleHelper::IdlePeriodState IdleHelper::State::idle_period_state() const {
344 helper_->CheckOnValidThread();
345 return idle_period_state_;
346 }
347
348 base::TimeTicks IdleHelper::State::idle_period_deadline() const {
349 helper_->CheckOnValidThread();
350 return idle_period_deadline_;
351 }
352
353 void IdleHelper::State::UpdateState(IdlePeriodState new_state,
354 base::TimeTicks new_deadline,
355 base::TimeTicks optional_now) {
356 IdlePeriodState old_idle_period_state = idle_period_state_;
357
358 helper_->CheckOnValidThread();
359 if (new_state == idle_period_state_) {
360 DCHECK_EQ(new_deadline, idle_period_deadline_);
361 return;
362 }
363
364 bool is_tracing;
365 TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
366 if (is_tracing) {
367 base::TimeTicks now(optional_now.is_null()
368 ? helper_->scheduler_tqm_delegate()->NowTicks()
369 : optional_now);
370 TraceEventIdlePeriodStateChange(
371 new_state, running_idle_task_for_tracing_, idle_period_deadline_, now);
372 }
373
374 idle_period_state_ = new_state;
375 idle_period_deadline_ = new_deadline;
376
377 // Inform the delegate if we are starting or ending an idle period.
378 if (IsInIdlePeriod(new_state) && !IsInIdlePeriod(old_idle_period_state)) {
379 delegate_->OnIdlePeriodStarted();
380 } else if (!IsInIdlePeriod(new_state) &&
381 IsInIdlePeriod(old_idle_period_state)) {
382 delegate_->OnIdlePeriodEnded();
383 }
384 }
385
386 void IdleHelper::State::TraceIdleIdleTaskStart() {
387 helper_->CheckOnValidThread();
388
389 bool is_tracing;
390 TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
391 if (is_tracing) {
392 TraceEventIdlePeriodStateChange(
393 idle_period_state_, true, idle_period_deadline_,
394 base::TimeTicks::Now());
395 }
396 }
397
398 void IdleHelper::State::TraceIdleIdleTaskEnd() {
399 helper_->CheckOnValidThread();
400
401 bool is_tracing;
402 TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing);
403 if (is_tracing) {
404 TraceEventIdlePeriodStateChange(
405 idle_period_state_, false, idle_period_deadline_,
406 base::TimeTicks::Now());
407 }
408 }
409
410 void IdleHelper::State::TraceEventIdlePeriodStateChange(
411 IdlePeriodState new_state,
412 bool new_running_idle_task,
413 base::TimeTicks new_deadline,
414 base::TimeTicks now) {
415 TRACE_EVENT2(disabled_by_default_tracing_category_, "SetIdlePeriodState",
416 "old_state",
417 IdleHelper::IdlePeriodStateToString(idle_period_state_),
418 "new_state", IdleHelper::IdlePeriodStateToString(new_state));
419
420 if (idle_period_trace_event_started_ && running_idle_task_for_tracing_ &&
421 !new_running_idle_task) {
422 running_idle_task_for_tracing_ = false;
423 if (!idle_period_deadline_.is_null() && now > idle_period_deadline_) {
424 TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(
425 tracing_category_, idle_period_tracing_name_, this,
426 "DeadlineOverrun",
427 std::max(idle_period_deadline_,
428 last_idle_task_trace_time_).ToInternalValue());
429 }
430 }
431
432 if (IsInIdlePeriod(new_state)) {
433 if (!idle_period_trace_event_started_) {
434 idle_period_trace_event_started_ = true;
435 TRACE_EVENT_ASYNC_BEGIN1(
436 tracing_category_, idle_period_tracing_name_, this,
437 "idle_period_length_ms", (new_deadline - now).ToInternalValue());
438 }
439
440 if (new_running_idle_task) {
441 last_idle_task_trace_time_ = now;
442 running_idle_task_for_tracing_ = true;
443 TRACE_EVENT_ASYNC_STEP_INTO0(
444 tracing_category_, idle_period_tracing_name_, this,
445 "RunningIdleTask");
446 } else if (new_state == IdlePeriodState::IN_SHORT_IDLE_PERIOD) {
447 TRACE_EVENT_ASYNC_STEP_INTO0(
448 tracing_category_, idle_period_tracing_name_, this,
449 "ShortIdlePeriod");
450 } else if (IsInLongIdlePeriod(new_state) &&
451 new_state != IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED) {
452 TRACE_EVENT_ASYNC_STEP_INTO0(
453 tracing_category_, idle_period_tracing_name_, this,
454 "LongIdlePeriod");
455 } else if (new_state == IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED) {
456 TRACE_EVENT_ASYNC_STEP_INTO0(
457 tracing_category_, idle_period_tracing_name_, this,
458 "LongIdlePeriodPaused");
459 }
460 } else if (idle_period_trace_event_started_) {
461 idle_period_trace_event_started_ = false;
462 TRACE_EVENT_ASYNC_END0(tracing_category_, idle_period_tracing_name_, this);
463 }
464 }
465
466 // static
467 const char* IdleHelper::IdlePeriodStateToString(
468 IdlePeriodState idle_period_state) {
469 switch (idle_period_state) {
470 case IdlePeriodState::NOT_IN_IDLE_PERIOD:
471 return "not_in_idle_period";
472 case IdlePeriodState::IN_SHORT_IDLE_PERIOD:
473 return "in_short_idle_period";
474 case IdlePeriodState::IN_LONG_IDLE_PERIOD:
475 return "in_long_idle_period";
476 case IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE:
477 return "in_long_idle_period_with_max_deadline";
478 case IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED:
479 return "in_long_idle_period_paused";
480 default:
481 NOTREACHED();
482 return nullptr;
483 }
484 }
485
486 } // namespace scheduler
OLDNEW
« no previous file with comments | « components/scheduler/child/idle_helper.h ('k') | components/scheduler/child/idle_helper_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698