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 |