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 "content/child/scheduler/scheduler_helper.h" | |
6 | |
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" | |
11 #include "content/child/scheduler/time_source.h" | |
12 | |
13 namespace content { | |
14 | |
15 SchedulerHelper::SchedulerHelper( | |
16 scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner, | |
17 SchedulerHelperDelegate* scheduler_helper_delegate, | |
18 const char* tracing_category, | |
19 const char* disabled_by_default_tracing_category, | |
20 const char* idle_period_tracing_name, | |
21 size_t total_task_queue_count, | |
22 base::TimeDelta required_quiescence_duration_before_long_idle_period) | |
23 : task_queue_selector_(new PrioritizingTaskQueueSelector()), | |
24 task_queue_manager_( | |
25 new TaskQueueManager(total_task_queue_count, | |
26 main_task_runner, | |
27 task_queue_selector_.get(), | |
28 disabled_by_default_tracing_category)), | |
29 idle_period_state_(IdlePeriodState::NOT_IN_IDLE_PERIOD), | |
30 scheduler_helper_delegate_(scheduler_helper_delegate), | |
31 control_task_runner_( | |
32 task_queue_manager_->TaskRunnerForQueue(QueueId::CONTROL_TASK_QUEUE)), | |
33 control_task_after_wakeup_runner_(task_queue_manager_->TaskRunnerForQueue( | |
34 QueueId::CONTROL_TASK_AFTER_WAKEUP_QUEUE)), | |
35 default_task_runner_( | |
36 task_queue_manager_->TaskRunnerForQueue(QueueId::DEFAULT_TASK_QUEUE)), | |
37 quiescence_monitored_task_queue_mask_( | |
38 ((1ull << total_task_queue_count) - 1ull) & | |
39 ~(1ull << QueueId::IDLE_TASK_QUEUE) & | |
40 ~(1ull << QueueId::CONTROL_TASK_QUEUE) & | |
41 ~(1ull << QueueId::CONTROL_TASK_AFTER_WAKEUP_QUEUE)), | |
42 required_quiescence_duration_before_long_idle_period_( | |
43 required_quiescence_duration_before_long_idle_period), | |
44 time_source_(new TimeSource), | |
45 tracing_category_(tracing_category), | |
46 disabled_by_default_tracing_category_( | |
47 disabled_by_default_tracing_category), | |
48 idle_period_tracing_name_(idle_period_tracing_name), | |
49 weak_factory_(this) { | |
50 DCHECK_GE(total_task_queue_count, | |
51 static_cast<size_t>(QueueId::TASK_QUEUE_COUNT)); | |
52 weak_scheduler_ptr_ = weak_factory_.GetWeakPtr(); | |
53 end_idle_period_closure_.Reset( | |
54 base::Bind(&SchedulerHelper::EndIdlePeriod, weak_scheduler_ptr_)); | |
55 enable_next_long_idle_period_closure_.Reset( | |
56 base::Bind(&SchedulerHelper::EnableLongIdlePeriod, weak_scheduler_ptr_)); | |
57 enable_next_long_idle_period_after_wakeup_closure_.Reset(base::Bind( | |
58 &SchedulerHelper::EnableLongIdlePeriodAfterWakeup, weak_scheduler_ptr_)); | |
59 | |
60 idle_task_runner_ = make_scoped_refptr(new SingleThreadIdleTaskRunner( | |
61 task_queue_manager_->TaskRunnerForQueue(QueueId::IDLE_TASK_QUEUE), | |
62 control_task_after_wakeup_runner_, | |
63 base::Bind(&SchedulerHelper::CurrentIdleTaskDeadlineCallback, | |
64 weak_scheduler_ptr_), | |
65 tracing_category)); | |
66 | |
67 task_queue_selector_->SetQueuePriority( | |
68 QueueId::CONTROL_TASK_QUEUE, | |
69 PrioritizingTaskQueueSelector::CONTROL_PRIORITY); | |
70 | |
71 task_queue_selector_->SetQueuePriority( | |
72 QueueId::CONTROL_TASK_AFTER_WAKEUP_QUEUE, | |
73 PrioritizingTaskQueueSelector::CONTROL_PRIORITY); | |
74 task_queue_manager_->SetPumpPolicy( | |
75 QueueId::CONTROL_TASK_AFTER_WAKEUP_QUEUE, | |
76 TaskQueueManager::PumpPolicy::AFTER_WAKEUP); | |
77 | |
78 task_queue_selector_->DisableQueue(QueueId::IDLE_TASK_QUEUE); | |
79 task_queue_manager_->SetPumpPolicy(QueueId::IDLE_TASK_QUEUE, | |
80 TaskQueueManager::PumpPolicy::MANUAL); | |
81 | |
82 for (size_t i = 0; i < TASK_QUEUE_COUNT; i++) { | |
83 task_queue_manager_->SetQueueName( | |
84 i, TaskQueueIdToString(static_cast<QueueId>(i))); | |
85 } | |
86 | |
87 // TODO(skyostil): Increase this to 4 (crbug.com/444764). | |
88 task_queue_manager_->SetWorkBatchSize(1); | |
89 } | |
90 | |
91 SchedulerHelper::~SchedulerHelper() { | |
92 } | |
93 | |
94 SchedulerHelper::SchedulerHelperDelegate::SchedulerHelperDelegate() { | |
95 } | |
96 | |
97 SchedulerHelper::SchedulerHelperDelegate::~SchedulerHelperDelegate() { | |
98 } | |
99 | |
100 void SchedulerHelper::Shutdown() { | |
101 CheckOnValidThread(); | |
102 task_queue_manager_.reset(); | |
103 } | |
104 | |
105 scoped_refptr<base::SingleThreadTaskRunner> | |
106 SchedulerHelper::DefaultTaskRunner() { | |
107 CheckOnValidThread(); | |
108 return default_task_runner_; | |
109 } | |
110 | |
111 scoped_refptr<SingleThreadIdleTaskRunner> SchedulerHelper::IdleTaskRunner() { | |
112 CheckOnValidThread(); | |
113 return idle_task_runner_; | |
114 } | |
115 | |
116 scoped_refptr<base::SingleThreadTaskRunner> | |
117 SchedulerHelper::ControlTaskRunner() { | |
118 return control_task_runner_; | |
119 } | |
120 | |
121 void SchedulerHelper::CurrentIdleTaskDeadlineCallback( | |
122 base::TimeTicks* deadline_out) const { | |
123 CheckOnValidThread(); | |
124 *deadline_out = idle_period_deadline_; | |
125 } | |
126 | |
127 SchedulerHelper::IdlePeriodState SchedulerHelper::ComputeNewLongIdlePeriodState( | |
128 const base::TimeTicks now, | |
129 base::TimeDelta* next_long_idle_period_delay_out) { | |
130 CheckOnValidThread(); | |
131 | |
132 if (!scheduler_helper_delegate_->CanEnterLongIdlePeriod( | |
133 now, next_long_idle_period_delay_out)) { | |
134 return IdlePeriodState::NOT_IN_IDLE_PERIOD; | |
135 } | |
136 | |
137 base::TimeTicks next_pending_delayed_task = | |
138 task_queue_manager_->NextPendingDelayedTaskRunTime(); | |
139 base::TimeDelta max_long_idle_period_duration = | |
140 base::TimeDelta::FromMilliseconds(kMaximumIdlePeriodMillis); | |
141 base::TimeDelta long_idle_period_duration; | |
142 if (next_pending_delayed_task.is_null()) { | |
143 long_idle_period_duration = max_long_idle_period_duration; | |
144 } else { | |
145 // Limit the idle period duration to be before the next pending task. | |
146 long_idle_period_duration = std::min(next_pending_delayed_task - now, | |
147 max_long_idle_period_duration); | |
148 } | |
149 | |
150 if (long_idle_period_duration > base::TimeDelta()) { | |
151 *next_long_idle_period_delay_out = long_idle_period_duration; | |
152 return long_idle_period_duration == max_long_idle_period_duration | |
153 ? IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE | |
154 : IdlePeriodState::IN_LONG_IDLE_PERIOD; | |
155 } else { | |
156 // If we can't start the idle period yet then try again after wakeup. | |
157 *next_long_idle_period_delay_out = base::TimeDelta::FromMilliseconds( | |
158 kRetryEnableLongIdlePeriodDelayMillis); | |
159 return IdlePeriodState::NOT_IN_IDLE_PERIOD; | |
160 } | |
161 } | |
162 | |
163 bool SchedulerHelper::ShouldWaitForQuiescence() { | |
164 CheckOnValidThread(); | |
165 | |
166 if (!task_queue_manager_) | |
167 return false; | |
168 | |
169 if (required_quiescence_duration_before_long_idle_period_ == | |
170 base::TimeDelta()) | |
171 return false; | |
172 | |
173 uint64 task_queues_run_since_last_check_bitmap = | |
174 task_queue_manager_->GetAndClearTaskWasRunOnQueueBitmap() & | |
175 quiescence_monitored_task_queue_mask_; | |
176 | |
177 TRACE_EVENT1(disabled_by_default_tracing_category_, "ShouldWaitForQuiescence", | |
178 "task_queues_run_since_last_check_bitmap", | |
179 task_queues_run_since_last_check_bitmap); | |
180 | |
181 // If anything was run on the queues we care about, then we're not quiescent | |
182 // and we should wait. | |
183 return task_queues_run_since_last_check_bitmap != 0; | |
184 } | |
185 | |
186 void SchedulerHelper::EnableLongIdlePeriod() { | |
187 TRACE_EVENT0(disabled_by_default_tracing_category_, "EnableLongIdlePeriod"); | |
188 CheckOnValidThread(); | |
189 | |
190 // End any previous idle period. | |
191 EndIdlePeriod(); | |
192 | |
193 if (ShouldWaitForQuiescence()) { | |
194 control_task_runner_->PostDelayedTask( | |
195 FROM_HERE, enable_next_long_idle_period_closure_.callback(), | |
196 required_quiescence_duration_before_long_idle_period_); | |
197 scheduler_helper_delegate_->IsNotQuiescent(); | |
198 return; | |
199 } | |
200 | |
201 base::TimeTicks now(Now()); | |
202 base::TimeDelta next_long_idle_period_delay; | |
203 IdlePeriodState new_idle_period_state = | |
204 ComputeNewLongIdlePeriodState(now, &next_long_idle_period_delay); | |
205 if (IsInIdlePeriod(new_idle_period_state)) { | |
206 StartIdlePeriod(new_idle_period_state, now, | |
207 now + next_long_idle_period_delay, | |
208 false); | |
209 } | |
210 | |
211 if (task_queue_manager_->IsQueueEmpty(QueueId::IDLE_TASK_QUEUE)) { | |
212 // If there are no current idle tasks then post the call to initiate the | |
213 // next idle for execution after wakeup (at which point after-wakeup idle | |
214 // tasks might be eligible to run or more idle tasks posted). | |
215 control_task_after_wakeup_runner_->PostDelayedTask( | |
216 FROM_HERE, | |
217 enable_next_long_idle_period_after_wakeup_closure_.callback(), | |
218 next_long_idle_period_delay); | |
219 } else { | |
220 // Otherwise post on the normal control task queue. | |
221 control_task_runner_->PostDelayedTask( | |
222 FROM_HERE, enable_next_long_idle_period_closure_.callback(), | |
223 next_long_idle_period_delay); | |
224 } | |
225 } | |
226 | |
227 void SchedulerHelper::EnableLongIdlePeriodAfterWakeup() { | |
228 TRACE_EVENT0(disabled_by_default_tracing_category_, | |
229 "EnableLongIdlePeriodAfterWakeup"); | |
230 CheckOnValidThread(); | |
231 | |
232 if (IsInIdlePeriod(idle_period_state_)) { | |
233 // Since we were asleep until now, end the async idle period trace event at | |
234 // the time when it would have ended were we awake. | |
235 TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0( | |
236 tracing_category_, idle_period_tracing_name_, this, | |
237 std::min(idle_period_deadline_, Now()).ToInternalValue()); | |
238 idle_period_state_ = IdlePeriodState::ENDING_LONG_IDLE_PERIOD; | |
239 EndIdlePeriod(); | |
240 } | |
241 | |
242 // Post a task to initiate the next long idle period rather than calling it | |
243 // directly to allow all pending PostIdleTaskAfterWakeup tasks to get enqueued | |
244 // on the idle task queue before the next idle period starts so they are | |
245 // eligible to be run during the new idle period. | |
246 control_task_runner_->PostTask( | |
247 FROM_HERE, enable_next_long_idle_period_closure_.callback()); | |
248 } | |
249 | |
250 void SchedulerHelper::StartIdlePeriod(IdlePeriodState new_state, | |
251 base::TimeTicks now, | |
252 base::TimeTicks idle_period_deadline, | |
253 bool post_end_idle_period) { | |
254 DCHECK_GT(idle_period_deadline, now); | |
255 TRACE_EVENT_ASYNC_BEGIN0(tracing_category_, idle_period_tracing_name_, this); | |
256 CheckOnValidThread(); | |
257 DCHECK(IsInIdlePeriod(new_state)); | |
258 | |
259 task_queue_selector_->EnableQueue( | |
260 QueueId::IDLE_TASK_QUEUE, | |
261 PrioritizingTaskQueueSelector::BEST_EFFORT_PRIORITY); | |
262 task_queue_manager_->PumpQueue(QueueId::IDLE_TASK_QUEUE); | |
263 idle_period_state_ = new_state; | |
264 | |
265 idle_period_deadline_ = idle_period_deadline; | |
266 if (post_end_idle_period) { | |
267 control_task_runner_->PostDelayedTask( | |
268 FROM_HERE, | |
269 end_idle_period_closure_.callback(), | |
270 idle_period_deadline_ - now); | |
271 } | |
272 } | |
273 | |
274 void SchedulerHelper::EndIdlePeriod() { | |
275 CheckOnValidThread(); | |
276 | |
277 end_idle_period_closure_.Cancel(); | |
278 enable_next_long_idle_period_closure_.Cancel(); | |
279 enable_next_long_idle_period_after_wakeup_closure_.Cancel(); | |
280 | |
281 // If we weren't already within an idle period then early-out. | |
282 if (!IsInIdlePeriod(idle_period_state_)) | |
283 return; | |
284 | |
285 // If we are in the ENDING_LONG_IDLE_PERIOD state we have already logged the | |
286 // trace event. | |
287 if (idle_period_state_ != IdlePeriodState::ENDING_LONG_IDLE_PERIOD) { | |
288 bool is_tracing; | |
289 TRACE_EVENT_CATEGORY_GROUP_ENABLED(tracing_category_, &is_tracing); | |
290 if (is_tracing && !idle_period_deadline_.is_null() && | |
291 base::TimeTicks::Now() > idle_period_deadline_) { | |
292 TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0( | |
293 tracing_category_, idle_period_tracing_name_, this, "DeadlineOverrun", | |
294 idle_period_deadline_.ToInternalValue()); | |
295 } | |
296 TRACE_EVENT_ASYNC_END0(tracing_category_, idle_period_tracing_name_, this); | |
297 } | |
298 | |
299 task_queue_selector_->DisableQueue(QueueId::IDLE_TASK_QUEUE); | |
300 idle_period_state_ = IdlePeriodState::NOT_IN_IDLE_PERIOD; | |
301 idle_period_deadline_ = base::TimeTicks(); | |
302 } | |
303 | |
304 // static | |
305 bool SchedulerHelper::IsInIdlePeriod(IdlePeriodState state) { | |
306 return state != IdlePeriodState::NOT_IN_IDLE_PERIOD; | |
307 } | |
308 | |
309 bool SchedulerHelper::CanExceedIdleDeadlineIfRequired() const { | |
310 TRACE_EVENT0(tracing_category_, "CanExceedIdleDeadlineIfRequired"); | |
311 CheckOnValidThread(); | |
312 return idle_period_state_ == | |
313 IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE; | |
314 } | |
315 | |
316 void SchedulerHelper::SetTimeSourceForTesting( | |
317 scoped_ptr<TimeSource> time_source) { | |
318 CheckOnValidThread(); | |
319 time_source_ = time_source.Pass(); | |
320 } | |
321 | |
322 void SchedulerHelper::SetWorkBatchSizeForTesting(size_t work_batch_size) { | |
323 CheckOnValidThread(); | |
324 task_queue_manager_->SetWorkBatchSize(work_batch_size); | |
325 } | |
326 | |
327 TaskQueueManager* SchedulerHelper::GetTaskQueueManagerForTesting() { | |
328 CheckOnValidThread(); | |
329 return task_queue_manager_.get(); | |
330 } | |
331 | |
332 base::TimeTicks SchedulerHelper::Now() const { | |
333 return time_source_->Now(); | |
334 } | |
335 | |
336 SchedulerHelper::IdlePeriodState | |
337 SchedulerHelper::SchedulerIdlePeriodState() const { | |
338 return idle_period_state_; | |
339 } | |
340 | |
341 PrioritizingTaskQueueSelector* | |
342 SchedulerHelper::SchedulerTaskQueueSelector() const { | |
343 return task_queue_selector_.get(); | |
344 } | |
345 | |
346 scoped_refptr<base::SingleThreadTaskRunner> SchedulerHelper::TaskRunnerForQueue( | |
347 size_t queue_index) const { | |
348 CheckOnValidThread(); | |
349 return task_queue_manager_->TaskRunnerForQueue(queue_index); | |
350 } | |
351 | |
352 void SchedulerHelper::SetQueueName(size_t queue_index, const char* name) { | |
353 CheckOnValidThread(); | |
354 task_queue_manager_->SetQueueName(queue_index, name); | |
355 } | |
356 | |
357 bool SchedulerHelper::IsQueueEmpty(size_t queue_index) const { | |
358 CheckOnValidThread(); | |
359 return task_queue_manager_->IsQueueEmpty(queue_index); | |
360 } | |
361 | |
362 // static | |
363 const char* SchedulerHelper::TaskQueueIdToString(QueueId queue_id) { | |
364 switch (queue_id) { | |
365 case DEFAULT_TASK_QUEUE: | |
366 return "default_tq"; | |
367 case IDLE_TASK_QUEUE: | |
368 return "idle_tq"; | |
369 case CONTROL_TASK_QUEUE: | |
370 return "control_tq"; | |
371 case CONTROL_TASK_AFTER_WAKEUP_QUEUE: | |
372 return "control_after_wakeup_tq"; | |
373 default: | |
374 NOTREACHED(); | |
375 return nullptr; | |
376 } | |
377 } | |
378 | |
379 // static | |
380 const char* SchedulerHelper::IdlePeriodStateToString( | |
381 IdlePeriodState idle_period_state) { | |
382 switch (idle_period_state) { | |
383 case IdlePeriodState::NOT_IN_IDLE_PERIOD: | |
384 return "not_in_idle_period"; | |
385 case IdlePeriodState::IN_SHORT_IDLE_PERIOD: | |
386 return "in_short_idle_period"; | |
387 case IdlePeriodState::IN_LONG_IDLE_PERIOD: | |
388 return "in_long_idle_period"; | |
389 case IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE: | |
390 return "in_long_idle_period_with_max_deadline"; | |
391 case IdlePeriodState::ENDING_LONG_IDLE_PERIOD: | |
392 return "ending_long_idle_period"; | |
393 default: | |
394 NOTREACHED(); | |
395 return nullptr; | |
396 } | |
397 } | |
398 | |
399 void SchedulerHelper::AddTaskObserver( | |
400 base::MessageLoop::TaskObserver* task_observer) { | |
401 CheckOnValidThread(); | |
402 if (task_queue_manager_) | |
403 task_queue_manager_->AddTaskObserver(task_observer); | |
404 } | |
405 | |
406 void SchedulerHelper::RemoveTaskObserver( | |
407 base::MessageLoop::TaskObserver* task_observer) { | |
408 CheckOnValidThread(); | |
409 if (task_queue_manager_) | |
410 task_queue_manager_->RemoveTaskObserver(task_observer); | |
411 } | |
412 | |
413 } // namespace content | |
OLD | NEW |