| 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 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 185 | 185 |
| 186 // Number of tasks executed since the last time the | 186 // Number of tasks executed since the last time the |
| 187 // TaskScheduler.NumTasksBeforeDetach histogram was recorded. | 187 // TaskScheduler.NumTasksBeforeDetach histogram was recorded. |
| 188 size_t num_tasks_since_last_detach_ = 0; | 188 size_t num_tasks_since_last_detach_ = 0; |
| 189 | 189 |
| 190 const int index_; | 190 const int index_; |
| 191 | 191 |
| 192 DISALLOW_COPY_AND_ASSIGN(SchedulerWorkerDelegateImpl); | 192 DISALLOW_COPY_AND_ASSIGN(SchedulerWorkerDelegateImpl); |
| 193 }; | 193 }; |
| 194 | 194 |
| 195 SchedulerWorkerPoolImpl::SchedulerWorkerPoolImpl( |
| 196 const std::string& name, |
| 197 ThreadPriority priority_hint, |
| 198 ReEnqueueSequenceCallback re_enqueue_sequence_callback, |
| 199 TaskTracker* task_tracker, |
| 200 DelayedTaskManager* delayed_task_manager) |
| 201 : name_(name), |
| 202 priority_hint_(priority_hint), |
| 203 re_enqueue_sequence_callback_(std::move(re_enqueue_sequence_callback)), |
| 204 idle_workers_stack_lock_(shared_priority_queue_.container_lock()), |
| 205 idle_workers_stack_cv_for_testing_( |
| 206 idle_workers_stack_lock_.CreateConditionVariable()), |
| 207 join_for_testing_returned_(WaitableEvent::ResetPolicy::MANUAL, |
| 208 WaitableEvent::InitialState::NOT_SIGNALED), |
| 209 #if DCHECK_IS_ON() |
| 210 workers_created_(WaitableEvent::ResetPolicy::MANUAL, |
| 211 WaitableEvent::InitialState::NOT_SIGNALED), |
| 212 #endif |
| 213 // Mimics the UMA_HISTOGRAM_LONG_TIMES macro. |
| 214 detach_duration_histogram_(Histogram::FactoryTimeGet( |
| 215 kDetachDurationHistogramPrefix + name_ + kPoolNameSuffix, |
| 216 TimeDelta::FromMilliseconds(1), |
| 217 TimeDelta::FromHours(1), |
| 218 50, |
| 219 HistogramBase::kUmaTargetedHistogramFlag)), |
| 220 // Mimics the UMA_HISTOGRAM_COUNTS_1000 macro. When a worker runs more |
| 221 // than 1000 tasks before detaching, there is no need to know the exact |
| 222 // number of tasks that ran. |
| 223 num_tasks_before_detach_histogram_(Histogram::FactoryGet( |
| 224 kNumTasksBeforeDetachHistogramPrefix + name_ + kPoolNameSuffix, |
| 225 1, |
| 226 1000, |
| 227 50, |
| 228 HistogramBase::kUmaTargetedHistogramFlag)), |
| 229 // Mimics the UMA_HISTOGRAM_COUNTS_100 macro. A SchedulerWorker is |
| 230 // expected to run between zero and a few tens of tasks between waits. |
| 231 // When it runs more than 100 tasks, there is no need to know the exact |
| 232 // number of tasks that ran. |
| 233 num_tasks_between_waits_histogram_(Histogram::FactoryGet( |
| 234 kNumTasksBetweenWaitsHistogramPrefix + name_ + kPoolNameSuffix, |
| 235 1, |
| 236 100, |
| 237 50, |
| 238 HistogramBase::kUmaTargetedHistogramFlag)), |
| 239 task_tracker_(task_tracker), |
| 240 delayed_task_manager_(delayed_task_manager) { |
| 241 DCHECK(task_tracker_); |
| 242 DCHECK(delayed_task_manager_); |
| 243 } |
| 244 |
| 245 void SchedulerWorkerPoolImpl::Start(const SchedulerWorkerPoolParams& params) { |
| 246 suggested_reclaim_time_ = params.suggested_reclaim_time(); |
| 247 |
| 248 std::vector<SchedulerWorker*> workers_to_wake_up; |
| 249 |
| 250 { |
| 251 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); |
| 252 |
| 253 #if DCHECK_IS_ON() |
| 254 DCHECK(!workers_created_.IsSignaled()); |
| 255 #endif |
| 256 |
| 257 DCHECK(workers_.empty()); |
| 258 workers_.resize(params.max_threads()); |
| 259 |
| 260 // The number of workers created alive is |num_wake_ups_before_start_|, plus |
| 261 // one if the standby thread policy is ONE (in order to start with one alive |
| 262 // idle worker). |
| 263 const int num_alive_workers = |
| 264 num_wake_ups_before_start_ + |
| 265 (params.standby_thread_policy() == |
| 266 SchedulerWorkerPoolParams::StandbyThreadPolicy::ONE |
| 267 ? 1 |
| 268 : 0); |
| 269 |
| 270 // Create workers in reverse order of index so that the worker with the |
| 271 // highest index is at the bottom of the idle stack. |
| 272 for (int index = params.max_threads() - 1; index >= 0; --index) { |
| 273 const SchedulerWorker::InitialState initial_state = |
| 274 index < num_alive_workers ? SchedulerWorker::InitialState::ALIVE |
| 275 : SchedulerWorker::InitialState::DETACHED; |
| 276 scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create( |
| 277 params.priority_hint(), |
| 278 MakeUnique<SchedulerWorkerDelegateImpl>( |
| 279 this, re_enqueue_sequence_callback_, index), |
| 280 task_tracker_, initial_state, params.backward_compatibility()); |
| 281 if (!worker) |
| 282 break; |
| 283 |
| 284 if (index < num_wake_ups_before_start_) |
| 285 workers_to_wake_up.push_back(worker.get()); |
| 286 else |
| 287 idle_workers_stack_.Push(worker.get()); |
| 288 |
| 289 workers_[index] = std::move(worker); |
| 290 } |
| 291 |
| 292 #if DCHECK_IS_ON() |
| 293 workers_created_.Signal(); |
| 294 #endif |
| 295 |
| 296 CHECK(!workers_.empty()); |
| 297 } |
| 298 |
| 299 for (SchedulerWorker* worker : workers_to_wake_up) |
| 300 worker->WakeUp(); |
| 301 } |
| 302 |
| 195 SchedulerWorkerPoolImpl::~SchedulerWorkerPoolImpl() { | 303 SchedulerWorkerPoolImpl::~SchedulerWorkerPoolImpl() { |
| 196 // SchedulerWorkerPool should never be deleted in production unless its | 304 // SchedulerWorkerPool should never be deleted in production unless its |
| 197 // initialization failed. | 305 // initialization failed. |
| 198 DCHECK(join_for_testing_returned_.IsSignaled() || workers_.empty()); | 306 DCHECK(join_for_testing_returned_.IsSignaled() || workers_.empty()); |
| 199 } | 307 } |
| 200 | 308 |
| 201 // static | |
| 202 std::unique_ptr<SchedulerWorkerPoolImpl> SchedulerWorkerPoolImpl::Create( | |
| 203 const SchedulerWorkerPoolParams& params, | |
| 204 const ReEnqueueSequenceCallback& re_enqueue_sequence_callback, | |
| 205 TaskTracker* task_tracker, | |
| 206 DelayedTaskManager* delayed_task_manager) { | |
| 207 auto worker_pool = WrapUnique( | |
| 208 new SchedulerWorkerPoolImpl(params, task_tracker, delayed_task_manager)); | |
| 209 if (worker_pool->Initialize(params, re_enqueue_sequence_callback)) | |
| 210 return worker_pool; | |
| 211 return nullptr; | |
| 212 } | |
| 213 | |
| 214 scoped_refptr<TaskRunner> SchedulerWorkerPoolImpl::CreateTaskRunnerWithTraits( | 309 scoped_refptr<TaskRunner> SchedulerWorkerPoolImpl::CreateTaskRunnerWithTraits( |
| 215 const TaskTraits& traits) { | 310 const TaskTraits& traits) { |
| 216 return make_scoped_refptr(new SchedulerParallelTaskRunner(traits, this)); | 311 return make_scoped_refptr(new SchedulerParallelTaskRunner(traits, this)); |
| 217 } | 312 } |
| 218 | 313 |
| 219 scoped_refptr<SequencedTaskRunner> | 314 scoped_refptr<SequencedTaskRunner> |
| 220 SchedulerWorkerPoolImpl::CreateSequencedTaskRunnerWithTraits( | 315 SchedulerWorkerPoolImpl::CreateSequencedTaskRunnerWithTraits( |
| 221 const TaskTraits& traits) { | 316 const TaskTraits& traits) { |
| 222 return make_scoped_refptr(new SchedulerSequencedTaskRunner(traits, this)); | 317 return make_scoped_refptr(new SchedulerSequencedTaskRunner(traits, this)); |
| 223 } | 318 } |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 293 } | 388 } |
| 294 } | 389 } |
| 295 | 390 |
| 296 void SchedulerWorkerPoolImpl::GetHistograms( | 391 void SchedulerWorkerPoolImpl::GetHistograms( |
| 297 std::vector<const HistogramBase*>* histograms) const { | 392 std::vector<const HistogramBase*>* histograms) const { |
| 298 histograms->push_back(detach_duration_histogram_); | 393 histograms->push_back(detach_duration_histogram_); |
| 299 histograms->push_back(num_tasks_between_waits_histogram_); | 394 histograms->push_back(num_tasks_between_waits_histogram_); |
| 300 } | 395 } |
| 301 | 396 |
| 302 int SchedulerWorkerPoolImpl::GetMaxConcurrentTasksDeprecated() const { | 397 int SchedulerWorkerPoolImpl::GetMaxConcurrentTasksDeprecated() const { |
| 398 #if DCHECK_IS_ON() |
| 399 DCHECK(workers_created_.IsSignaled()); |
| 400 #endif |
| 303 return workers_.size(); | 401 return workers_.size(); |
| 304 } | 402 } |
| 305 | 403 |
| 306 void SchedulerWorkerPoolImpl::WaitForAllWorkersIdleForTesting() { | 404 void SchedulerWorkerPoolImpl::WaitForAllWorkersIdleForTesting() { |
| 405 #if DCHECK_IS_ON() |
| 406 DCHECK(workers_created_.IsSignaled()); |
| 407 #endif |
| 307 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); | 408 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); |
| 308 while (idle_workers_stack_.Size() < workers_.size()) | 409 while (idle_workers_stack_.Size() < workers_.size()) |
| 309 idle_workers_stack_cv_for_testing_->Wait(); | 410 idle_workers_stack_cv_for_testing_->Wait(); |
| 310 } | 411 } |
| 311 | 412 |
| 312 void SchedulerWorkerPoolImpl::JoinForTesting() { | 413 void SchedulerWorkerPoolImpl::JoinForTesting() { |
| 414 #if DCHECK_IS_ON() |
| 415 DCHECK(workers_created_.IsSignaled()); |
| 416 #endif |
| 313 DCHECK(!CanWorkerDetachForTesting() || suggested_reclaim_time_.is_max()) | 417 DCHECK(!CanWorkerDetachForTesting() || suggested_reclaim_time_.is_max()) |
| 314 << "Workers can detach during join."; | 418 << "Workers can detach during join."; |
| 315 for (const auto& worker : workers_) | 419 for (const auto& worker : workers_) |
| 316 worker->JoinForTesting(); | 420 worker->JoinForTesting(); |
| 317 | 421 |
| 318 DCHECK(!join_for_testing_returned_.IsSignaled()); | 422 DCHECK(!join_for_testing_returned_.IsSignaled()); |
| 319 join_for_testing_returned_.Signal(); | 423 join_for_testing_returned_.Signal(); |
| 320 } | 424 } |
| 321 | 425 |
| 322 void SchedulerWorkerPoolImpl::DisallowWorkerDetachmentForTesting() { | 426 void SchedulerWorkerPoolImpl::DisallowWorkerDetachmentForTesting() { |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 458 } | 562 } |
| 459 | 563 |
| 460 void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::OnDetach() { | 564 void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::OnDetach() { |
| 461 DCHECK(!did_detach_since_last_get_work_); | 565 DCHECK(!did_detach_since_last_get_work_); |
| 462 outer_->num_tasks_before_detach_histogram_->Add(num_tasks_since_last_detach_); | 566 outer_->num_tasks_before_detach_histogram_->Add(num_tasks_since_last_detach_); |
| 463 num_tasks_since_last_detach_ = 0; | 567 num_tasks_since_last_detach_ = 0; |
| 464 did_detach_since_last_get_work_ = true; | 568 did_detach_since_last_get_work_ = true; |
| 465 last_detach_time_ = TimeTicks::Now(); | 569 last_detach_time_ = TimeTicks::Now(); |
| 466 } | 570 } |
| 467 | 571 |
| 468 SchedulerWorkerPoolImpl::SchedulerWorkerPoolImpl( | 572 void SchedulerWorkerPoolImpl::WakeUpOneWorker() { |
| 469 const SchedulerWorkerPoolParams& params, | 573 SchedulerWorker* worker = nullptr; |
| 470 TaskTracker* task_tracker, | 574 { |
| 471 DelayedTaskManager* delayed_task_manager) | 575 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); |
| 472 : name_(params.name()), | 576 |
| 473 suggested_reclaim_time_(params.suggested_reclaim_time()), | |
| 474 idle_workers_stack_lock_(shared_priority_queue_.container_lock()), | |
| 475 idle_workers_stack_cv_for_testing_( | |
| 476 idle_workers_stack_lock_.CreateConditionVariable()), | |
| 477 join_for_testing_returned_(WaitableEvent::ResetPolicy::MANUAL, | |
| 478 WaitableEvent::InitialState::NOT_SIGNALED), | |
| 479 #if DCHECK_IS_ON() | 577 #if DCHECK_IS_ON() |
| 480 workers_created_(WaitableEvent::ResetPolicy::MANUAL, | 578 DCHECK_EQ(workers_.empty(), !workers_created_.IsSignaled()); |
| 481 WaitableEvent::InitialState::NOT_SIGNALED), | |
| 482 #endif | 579 #endif |
| 483 // Mimics the UMA_HISTOGRAM_LONG_TIMES macro. | |
| 484 detach_duration_histogram_(Histogram::FactoryTimeGet( | |
| 485 kDetachDurationHistogramPrefix + name_ + kPoolNameSuffix, | |
| 486 TimeDelta::FromMilliseconds(1), | |
| 487 TimeDelta::FromHours(1), | |
| 488 50, | |
| 489 HistogramBase::kUmaTargetedHistogramFlag)), | |
| 490 // Mimics the UMA_HISTOGRAM_COUNTS_1000 macro. When a worker runs more | |
| 491 // than 1000 tasks before detaching, there is no need to know the exact | |
| 492 // number of tasks that ran. | |
| 493 num_tasks_before_detach_histogram_(Histogram::FactoryGet( | |
| 494 kNumTasksBeforeDetachHistogramPrefix + name_ + kPoolNameSuffix, | |
| 495 1, | |
| 496 1000, | |
| 497 50, | |
| 498 HistogramBase::kUmaTargetedHistogramFlag)), | |
| 499 // Mimics the UMA_HISTOGRAM_COUNTS_100 macro. A SchedulerWorker is | |
| 500 // expected to run between zero and a few tens of tasks between waits. | |
| 501 // When it runs more than 100 tasks, there is no need to know the exact | |
| 502 // number of tasks that ran. | |
| 503 num_tasks_between_waits_histogram_(Histogram::FactoryGet( | |
| 504 kNumTasksBetweenWaitsHistogramPrefix + name_ + kPoolNameSuffix, | |
| 505 1, | |
| 506 100, | |
| 507 50, | |
| 508 HistogramBase::kUmaTargetedHistogramFlag)), | |
| 509 task_tracker_(task_tracker), | |
| 510 delayed_task_manager_(delayed_task_manager) { | |
| 511 DCHECK(task_tracker_); | |
| 512 DCHECK(delayed_task_manager_); | |
| 513 } | |
| 514 | 580 |
| 515 bool SchedulerWorkerPoolImpl::Initialize( | 581 if (workers_.empty()) |
| 516 const SchedulerWorkerPoolParams& params, | 582 ++num_wake_ups_before_start_; |
| 517 const ReEnqueueSequenceCallback& re_enqueue_sequence_callback) { | 583 else |
| 518 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); | 584 worker = idle_workers_stack_.Pop(); |
| 519 | |
| 520 DCHECK(workers_.empty()); | |
| 521 workers_.resize(params.max_threads()); | |
| 522 | |
| 523 // Create workers and push them to the idle stack in reverse order of index. | |
| 524 // This ensures that they are woken up in order of index and that the ALIVE | |
| 525 // worker is on top of the stack. | |
| 526 for (int index = params.max_threads() - 1; index >= 0; --index) { | |
| 527 const bool is_standby_lazy = | |
| 528 params.standby_thread_policy() == | |
| 529 SchedulerWorkerPoolParams::StandbyThreadPolicy::LAZY; | |
| 530 const SchedulerWorker::InitialState initial_state = | |
| 531 (index == 0 && !is_standby_lazy) | |
| 532 ? SchedulerWorker::InitialState::ALIVE | |
| 533 : SchedulerWorker::InitialState::DETACHED; | |
| 534 scoped_refptr<SchedulerWorker> worker = SchedulerWorker::Create( | |
| 535 params.priority_hint(), | |
| 536 MakeUnique<SchedulerWorkerDelegateImpl>( | |
| 537 this, re_enqueue_sequence_callback, index), | |
| 538 task_tracker_, initial_state, params.backward_compatibility()); | |
| 539 if (!worker) | |
| 540 break; | |
| 541 idle_workers_stack_.Push(worker.get()); | |
| 542 workers_[index] = std::move(worker); | |
| 543 } | 585 } |
| 544 | 586 |
| 545 #if DCHECK_IS_ON() | |
| 546 workers_created_.Signal(); | |
| 547 #endif | |
| 548 | |
| 549 return !workers_.empty(); | |
| 550 } | |
| 551 | |
| 552 void SchedulerWorkerPoolImpl::WakeUpOneWorker() { | |
| 553 SchedulerWorker* worker; | |
| 554 { | |
| 555 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); | |
| 556 worker = idle_workers_stack_.Pop(); | |
| 557 } | |
| 558 if (worker) | 587 if (worker) |
| 559 worker->WakeUp(); | 588 worker->WakeUp(); |
| 560 // TODO(robliao): Honor StandbyThreadPolicy::ONE here and consider adding | 589 // TODO(robliao): Honor StandbyThreadPolicy::ONE here and consider adding |
| 561 // hysteresis to the CanDetach check. See https://crbug.com/666041. | 590 // hysteresis to the CanDetach check. See https://crbug.com/666041. |
| 562 } | 591 } |
| 563 | 592 |
| 564 void SchedulerWorkerPoolImpl::AddToIdleWorkersStack( | 593 void SchedulerWorkerPoolImpl::AddToIdleWorkersStack( |
| 565 SchedulerWorker* worker) { | 594 SchedulerWorker* worker) { |
| 566 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); | 595 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); |
| 567 // Detachment may cause multiple attempts to add because the delegate cannot | 596 // Detachment may cause multiple attempts to add because the delegate cannot |
| (...skipping 19 matching lines...) Expand all Loading... |
| 587 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); | 616 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); |
| 588 idle_workers_stack_.Remove(worker); | 617 idle_workers_stack_.Remove(worker); |
| 589 } | 618 } |
| 590 | 619 |
| 591 bool SchedulerWorkerPoolImpl::CanWorkerDetachForTesting() { | 620 bool SchedulerWorkerPoolImpl::CanWorkerDetachForTesting() { |
| 592 return !worker_detachment_disallowed_.IsSet(); | 621 return !worker_detachment_disallowed_.IsSet(); |
| 593 } | 622 } |
| 594 | 623 |
| 595 } // namespace internal | 624 } // namespace internal |
| 596 } // namespace base | 625 } // namespace base |
| OLD | NEW |