| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "base/task_scheduler/scheduler_worker_pool_impl.h" | 5 #include "base/task_scheduler/scheduler_worker_pool_impl.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <utility> | 10 #include <utility> |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 230 const ReEnqueueSequenceCallback& re_enqueue_sequence_callback, | 230 const ReEnqueueSequenceCallback& re_enqueue_sequence_callback, |
| 231 const PriorityQueue* shared_priority_queue, | 231 const PriorityQueue* shared_priority_queue, |
| 232 int index); | 232 int index); |
| 233 ~SchedulerWorkerDelegateImpl() override; | 233 ~SchedulerWorkerDelegateImpl() override; |
| 234 | 234 |
| 235 PriorityQueue* single_threaded_priority_queue() { | 235 PriorityQueue* single_threaded_priority_queue() { |
| 236 return &single_threaded_priority_queue_; | 236 return &single_threaded_priority_queue_; |
| 237 } | 237 } |
| 238 | 238 |
| 239 // SchedulerWorker::Delegate: | 239 // SchedulerWorker::Delegate: |
| 240 void OnMainEntry(SchedulerWorker* worker, | 240 void OnMainEntry(SchedulerWorker* worker) override; |
| 241 const TimeDelta& detach_duration) override; | |
| 242 scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override; | 241 scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override; |
| 243 void DidRunTaskWithPriority(TaskPriority task_priority, | 242 void DidRunTaskWithPriority(TaskPriority task_priority, |
| 244 const TimeDelta& task_latency) override; | 243 const TimeDelta& task_latency) override; |
| 245 void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override; | 244 void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override; |
| 246 TimeDelta GetSleepTimeout() override; | 245 TimeDelta GetSleepTimeout() override; |
| 247 bool CanDetach(SchedulerWorker* worker) override; | 246 bool CanDetach(SchedulerWorker* worker) override; |
| 247 void OnDetach() override; |
| 248 | 248 |
| 249 void RegisterSingleThreadTaskRunner() { | 249 void RegisterSingleThreadTaskRunner() { |
| 250 // No barrier as barriers only affect sequential consistency which is | 250 // No barrier as barriers only affect sequential consistency which is |
| 251 // irrelevant in a single variable use case (they don't force an immediate | 251 // irrelevant in a single variable use case (they don't force an immediate |
| 252 // flush anymore than atomics do by default). | 252 // flush anymore than atomics do by default). |
| 253 subtle::NoBarrier_AtomicIncrement(&num_single_threaded_runners_, 1); | 253 subtle::NoBarrier_AtomicIncrement(&num_single_threaded_runners_, 1); |
| 254 } | 254 } |
| 255 | 255 |
| 256 void UnregisterSingleThreadTaskRunner() { | 256 void UnregisterSingleThreadTaskRunner() { |
| 257 subtle::NoBarrier_AtomicIncrement(&num_single_threaded_runners_, -1); | 257 subtle::NoBarrier_AtomicIncrement(&num_single_threaded_runners_, -1); |
| 258 } | 258 } |
| 259 | 259 |
| 260 private: | 260 private: |
| 261 SchedulerWorkerPoolImpl* outer_; | 261 SchedulerWorkerPoolImpl* outer_; |
| 262 const ReEnqueueSequenceCallback re_enqueue_sequence_callback_; | 262 const ReEnqueueSequenceCallback re_enqueue_sequence_callback_; |
| 263 | 263 |
| 264 // Single-threaded PriorityQueue for the worker. | 264 // Single-threaded PriorityQueue for the worker. |
| 265 PriorityQueue single_threaded_priority_queue_; | 265 PriorityQueue single_threaded_priority_queue_; |
| 266 | 266 |
| 267 // True if the last Sequence returned by GetWork() was extracted from | 267 // True if the last Sequence returned by GetWork() was extracted from |
| 268 // |single_threaded_priority_queue_|. | 268 // |single_threaded_priority_queue_|. |
| 269 bool last_sequence_is_single_threaded_ = false; | 269 bool last_sequence_is_single_threaded_ = false; |
| 270 | 270 |
| 271 // Time of the last detach. |
| 272 TimeTicks last_detach_time_; |
| 273 |
| 271 // Time when GetWork() first returned nullptr. | 274 // Time when GetWork() first returned nullptr. |
| 272 TimeTicks idle_start_time_; | 275 TimeTicks idle_start_time_; |
| 273 | 276 |
| 274 // Indicates whether the last call to GetWork() returned nullptr. | 277 // Indicates whether the last call to GetWork() returned nullptr. |
| 275 bool last_get_work_returned_nullptr_ = false; | 278 bool last_get_work_returned_nullptr_ = false; |
| 276 | 279 |
| 277 // Indicates whether the SchedulerWorker was detached since the last call to | 280 // Indicates whether the SchedulerWorker was detached since the last call to |
| 278 // GetWork(). | 281 // GetWork(). |
| 279 bool did_detach_since_last_get_work_ = false; | 282 bool did_detach_since_last_get_work_ = false; |
| 280 | 283 |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 474 int index) | 477 int index) |
| 475 : outer_(outer), | 478 : outer_(outer), |
| 476 re_enqueue_sequence_callback_(re_enqueue_sequence_callback), | 479 re_enqueue_sequence_callback_(re_enqueue_sequence_callback), |
| 477 single_threaded_priority_queue_(shared_priority_queue), | 480 single_threaded_priority_queue_(shared_priority_queue), |
| 478 index_(index) {} | 481 index_(index) {} |
| 479 | 482 |
| 480 SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl:: | 483 SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl:: |
| 481 ~SchedulerWorkerDelegateImpl() = default; | 484 ~SchedulerWorkerDelegateImpl() = default; |
| 482 | 485 |
| 483 void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::OnMainEntry( | 486 void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::OnMainEntry( |
| 484 SchedulerWorker* worker, | 487 SchedulerWorker* worker) { |
| 485 const TimeDelta& detach_duration) { | |
| 486 #if DCHECK_IS_ON() | 488 #if DCHECK_IS_ON() |
| 487 // Wait for |outer_->workers_created_| to avoid traversing | 489 // Wait for |outer_->workers_created_| to avoid traversing |
| 488 // |outer_->workers_| while it is being filled by Initialize(). | 490 // |outer_->workers_| while it is being filled by Initialize(). |
| 489 outer_->workers_created_.Wait(); | 491 outer_->workers_created_.Wait(); |
| 490 DCHECK(ContainsWorker(outer_->workers_, worker)); | 492 DCHECK(ContainsWorker(outer_->workers_, worker)); |
| 491 #endif | 493 #endif |
| 492 | 494 |
| 493 DCHECK_EQ(num_tasks_since_last_wait_, 0U); | 495 DCHECK_EQ(num_tasks_since_last_wait_, 0U); |
| 494 | 496 |
| 495 // Record histograms if the worker detached in the past. | 497 if (!last_detach_time_.is_null()) { |
| 496 if (!detach_duration.is_max()) { | 498 outer_->detach_duration_histogram_->AddTime(TimeTicks::Now() - |
| 497 outer_->detach_duration_histogram_->AddTime(detach_duration); | 499 last_detach_time_); |
| 498 outer_->num_tasks_before_detach_histogram_->Add( | |
| 499 num_tasks_since_last_detach_); | |
| 500 num_tasks_since_last_detach_ = 0; | |
| 501 did_detach_since_last_get_work_ = true; | |
| 502 } | 500 } |
| 503 | 501 |
| 504 PlatformThread::SetName( | 502 PlatformThread::SetName( |
| 505 StringPrintf("TaskScheduler%sWorker%d", outer_->name_.c_str(), index_)); | 503 StringPrintf("TaskScheduler%sWorker%d", outer_->name_.c_str(), index_)); |
| 506 | 504 |
| 507 DCHECK(!tls_current_worker.Get().Get()); | 505 DCHECK(!tls_current_worker.Get().Get()); |
| 508 DCHECK(!tls_current_worker_pool.Get().Get()); | 506 DCHECK(!tls_current_worker_pool.Get().Get()); |
| 509 tls_current_worker.Get().Set(worker); | 507 tls_current_worker.Get().Set(worker); |
| 510 tls_current_worker_pool.Get().Set(outer_); | 508 tls_current_worker_pool.Get().Set(outer_); |
| 511 | 509 |
| 512 // New threads haven't run GetWork() yet, so reset the |idle_start_time_|. | 510 // New threads haven't run GetWork() yet, so reset the |idle_start_time_|. |
| 513 idle_start_time_ = TimeTicks(); | 511 idle_start_time_ = TimeTicks(); |
| 514 | 512 |
| 515 ThreadRestrictions::SetIOAllowed( | 513 ThreadRestrictions::SetIOAllowed( |
| 516 outer_->io_restriction_ == | 514 outer_->io_restriction_ == |
| 517 SchedulerWorkerPoolParams::IORestriction::ALLOWED); | 515 SchedulerWorkerPoolParams::IORestriction::ALLOWED); |
| 518 } | 516 } |
| 519 | 517 |
| 520 scoped_refptr<Sequence> | 518 scoped_refptr<Sequence> |
| 521 SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::GetWork( | 519 SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::GetWork( |
| 522 SchedulerWorker* worker) { | 520 SchedulerWorker* worker) { |
| 523 DCHECK(ContainsWorker(outer_->workers_, worker)); | 521 DCHECK(ContainsWorker(outer_->workers_, worker)); |
| 524 | 522 |
| 525 // Record the TaskScheduler.NumTasksBetweenWaits histogram if the | 523 // Record the TaskScheduler.NumTasksBetweenWaits histogram if the |
| 526 // SchedulerWorker waited on its WaitableEvent since the last GetWork(). | 524 // SchedulerWorker waited on its WaitableEvent since the last GetWork(). |
| 527 // | 525 // |
| 528 // Note: When GetWork() returns nullptr for the first time after returning a | 526 // Note: When GetWork() starts returning nullptr, the SchedulerWorker waits on |
| 529 // Sequence, SchedulerWorker waits on its WaitableEvent. When the wait stops | 527 // its WaitableEvent. When it wakes up (either because WakeUp() was called or |
| 530 // (either because WakeUp() was called or because the sleep timeout expired), | 528 // because the sleep timeout expired), it calls GetWork() again. The code |
| 531 // GetWork() is called and the histogram is recorded. If GetWork() returns | 529 // below records the histogram and, if GetWork() returns nullptr again, the |
| 532 // nullptr again, the SchedulerWorker may detach. | 530 // SchedulerWorker may detach. If that happens, |
| 533 // |did_detach_since_last_get_work_| is set to true from OnMainEntry() if the | 531 // |did_detach_since_last_get_work_| is set to true and the next call to |
| 534 // SchedulerWorker detaches and wakes up again. The next call to GetWork() | 532 // GetWork() won't record the histogram (which is correct since the |
| 535 // won't record the histogram (which is correct since the SchedulerWorker | 533 // SchedulerWorker didn't wait on its WaitableEvent since the last time the |
| 536 // didn't wait on its WaitableEvent since the last time the histogram was | 534 // histogram was recorded). |
| 537 // recorded). | |
| 538 if (last_get_work_returned_nullptr_ && !did_detach_since_last_get_work_) { | 535 if (last_get_work_returned_nullptr_ && !did_detach_since_last_get_work_) { |
| 539 outer_->num_tasks_between_waits_histogram_->Add(num_tasks_since_last_wait_); | 536 outer_->num_tasks_between_waits_histogram_->Add(num_tasks_since_last_wait_); |
| 540 num_tasks_since_last_wait_ = 0; | 537 num_tasks_since_last_wait_ = 0; |
| 541 } | 538 } |
| 542 | 539 |
| 543 scoped_refptr<Sequence> sequence; | 540 scoped_refptr<Sequence> sequence; |
| 544 { | 541 { |
| 545 std::unique_ptr<PriorityQueue::Transaction> shared_transaction( | 542 std::unique_ptr<PriorityQueue::Transaction> shared_transaction( |
| 546 outer_->shared_priority_queue_.BeginTransaction()); | 543 outer_->shared_priority_queue_.BeginTransaction()); |
| 547 std::unique_ptr<PriorityQueue::Transaction> single_threaded_transaction( | 544 std::unique_ptr<PriorityQueue::Transaction> single_threaded_transaction( |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 652 // created by the worker. | 649 // created by the worker. |
| 653 const bool can_detach = | 650 const bool can_detach = |
| 654 !idle_start_time_.is_null() && | 651 !idle_start_time_.is_null() && |
| 655 (TimeTicks::Now() - idle_start_time_) > outer_->suggested_reclaim_time_ && | 652 (TimeTicks::Now() - idle_start_time_) > outer_->suggested_reclaim_time_ && |
| 656 worker != outer_->PeekAtIdleWorkersStack() && | 653 worker != outer_->PeekAtIdleWorkersStack() && |
| 657 !subtle::NoBarrier_Load(&num_single_threaded_runners_) && | 654 !subtle::NoBarrier_Load(&num_single_threaded_runners_) && |
| 658 outer_->CanWorkerDetachForTesting(); | 655 outer_->CanWorkerDetachForTesting(); |
| 659 return can_detach; | 656 return can_detach; |
| 660 } | 657 } |
| 661 | 658 |
| 659 void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::OnDetach() { |
| 660 DCHECK(!did_detach_since_last_get_work_); |
| 661 outer_->num_tasks_before_detach_histogram_->Add(num_tasks_since_last_detach_); |
| 662 num_tasks_since_last_detach_ = 0; |
| 663 did_detach_since_last_get_work_ = true; |
| 664 last_detach_time_ = TimeTicks::Now(); |
| 665 } |
| 666 |
| 662 SchedulerWorkerPoolImpl::SchedulerWorkerPoolImpl( | 667 SchedulerWorkerPoolImpl::SchedulerWorkerPoolImpl( |
| 663 StringPiece name, | 668 StringPiece name, |
| 664 SchedulerWorkerPoolParams::IORestriction io_restriction, | 669 SchedulerWorkerPoolParams::IORestriction io_restriction, |
| 665 const TimeDelta& suggested_reclaim_time, | 670 const TimeDelta& suggested_reclaim_time, |
| 666 TaskTracker* task_tracker, | 671 TaskTracker* task_tracker, |
| 667 DelayedTaskManager* delayed_task_manager) | 672 DelayedTaskManager* delayed_task_manager) |
| 668 : name_(name.as_string()), | 673 : name_(name.as_string()), |
| 669 io_restriction_(io_restriction), | 674 io_restriction_(io_restriction), |
| 670 suggested_reclaim_time_(suggested_reclaim_time), | 675 suggested_reclaim_time_(suggested_reclaim_time), |
| 671 idle_workers_stack_lock_(shared_priority_queue_.container_lock()), | 676 idle_workers_stack_lock_(shared_priority_queue_.container_lock()), |
| 672 idle_workers_stack_cv_for_testing_( | 677 idle_workers_stack_cv_for_testing_( |
| 673 idle_workers_stack_lock_.CreateConditionVariable()), | 678 idle_workers_stack_lock_.CreateConditionVariable()), |
| 674 join_for_testing_returned_(WaitableEvent::ResetPolicy::MANUAL, | 679 join_for_testing_returned_(WaitableEvent::ResetPolicy::MANUAL, |
| 675 WaitableEvent::InitialState::NOT_SIGNALED), | 680 WaitableEvent::InitialState::NOT_SIGNALED), |
| 676 #if DCHECK_IS_ON() | 681 #if DCHECK_IS_ON() |
| 677 workers_created_(WaitableEvent::ResetPolicy::MANUAL, | 682 workers_created_(WaitableEvent::ResetPolicy::MANUAL, |
| 678 WaitableEvent::InitialState::NOT_SIGNALED), | 683 WaitableEvent::InitialState::NOT_SIGNALED), |
| 679 #endif | 684 #endif |
| 680 // Mimics the UMA_HISTOGRAM_LONG_TIMES macro. | 685 // Mimics the UMA_HISTOGRAM_LONG_TIMES macro. |
| 681 detach_duration_histogram_(Histogram::FactoryTimeGet( | 686 detach_duration_histogram_(Histogram::FactoryTimeGet( |
| 682 kDetachDurationHistogramPrefix + name_ + kPoolNameSuffix, | 687 kDetachDurationHistogramPrefix + name_ + kPoolNameSuffix, |
| 683 TimeDelta::FromMilliseconds(1), | 688 TimeDelta::FromMilliseconds(1), |
| 684 TimeDelta::FromHours(1), | 689 TimeDelta::FromHours(1), |
| 685 50, | 690 50, |
| 686 HistogramBase::kUmaTargetedHistogramFlag)), | 691 HistogramBase::kUmaTargetedHistogramFlag)), |
| 687 // Mimics the UMA_HISTOGRAM_COUNTS_1000 macro. A SchedulerWorker is | 692 // Mimics the UMA_HISTOGRAM_COUNTS_1000 macro. When a worker runs more |
| 688 // expected to run between zero and a few hundreds of tasks before | 693 // than 1000 tasks before detaching, there is no need to know the exact |
| 689 // detaching. When it runs more than 1000 tasks, there is no need to know | 694 // number of tasks that ran. |
| 690 // the exact number of tasks that ran. | |
| 691 num_tasks_before_detach_histogram_(Histogram::FactoryGet( | 695 num_tasks_before_detach_histogram_(Histogram::FactoryGet( |
| 692 kNumTasksBeforeDetachHistogramPrefix + name_ + kPoolNameSuffix, | 696 kNumTasksBeforeDetachHistogramPrefix + name_ + kPoolNameSuffix, |
| 693 1, | 697 1, |
| 694 1000, | 698 1000, |
| 695 50, | 699 50, |
| 696 HistogramBase::kUmaTargetedHistogramFlag)), | 700 HistogramBase::kUmaTargetedHistogramFlag)), |
| 697 // Mimics the UMA_HISTOGRAM_COUNTS_100 macro. A SchedulerWorker is | 701 // Mimics the UMA_HISTOGRAM_COUNTS_100 macro. A SchedulerWorker is |
| 698 // expected to run between zero and a few tens of tasks between waits. | 702 // expected to run between zero and a few tens of tasks between waits. |
| 699 // When it runs more than 100 tasks, there is no need to know the exact | 703 // When it runs more than 100 tasks, there is no need to know the exact |
| 700 // number of tasks that ran. | 704 // number of tasks that ran. |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 786 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); | 790 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); |
| 787 idle_workers_stack_.Remove(worker); | 791 idle_workers_stack_.Remove(worker); |
| 788 } | 792 } |
| 789 | 793 |
| 790 bool SchedulerWorkerPoolImpl::CanWorkerDetachForTesting() { | 794 bool SchedulerWorkerPoolImpl::CanWorkerDetachForTesting() { |
| 791 return !worker_detachment_disallowed_.IsSet(); | 795 return !worker_detachment_disallowed_.IsSet(); |
| 792 } | 796 } |
| 793 | 797 |
| 794 } // namespace internal | 798 } // namespace internal |
| 795 } // namespace base | 799 } // namespace base |
| OLD | NEW |