| 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 |