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