OLD | NEW |
| (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 | |
OLD | NEW |