| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "platform/scheduler/base/task_queue_manager.h" | 5 #include "platform/scheduler/base/task_queue_manager.h" |
| 6 | 6 |
| 7 #include <queue> | 7 #include <queue> |
| 8 #include <set> | 8 #include <set> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 124 if (observer_) | 124 if (observer_) |
| 125 observer_->OnUnregisterTaskQueue(task_queue); | 125 observer_->OnUnregisterTaskQueue(task_queue); |
| 126 | 126 |
| 127 // Add |task_queue| to |queues_to_delete_| so we can prevent it from being | 127 // Add |task_queue| to |queues_to_delete_| so we can prevent it from being |
| 128 // freed while any of our structures hold hold a raw pointer to it. | 128 // freed while any of our structures hold hold a raw pointer to it. |
| 129 queues_to_delete_.insert(task_queue); | 129 queues_to_delete_.insert(task_queue); |
| 130 queues_.erase(task_queue); | 130 queues_.erase(task_queue); |
| 131 selector_.RemoveQueue(task_queue.get()); | 131 selector_.RemoveQueue(task_queue.get()); |
| 132 } | 132 } |
| 133 | 133 |
| 134 void TaskQueueManager::UpdateWorkQueues( | 134 void TaskQueueManager::UpdateWorkQueues(LazyNow lazy_now) { |
| 135 bool should_trigger_wakeup, | |
| 136 const internal::TaskQueueImpl::Task* previous_task, | |
| 137 LazyNow lazy_now) { | |
| 138 TRACE_EVENT0(disabled_by_default_tracing_category_, | 135 TRACE_EVENT0(disabled_by_default_tracing_category_, |
| 139 "TaskQueueManager::UpdateWorkQueues"); | 136 "TaskQueueManager::UpdateWorkQueues"); |
| 140 | 137 |
| 141 for (TimeDomain* time_domain : time_domains_) { | 138 for (TimeDomain* time_domain : time_domains_) { |
| 142 LazyNow lazy_now_in_domain = time_domain == real_time_domain_.get() | 139 LazyNow lazy_now_in_domain = time_domain == real_time_domain_.get() |
| 143 ? lazy_now | 140 ? lazy_now |
| 144 : time_domain->CreateLazyNow(); | 141 : time_domain->CreateLazyNow(); |
| 145 time_domain->UpdateWorkQueues(should_trigger_wakeup, previous_task, | 142 time_domain->UpdateWorkQueues(lazy_now_in_domain); |
| 146 lazy_now_in_domain); | |
| 147 } | 143 } |
| 148 } | 144 } |
| 149 | 145 |
| 150 void TaskQueueManager::MaybeScheduleImmediateWork( | 146 void TaskQueueManager::MaybeScheduleImmediateWork( |
| 151 const tracked_objects::Location& from_here) { | 147 const tracked_objects::Location& from_here) { |
| 152 bool on_main_thread = delegate_->BelongsToCurrentThread(); | 148 bool on_main_thread = delegate_->BelongsToCurrentThread(); |
| 153 // De-duplicate DoWork posts. | 149 // De-duplicate DoWork posts. |
| 154 if (on_main_thread) { | 150 if (on_main_thread) { |
| 155 if (!main_thread_pending_wakeups_.insert(base::TimeTicks()).second) { | 151 if (!main_thread_pending_wakeups_.insert(base::TimeTicks()).second) { |
| 156 return; | 152 return; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 187 DCHECK(main_thread_checker_.CalledOnValidThread()); | 183 DCHECK(main_thread_checker_.CalledOnValidThread()); |
| 188 TRACE_EVENT1(tracing_category_, "TaskQueueManager::DoWork", | 184 TRACE_EVENT1(tracing_category_, "TaskQueueManager::DoWork", |
| 189 "from_main_thread", from_main_thread); | 185 "from_main_thread", from_main_thread); |
| 190 if (from_main_thread) { | 186 if (from_main_thread) { |
| 191 main_thread_pending_wakeups_.erase(run_time); | 187 main_thread_pending_wakeups_.erase(run_time); |
| 192 } else { | 188 } else { |
| 193 base::AutoLock lock(other_thread_lock_); | 189 base::AutoLock lock(other_thread_lock_); |
| 194 other_thread_pending_wakeups_.erase(run_time); | 190 other_thread_pending_wakeups_.erase(run_time); |
| 195 } | 191 } |
| 196 | 192 |
| 193 // TODO(alexclarke): Add a base::RunLoop observer and prevent |
| 194 // MaybeScheduleImmediateWork posting MaybeScheduleImmediateWork while DoWork |
| 195 // is running. We'll need to post a DoWork on entering and leaving a nested |
| 196 // run loop. |
| 197 |
| 197 if (!delegate_->IsNested()) | 198 if (!delegate_->IsNested()) |
| 198 queues_to_delete_.clear(); | 199 queues_to_delete_.clear(); |
| 199 | 200 |
| 200 LazyNow lazy_now(real_time_domain()->CreateLazyNow()); | 201 LazyNow lazy_now(real_time_domain()->CreateLazyNow()); |
| 201 base::TimeTicks task_start_time; | 202 base::TimeTicks task_start_time; |
| 202 | 203 |
| 203 if (!delegate_->IsNested() && task_time_observers_.might_have_observers()) | 204 if (!delegate_->IsNested() && task_time_observers_.might_have_observers()) |
| 204 task_start_time = lazy_now.Now(); | 205 task_start_time = lazy_now.Now(); |
| 205 | 206 |
| 206 // Pass false and nullptr to UpdateWorkQueues here to prevent waking up a | 207 // TODO(alexclarke): Get rid of this and call once per loop iteration. |
| 207 // pump-after-wakeup queue. | 208 UpdateWorkQueues(lazy_now); |
| 208 UpdateWorkQueues(false, nullptr, lazy_now); | |
| 209 | |
| 210 internal::TaskQueueImpl::Task previous_task; | |
| 211 | 209 |
| 212 for (int i = 0; i < work_batch_size_; i++) { | 210 for (int i = 0; i < work_batch_size_; i++) { |
| 213 internal::WorkQueue* work_queue; | 211 internal::WorkQueue* work_queue; |
| 214 if (!SelectWorkQueueToService(&work_queue)) { | 212 if (!SelectWorkQueueToService(&work_queue)) |
| 215 break; | 213 break; |
| 216 } | |
| 217 | 214 |
| 218 bool should_trigger_wakeup = work_queue->task_queue()->wakeup_policy() == | 215 switch (ProcessTaskFromWorkQueue(work_queue)) { |
| 219 TaskQueue::WakeupPolicy::CAN_WAKE_OTHER_QUEUES; | |
| 220 | |
| 221 switch (ProcessTaskFromWorkQueue(work_queue, &previous_task)) { | |
| 222 case ProcessTaskResult::DEFERRED: | 216 case ProcessTaskResult::DEFERRED: |
| 223 // If a task was deferred, try again with another task. Note that this | 217 // If a task was deferred, try again with another task. |
| 224 // means deferred tasks (i.e. non-nestable tasks) will never trigger | |
| 225 // queue wake-ups. | |
| 226 continue; | 218 continue; |
| 227 case ProcessTaskResult::EXECUTED: | 219 case ProcessTaskResult::EXECUTED: |
| 228 break; | 220 break; |
| 229 case ProcessTaskResult::TASK_QUEUE_MANAGER_DELETED: | 221 case ProcessTaskResult::TASK_QUEUE_MANAGER_DELETED: |
| 230 return; // The TaskQueueManager got deleted, we must bail out. | 222 return; // The TaskQueueManager got deleted, we must bail out. |
| 231 } | 223 } |
| 232 | 224 |
| 233 lazy_now = real_time_domain()->CreateLazyNow(); | 225 lazy_now = real_time_domain()->CreateLazyNow(); |
| 234 if (!delegate_->IsNested() && task_start_time != base::TimeTicks()) { | 226 if (!delegate_->IsNested() && task_start_time != base::TimeTicks()) { |
| 235 // Only report top level task durations. | 227 // Only report top level task durations. |
| 236 base::TimeTicks task_end_time = lazy_now.Now(); | 228 base::TimeTicks task_end_time = lazy_now.Now(); |
| 237 FOR_EACH_OBSERVER(TaskTimeObserver, task_time_observers_, | 229 FOR_EACH_OBSERVER(TaskTimeObserver, task_time_observers_, |
| 238 ReportTaskTime(MonotonicTimeInSeconds(task_start_time), | 230 ReportTaskTime(MonotonicTimeInSeconds(task_start_time), |
| 239 MonotonicTimeInSeconds(task_end_time))); | 231 MonotonicTimeInSeconds(task_end_time))); |
| 240 task_start_time = task_end_time; | 232 task_start_time = task_end_time; |
| 241 } | 233 } |
| 242 | 234 |
| 243 work_queue = nullptr; // The queue may have been unregistered. | 235 work_queue = nullptr; // The queue may have been unregistered. |
| 244 | 236 |
| 245 UpdateWorkQueues(should_trigger_wakeup, &previous_task, lazy_now); | 237 UpdateWorkQueues(lazy_now); |
| 246 | 238 |
| 247 // Only run a single task per batch in nested run loops so that we can | 239 // Only run a single task per batch in nested run loops so that we can |
| 248 // properly exit the nested loop when someone calls RunLoop::Quit(). | 240 // properly exit the nested loop when someone calls RunLoop::Quit(). |
| 249 if (delegate_->IsNested()) | 241 if (delegate_->IsNested()) |
| 250 break; | 242 break; |
| 251 } | 243 } |
| 252 | 244 |
| 253 // TODO(alexclarke): Consider refactoring the above loop to terminate only | 245 // TODO(alexclarke): Consider refactoring the above loop to terminate only |
| 254 // when there's no more work left to be done, rather than posting a | 246 // when there's no more work left to be done, rather than posting a |
| 255 // continuation task. | 247 // continuation task. |
| (...skipping 17 matching lines...) Expand all Loading... |
| 273 AsValueWithSelectorResult(should_run, *out_work_queue)); | 265 AsValueWithSelectorResult(should_run, *out_work_queue)); |
| 274 return should_run; | 266 return should_run; |
| 275 } | 267 } |
| 276 | 268 |
| 277 void TaskQueueManager::DidQueueTask( | 269 void TaskQueueManager::DidQueueTask( |
| 278 const internal::TaskQueueImpl::Task& pending_task) { | 270 const internal::TaskQueueImpl::Task& pending_task) { |
| 279 task_annotator_.DidQueueTask("TaskQueueManager::PostTask", pending_task); | 271 task_annotator_.DidQueueTask("TaskQueueManager::PostTask", pending_task); |
| 280 } | 272 } |
| 281 | 273 |
| 282 TaskQueueManager::ProcessTaskResult TaskQueueManager::ProcessTaskFromWorkQueue( | 274 TaskQueueManager::ProcessTaskResult TaskQueueManager::ProcessTaskFromWorkQueue( |
| 283 internal::WorkQueue* work_queue, | 275 internal::WorkQueue* work_queue) { |
| 284 internal::TaskQueueImpl::Task* out_previous_task) { | |
| 285 DCHECK(main_thread_checker_.CalledOnValidThread()); | 276 DCHECK(main_thread_checker_.CalledOnValidThread()); |
| 286 scoped_refptr<DeletionSentinel> protect(deletion_sentinel_); | 277 scoped_refptr<DeletionSentinel> protect(deletion_sentinel_); |
| 287 internal::TaskQueueImpl* queue = work_queue->task_queue(); | 278 internal::TaskQueueImpl* queue = work_queue->task_queue(); |
| 288 | 279 |
| 289 if (queue->GetQuiescenceMonitored()) | 280 if (queue->GetQuiescenceMonitored()) |
| 290 task_was_run_on_quiescence_monitored_queue_ = true; | 281 task_was_run_on_quiescence_monitored_queue_ = true; |
| 291 | 282 |
| 292 internal::TaskQueueImpl::Task pending_task = | 283 internal::TaskQueueImpl::Task pending_task = |
| 293 work_queue->TakeTaskFromWorkQueue(); | 284 work_queue->TakeTaskFromWorkQueue(); |
| 294 if (!pending_task.nestable && delegate_->IsNested()) { | 285 if (!pending_task.nestable && delegate_->IsNested()) { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 311 } | 302 } |
| 312 TRACE_EVENT1(tracing_category_, "TaskQueueManager::RunTask", "queue", | 303 TRACE_EVENT1(tracing_category_, "TaskQueueManager::RunTask", "queue", |
| 313 queue->GetName()); | 304 queue->GetName()); |
| 314 // NOTE when TaskQueues get unregistered a reference ends up getting retained | 305 // NOTE when TaskQueues get unregistered a reference ends up getting retained |
| 315 // by |queues_to_delete_| which is cleared at the top of |DoWork|. This means | 306 // by |queues_to_delete_| which is cleared at the top of |DoWork|. This means |
| 316 // we are OK to use raw pointers here. | 307 // we are OK to use raw pointers here. |
| 317 internal::TaskQueueImpl* prev_executing_task_queue = | 308 internal::TaskQueueImpl* prev_executing_task_queue = |
| 318 currently_executing_task_queue_; | 309 currently_executing_task_queue_; |
| 319 currently_executing_task_queue_ = queue; | 310 currently_executing_task_queue_ = queue; |
| 320 task_annotator_.RunTask("TaskQueueManager::PostTask", pending_task); | 311 task_annotator_.RunTask("TaskQueueManager::PostTask", pending_task); |
| 321 | |
| 322 // Detect if the TaskQueueManager just got deleted. If this happens we must | 312 // Detect if the TaskQueueManager just got deleted. If this happens we must |
| 323 // not access any member variables after this point. | 313 // not access any member variables after this point. |
| 324 if (protect->HasOneRef()) | 314 if (protect->HasOneRef()) |
| 325 return ProcessTaskResult::TASK_QUEUE_MANAGER_DELETED; | 315 return ProcessTaskResult::TASK_QUEUE_MANAGER_DELETED; |
| 326 | 316 |
| 327 currently_executing_task_queue_ = prev_executing_task_queue; | 317 currently_executing_task_queue_ = prev_executing_task_queue; |
| 328 | 318 |
| 329 if (queue->GetShouldNotifyObservers()) { | 319 if (queue->GetShouldNotifyObservers()) { |
| 330 FOR_EACH_OBSERVER(base::MessageLoop::TaskObserver, task_observers_, | 320 FOR_EACH_OBSERVER(base::MessageLoop::TaskObserver, task_observers_, |
| 331 DidProcessTask(pending_task)); | 321 DidProcessTask(pending_task)); |
| 332 queue->NotifyDidProcessTask(pending_task); | 322 queue->NotifyDidProcessTask(pending_task); |
| 333 } | 323 } |
| 334 | 324 |
| 335 pending_task.task.Reset(); | |
| 336 *out_previous_task = std::move(pending_task); | |
| 337 return ProcessTaskResult::EXECUTED; | 325 return ProcessTaskResult::EXECUTED; |
| 338 } | 326 } |
| 339 | 327 |
| 340 void TaskQueueManager::MaybeRecordTaskDelayHistograms( | 328 void TaskQueueManager::MaybeRecordTaskDelayHistograms( |
| 341 const internal::TaskQueueImpl::Task& pending_task, | 329 const internal::TaskQueueImpl::Task& pending_task, |
| 342 const internal::TaskQueueImpl* queue) { | 330 const internal::TaskQueueImpl* queue) { |
| 343 if ((task_count_++ % kRecordRecordTaskDelayHistogramsEveryNTasks) != 0) | 331 if ((task_count_++ % kRecordRecordTaskDelayHistogramsEveryNTasks) != 0) |
| 344 return; | 332 return; |
| 345 | 333 |
| 346 // Record delayed task lateness and immediate task queueing durations, but | 334 // Record delayed task lateness and immediate task queuing durations. |
| 347 // only for auto-pumped queues. Manually pumped and after wakeup queues can | 335 if (!pending_task.delayed_run_time.is_null()) { |
| 348 // have arbitarially large delayes, which would cloud any analysis. | 336 RecordDelayedTaskLateness(delegate_->NowTicks() - |
| 349 if (queue->GetPumpPolicy() == TaskQueue::PumpPolicy::AUTO) { | 337 pending_task.delayed_run_time); |
| 350 if (!pending_task.delayed_run_time.is_null()) { | 338 } else if (!pending_task.time_posted.is_null()) { |
| 351 RecordDelayedTaskLateness(delegate_->NowTicks() - | 339 RecordImmediateTaskQueueingDuration(tracked_objects::TrackedTime::Now() - |
| 352 pending_task.delayed_run_time); | 340 pending_task.time_posted); |
| 353 } else if (!pending_task.time_posted.is_null()) { | |
| 354 RecordImmediateTaskQueueingDuration(tracked_objects::TrackedTime::Now() - | |
| 355 pending_task.time_posted); | |
| 356 } | |
| 357 } | 341 } |
| 358 } | 342 } |
| 359 | 343 |
| 360 bool TaskQueueManager::RunsTasksOnCurrentThread() const { | 344 bool TaskQueueManager::RunsTasksOnCurrentThread() const { |
| 361 return delegate_->RunsTasksOnCurrentThread(); | 345 return delegate_->RunsTasksOnCurrentThread(); |
| 362 } | 346 } |
| 363 | 347 |
| 364 void TaskQueueManager::SetWorkBatchSize(int work_batch_size) { | 348 void TaskQueueManager::SetWorkBatchSize(int work_batch_size) { |
| 365 DCHECK(main_thread_checker_.CalledOnValidThread()); | 349 DCHECK(main_thread_checker_.CalledOnValidThread()); |
| 366 DCHECK_GE(work_batch_size, 1); | 350 DCHECK_GE(work_batch_size, 1); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 432 state->BeginArray("time_domains"); | 416 state->BeginArray("time_domains"); |
| 433 for (auto* time_domain : time_domains_) | 417 for (auto* time_domain : time_domains_) |
| 434 time_domain->AsValueInto(state.get()); | 418 time_domain->AsValueInto(state.get()); |
| 435 state->EndArray(); | 419 state->EndArray(); |
| 436 return std::move(state); | 420 return std::move(state); |
| 437 } | 421 } |
| 438 | 422 |
| 439 void TaskQueueManager::OnTaskQueueEnabled(internal::TaskQueueImpl* queue) { | 423 void TaskQueueManager::OnTaskQueueEnabled(internal::TaskQueueImpl* queue) { |
| 440 DCHECK(main_thread_checker_.CalledOnValidThread()); | 424 DCHECK(main_thread_checker_.CalledOnValidThread()); |
| 441 // Only schedule DoWork if there's something to do. | 425 // Only schedule DoWork if there's something to do. |
| 442 if (!queue->immediate_work_queue()->Empty() || | 426 if (queue->HasPendingImmediateWork()) |
| 443 !queue->delayed_work_queue()->Empty()) { | |
| 444 MaybeScheduleImmediateWork(FROM_HERE); | 427 MaybeScheduleImmediateWork(FROM_HERE); |
| 445 } | |
| 446 } | 428 } |
| 447 | 429 |
| 448 void TaskQueueManager::OnTriedToSelectBlockedWorkQueue( | 430 void TaskQueueManager::OnTriedToSelectBlockedWorkQueue( |
| 449 internal::WorkQueue* work_queue) { | 431 internal::WorkQueue* work_queue) { |
| 450 DCHECK(main_thread_checker_.CalledOnValidThread()); | 432 DCHECK(main_thread_checker_.CalledOnValidThread()); |
| 451 DCHECK(!work_queue->Empty()); | 433 DCHECK(!work_queue->Empty()); |
| 452 if (observer_) { | 434 if (observer_) { |
| 453 observer_->OnTriedToExecuteBlockedTask(*work_queue->task_queue(), | 435 observer_->OnTriedToExecuteBlockedTask(*work_queue->task_queue(), |
| 454 *work_queue->GetFrontTask()); | 436 *work_queue->GetFrontTask()); |
| 455 } | 437 } |
| 456 } | 438 } |
| 457 | 439 |
| 458 } // namespace scheduler | 440 } // namespace scheduler |
| 459 } // namespace blink | 441 } // namespace blink |
| OLD | NEW |