Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(519)

Side by Side Diff: base/task_scheduler/scheduler_worker_pool_impl_unittest.cc

Issue 2362743002: Add TaskScheduler.NumTasksBetweenWaits.[pool] histogram. (Closed)
Patch Set: CR gab/robliao #7-10 Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 <memory> 9 #include <memory>
10 #include <unordered_set> 10 #include <unordered_set>
11 #include <vector> 11 #include <vector>
12 12
13 #include "base/atomicops.h" 13 #include "base/atomicops.h"
14 #include "base/bind.h" 14 #include "base/bind.h"
15 #include "base/bind_helpers.h" 15 #include "base/bind_helpers.h"
16 #include "base/callback.h" 16 #include "base/callback.h"
17 #include "base/macros.h" 17 #include "base/macros.h"
18 #include "base/memory/ptr_util.h" 18 #include "base/memory/ptr_util.h"
19 #include "base/memory/ref_counted.h" 19 #include "base/memory/ref_counted.h"
20 #include "base/metrics/histogram.h"
21 #include "base/metrics/histogram_samples.h"
22 #include "base/metrics/statistics_recorder.h"
20 #include "base/synchronization/condition_variable.h" 23 #include "base/synchronization/condition_variable.h"
21 #include "base/synchronization/lock.h" 24 #include "base/synchronization/lock.h"
22 #include "base/synchronization/waitable_event.h" 25 #include "base/synchronization/waitable_event.h"
23 #include "base/task_runner.h" 26 #include "base/task_runner.h"
24 #include "base/task_scheduler/delayed_task_manager.h" 27 #include "base/task_scheduler/delayed_task_manager.h"
25 #include "base/task_scheduler/scheduler_worker_pool_params.h" 28 #include "base/task_scheduler/scheduler_worker_pool_params.h"
26 #include "base/task_scheduler/sequence.h" 29 #include "base/task_scheduler/sequence.h"
27 #include "base/task_scheduler/sequence_sort_key.h" 30 #include "base/task_scheduler/sequence_sort_key.h"
28 #include "base/task_scheduler/task_tracker.h" 31 #include "base/task_scheduler/task_tracker.h"
29 #include "base/task_scheduler/test_task_factory.h" 32 #include "base/task_scheduler/test_task_factory.h"
30 #include "base/test/gtest_util.h" 33 #include "base/test/gtest_util.h"
31 #include "base/threading/platform_thread.h" 34 #include "base/threading/platform_thread.h"
32 #include "base/threading/simple_thread.h" 35 #include "base/threading/simple_thread.h"
33 #include "base/threading/thread_checker_impl.h" 36 #include "base/threading/thread_checker_impl.h"
34 #include "base/threading/thread_local_storage.h" 37 #include "base/threading/thread_local_storage.h"
35 #include "base/threading/thread_restrictions.h" 38 #include "base/threading/thread_restrictions.h"
36 #include "base/time/time.h" 39 #include "base/time/time.h"
37 #include "testing/gtest/include/gtest/gtest.h" 40 #include "testing/gtest/include/gtest/gtest.h"
38 41
39 namespace base { 42 namespace base {
40 namespace internal { 43 namespace internal {
41 namespace { 44 namespace {
42 45
43 const size_t kNumWorkersInWorkerPool = 4; 46 constexpr size_t kNumWorkersInWorkerPool = 4;
44 const size_t kNumThreadsPostingTasks = 4; 47 constexpr size_t kNumThreadsPostingTasks = 4;
45 const size_t kNumTasksPostedPerThread = 150; 48 constexpr size_t kNumTasksPostedPerThread = 150;
49 // This can't be lower because Windows' WaitableEvent wakes up too early when a
50 // small timeout is used. This results in many spurious wake ups before a worker
51 // is allowed to detach.
46 constexpr TimeDelta kReclaimTimeForDetachTests = 52 constexpr TimeDelta kReclaimTimeForDetachTests =
47 TimeDelta::FromMilliseconds(10); 53 TimeDelta::FromMilliseconds(200);
54 constexpr TimeDelta kExtraTimeToWaitForDetach =
55 TimeDelta::FromMilliseconds(200);
48 56
49 using IORestriction = SchedulerWorkerPoolParams::IORestriction; 57 using IORestriction = SchedulerWorkerPoolParams::IORestriction;
50 58
51 class TestDelayedTaskManager : public DelayedTaskManager { 59 class TestDelayedTaskManager : public DelayedTaskManager {
52 public: 60 public:
53 TestDelayedTaskManager() : DelayedTaskManager(Bind(&DoNothing)) {} 61 TestDelayedTaskManager() : DelayedTaskManager(Bind(&DoNothing)) {}
54 62
55 void SetCurrentTime(TimeTicks now) { now_ = now; } 63 void SetCurrentTime(TimeTicks now) { now_ = now; }
56 64
57 // DelayedTaskManager: 65 // DelayedTaskManager:
58 TimeTicks Now() const override { return now_; } 66 TimeTicks Now() const override { return now_; }
59 67
60 private: 68 private:
61 TimeTicks now_ = TimeTicks::Now(); 69 TimeTicks now_ = TimeTicks::Now();
62 70
63 DISALLOW_COPY_AND_ASSIGN(TestDelayedTaskManager); 71 DISALLOW_COPY_AND_ASSIGN(TestDelayedTaskManager);
64 }; 72 };
65 73
66 class TaskSchedulerWorkerPoolImplTest 74 class TaskSchedulerWorkerPoolImplTest
67 : public testing::TestWithParam<ExecutionMode> { 75 : public testing::TestWithParam<ExecutionMode> {
68 protected: 76 protected:
69 TaskSchedulerWorkerPoolImplTest() = default; 77 TaskSchedulerWorkerPoolImplTest() = default;
70 78
71 void SetUp() override { 79 void SetUp() override {
72 InitializeWorkerPool(TimeDelta::Max()); 80 InitializeWorkerPool(TimeDelta::Max(), kNumWorkersInWorkerPool);
73 } 81 }
74 82
75 void TearDown() override { 83 void TearDown() override {
76 worker_pool_->WaitForAllWorkersIdleForTesting(); 84 worker_pool_->WaitForAllWorkersIdleForTesting();
77 worker_pool_->JoinForTesting(); 85 worker_pool_->JoinForTesting();
78 } 86 }
79 87
80 void InitializeWorkerPool(const TimeDelta& suggested_reclaim_time) { 88 void InitializeWorkerPool(const TimeDelta& suggested_reclaim_time,
89 size_t num_workers) {
81 worker_pool_ = SchedulerWorkerPoolImpl::Create( 90 worker_pool_ = SchedulerWorkerPoolImpl::Create(
82 SchedulerWorkerPoolParams("TestWorkerPoolWithFileIO", 91 SchedulerWorkerPoolParams("TestWorkerPool", ThreadPriority::NORMAL,
83 ThreadPriority::NORMAL, 92 IORestriction::ALLOWED, num_workers,
84 IORestriction::ALLOWED,
85 kNumWorkersInWorkerPool,
86 suggested_reclaim_time), 93 suggested_reclaim_time),
87 Bind(&TaskSchedulerWorkerPoolImplTest::ReEnqueueSequenceCallback, 94 Bind(&TaskSchedulerWorkerPoolImplTest::ReEnqueueSequenceCallback,
88 Unretained(this)), 95 Unretained(this)),
89 &task_tracker_, &delayed_task_manager_); 96 &task_tracker_, &delayed_task_manager_);
90 ASSERT_TRUE(worker_pool_); 97 ASSERT_TRUE(worker_pool_);
91 } 98 }
92 99
93 std::unique_ptr<SchedulerWorkerPoolImpl> worker_pool_; 100 std::unique_ptr<SchedulerWorkerPoolImpl> worker_pool_;
94 101
95 TaskTracker task_tracker_; 102 TaskTracker task_tracker_;
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after
430 void InitializeThreadChecker() { 437 void InitializeThreadChecker() {
431 thread_checker_.reset(new ThreadCheckerImpl()); 438 thread_checker_.reset(new ThreadCheckerImpl());
432 } 439 }
433 440
434 void CheckValidThread() { 441 void CheckValidThread() {
435 EXPECT_TRUE(thread_checker_->CalledOnValidThread()); 442 EXPECT_TRUE(thread_checker_->CalledOnValidThread());
436 } 443 }
437 444
438 protected: 445 protected:
439 void SetUp() override { 446 void SetUp() override {
440 InitializeWorkerPool(kReclaimTimeForDetachTests); 447 InitializeWorkerPool(kReclaimTimeForDetachTests, kNumWorkersInWorkerPool);
441 } 448 }
442 449
443 TaskSchedulerWorkerPoolSingleThreadedTest() = default; 450 TaskSchedulerWorkerPoolSingleThreadedTest() = default;
444 451
445 private: 452 private:
446 std::unique_ptr<ThreadCheckerImpl> thread_checker_; 453 std::unique_ptr<ThreadCheckerImpl> thread_checker_;
447 454
448 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolSingleThreadedTest); 455 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolSingleThreadedTest);
449 }; 456 };
450 457
(...skipping 11 matching lines...) Expand all
462 Bind(&TaskSchedulerWorkerPoolSingleThreadedTest::InitializeThreadChecker, 469 Bind(&TaskSchedulerWorkerPoolSingleThreadedTest::InitializeThreadChecker,
463 Unretained(this))); 470 Unretained(this)));
464 WaitableEvent task_waiter(WaitableEvent::ResetPolicy::AUTOMATIC, 471 WaitableEvent task_waiter(WaitableEvent::ResetPolicy::AUTOMATIC,
465 WaitableEvent::InitialState::NOT_SIGNALED); 472 WaitableEvent::InitialState::NOT_SIGNALED);
466 single_thread_task_runner->PostTask( 473 single_thread_task_runner->PostTask(
467 FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_waiter))); 474 FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_waiter)));
468 task_waiter.Wait(); 475 task_waiter.Wait();
469 worker_pool_->WaitForAllWorkersIdleForTesting(); 476 worker_pool_->WaitForAllWorkersIdleForTesting();
470 477
471 // Give the worker pool a chance to reclaim its threads. 478 // Give the worker pool a chance to reclaim its threads.
472 PlatformThread::Sleep( 479 PlatformThread::Sleep(kReclaimTimeForDetachTests + kExtraTimeToWaitForDetach);
473 kReclaimTimeForDetachTests + TimeDelta::FromMilliseconds(200));
474 480
475 worker_pool_->DisallowWorkerDetachmentForTesting(); 481 worker_pool_->DisallowWorkerDetachmentForTesting();
476 482
477 single_thread_task_runner->PostTask( 483 single_thread_task_runner->PostTask(
478 FROM_HERE, 484 FROM_HERE,
479 Bind(&TaskSchedulerWorkerPoolSingleThreadedTest::CheckValidThread, 485 Bind(&TaskSchedulerWorkerPoolSingleThreadedTest::CheckValidThread,
480 Unretained(this))); 486 Unretained(this)));
481 single_thread_task_runner->PostTask( 487 single_thread_task_runner->PostTask(
482 FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_waiter))); 488 FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_waiter)));
483 task_waiter.Wait(); 489 task_waiter.Wait();
(...skipping 18 matching lines...) Expand all
502 count_waiter->Signal(); 508 count_waiter->Signal();
503 waiter_.Wait(); 509 waiter_.Wait();
504 } 510 }
505 511
506 protected: 512 protected:
507 TaskSchedulerWorkerPoolCheckTlsReuse() : 513 TaskSchedulerWorkerPoolCheckTlsReuse() :
508 waiter_(WaitableEvent::ResetPolicy::MANUAL, 514 waiter_(WaitableEvent::ResetPolicy::MANUAL,
509 WaitableEvent::InitialState::NOT_SIGNALED) {} 515 WaitableEvent::InitialState::NOT_SIGNALED) {}
510 516
511 void SetUp() override { 517 void SetUp() override {
512 InitializeWorkerPool(kReclaimTimeForDetachTests); 518 InitializeWorkerPool(kReclaimTimeForDetachTests, kNumWorkersInWorkerPool);
513 } 519 }
514 520
515 subtle::Atomic32 zero_tls_values_ = 0; 521 subtle::Atomic32 zero_tls_values_ = 0;
516 522
517 WaitableEvent waiter_; 523 WaitableEvent waiter_;
518 524
519 private: 525 private:
520 ThreadLocalStorage::Slot slot_; 526 ThreadLocalStorage::Slot slot_;
521 527
522 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolCheckTlsReuse); 528 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolCheckTlsReuse);
(...skipping 18 matching lines...) Expand all
541 } 547 }
542 548
543 // Release tasks waiting on |waiter_|. 549 // Release tasks waiting on |waiter_|.
544 waiter_.Signal(); 550 waiter_.Signal();
545 worker_pool_->WaitForAllWorkersIdleForTesting(); 551 worker_pool_->WaitForAllWorkersIdleForTesting();
546 552
547 // All threads should be done running by now, so reset for the next phase. 553 // All threads should be done running by now, so reset for the next phase.
548 waiter_.Reset(); 554 waiter_.Reset();
549 555
550 // Give the worker pool a chance to detach its threads. 556 // Give the worker pool a chance to detach its threads.
551 PlatformThread::Sleep( 557 PlatformThread::Sleep(kReclaimTimeForDetachTests + kExtraTimeToWaitForDetach);
552 kReclaimTimeForDetachTests + TimeDelta::FromMilliseconds(200));
553 558
554 worker_pool_->DisallowWorkerDetachmentForTesting(); 559 worker_pool_->DisallowWorkerDetachmentForTesting();
555 560
556 // Saturate and count the threads that do not have the magic TLS value. If the 561 // Saturate and count the threads that do not have the magic TLS value. If the
557 // value is not there, that means we're at a new thread. 562 // value is not there, that means we're at a new thread.
558 std::vector<std::unique_ptr<WaitableEvent>> count_waiters; 563 std::vector<std::unique_ptr<WaitableEvent>> count_waiters;
559 for (auto& factory : factories) { 564 for (auto& factory : factories) {
560 count_waiters.push_back(WrapUnique(new WaitableEvent( 565 count_waiters.push_back(WrapUnique(new WaitableEvent(
561 WaitableEvent::ResetPolicy::MANUAL, 566 WaitableEvent::ResetPolicy::MANUAL,
562 WaitableEvent::InitialState::NOT_SIGNALED))); 567 WaitableEvent::InitialState::NOT_SIGNALED)));
563 ASSERT_TRUE(factory->PostTask( 568 ASSERT_TRUE(factory->PostTask(
564 PostNestedTask::NO, 569 PostNestedTask::NO,
565 Bind(&TaskSchedulerWorkerPoolCheckTlsReuse::CountZeroTlsValuesAndWait, 570 Bind(&TaskSchedulerWorkerPoolCheckTlsReuse::CountZeroTlsValuesAndWait,
566 Unretained(this), 571 Unretained(this),
567 count_waiters.back().get()))); 572 count_waiters.back().get())));
568 factory->WaitForAllTasksToRun(); 573 factory->WaitForAllTasksToRun();
569 } 574 }
570 575
571 // Wait for all counters to complete. 576 // Wait for all counters to complete.
572 for (auto& count_waiter : count_waiters) 577 for (auto& count_waiter : count_waiters)
573 count_waiter->Wait(); 578 count_waiter->Wait();
574 579
575 EXPECT_GT(subtle::NoBarrier_Load(&zero_tls_values_), 0); 580 EXPECT_GT(subtle::NoBarrier_Load(&zero_tls_values_), 0);
576 581
577 // Release tasks waiting on |waiter_|. 582 // Release tasks waiting on |waiter_|.
578 waiter_.Signal(); 583 waiter_.Signal();
579 } 584 }
580 585
586 namespace {
587
588 class TaskSchedulerWorkerPoolHistogramTest
589 : public TaskSchedulerWorkerPoolImplTest {
590 public:
591 TaskSchedulerWorkerPoolHistogramTest() = default;
592
593 protected:
594 void SetUp() override {}
595
596 void TearDown() override { worker_pool_->JoinForTesting(); }
597
598 private:
599 std::unique_ptr<StatisticsRecorder> statistics_recorder_ =
600 StatisticsRecorder::CreateTemporaryForTesting();
601
602 protected:
603 HistogramBase* const num_tasks_between_waits_histogram_ =
604 Histogram::FactoryGet(
robliao 2016/09/23 19:44:19 Abstract this as a static call on the SchedulerWor
fdoray 2016/09/26 12:22:05 FRIEND_TEST_ALL_PREFIXES in SchedulerWorkerPoolImp
robliao 2016/09/26 17:19:01 I'd say go for the static method. It's relatively
fdoray 2016/09/26 19:14:36 WDYT of a public accessor for |num_tasks_between_w
robliao 2016/09/26 19:26:38 A public accessor sounds fine too. Accomplishes a
fdoray 2016/09/26 19:32:38 Done.
605 "TaskScheduler.NumTasksBetweenWaits.TestWorkerPoolPool",
606 1,
607 100,
608 50,
609 HistogramBase::kUmaTargetedHistogramFlag);
610
611 private:
612 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolHistogramTest);
613 };
614
615 } // namespace
616
617 static void IncrementCountAndWaitForEvent() {}
robliao 2016/09/23 19:44:19 Stray function?
fdoray 2016/09/26 12:22:05 Done.
618
619 TEST_F(TaskSchedulerWorkerPoolHistogramTest, NumTasksBetweenWaits) {
620 WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
621 WaitableEvent::InitialState::NOT_SIGNALED);
622 InitializeWorkerPool(TimeDelta::Max(), kNumWorkersInWorkerPool);
623 auto task_runner = worker_pool_->CreateTaskRunnerWithTraits(
624 TaskTraits(), ExecutionMode::SEQUENCED);
625
626 // Post a task.
627 task_runner->PostTask(FROM_HERE,
628 Bind(&WaitableEvent::Wait, Unretained(&event)));
629
630 // Post 2 more tasks while the first task hasn't completed its execution. It
631 // is guaranteed that these tasks will run immediately after the first task,
632 // without allowing the worker to sleep.
633 task_runner->PostTask(FROM_HERE, Bind(&DoNothing));
634 task_runner->PostTask(FROM_HERE, Bind(&DoNothing));
635
636 // Allow tasks to run and wait until the SchedulerWorker is idle.
637 event.Signal();
638 worker_pool_->WaitForAllWorkersIdleForTesting();
639
640 // Wake up the SchedulerWorker that just became idle by posting a task and
641 // wait until it becomes idle again. The SchedulerWorker should record the
642 // TaskScheduler.NumTasksBetweenWaits.* histogram on wake up.
643 task_runner->PostTask(FROM_HERE, Bind(&DoNothing));
644 worker_pool_->WaitForAllWorkersIdleForTesting();
645
646 // Verify that counts were recorded to the histogram as expected.
647 EXPECT_EQ(0,
648 num_tasks_between_waits_histogram_->SnapshotDelta()->GetCount(0));
649 EXPECT_EQ(1,
650 num_tasks_between_waits_histogram_->SnapshotDelta()->GetCount(3));
651 EXPECT_EQ(0,
652 num_tasks_between_waits_histogram_->SnapshotDelta()->GetCount(10));
653 }
654
655 static void SignalAndWaitEvent(WaitableEvent* signal_event,
robliao 2016/09/23 19:44:19 Wrap in anonymous namespace, remove static.
fdoray 2016/09/26 12:22:05 Done.
656 WaitableEvent* wait_event) {
657 signal_event->Signal();
658 wait_event->Wait();
659 }
660
661 TEST_F(TaskSchedulerWorkerPoolHistogramTest, NumTasksBetweenWaitsWithDetach) {
662 WaitableEvent tasks_can_exit_event(WaitableEvent::ResetPolicy::MANUAL,
663 WaitableEvent::InitialState::NOT_SIGNALED);
664 InitializeWorkerPool(kReclaimTimeForDetachTests, kNumWorkersInWorkerPool);
665 auto task_runner = worker_pool_->CreateTaskRunnerWithTraits(
666 TaskTraits(), ExecutionMode::PARALLEL);
667
668 // Post tasks to saturate the pool.
669 std::vector<std::unique_ptr<WaitableEvent>> task_started_events;
670 for (int i = 0; i < kNumWorkersInWorkerPool; ++i) {
671 task_started_events.push_back(
672 MakeUnique<WaitableEvent>(WaitableEvent::ResetPolicy::MANUAL,
673 WaitableEvent::InitialState::NOT_SIGNALED));
674 task_runner->PostTask(
675 FROM_HERE,
676 Bind(&SignalAndWaitEvent, Unretained(task_started_events.back().get()),
677 Unretained(&tasks_can_exit_event)));
678 }
679 for (const auto& task_started_event : task_started_events)
680 task_started_event->Wait();
681
682 // Allow tasks to complete their execution and wait to allow workers to
683 // detach.
684 tasks_can_exit_event.Signal();
685 worker_pool_->WaitForAllWorkersIdleForTesting();
686 PlatformThread::Sleep(kReclaimTimeForDetachTests + kExtraTimeToWaitForDetach);
687
688 // Wake up SchedulerWorkers by posting tasks. They should record the
689 // TaskScheduler.NumTasksBetweenWaits.* histogram on wake up.
690 tasks_can_exit_event.Reset();
691 task_started_events.clear();
692 for (int i = 0; i < kNumWorkersInWorkerPool; ++i) {
693 task_started_events.push_back(
694 MakeUnique<WaitableEvent>(WaitableEvent::ResetPolicy::MANUAL,
695 WaitableEvent::InitialState::NOT_SIGNALED));
696 task_runner->PostTask(
697 FROM_HERE,
698 Bind(&SignalAndWaitEvent, Unretained(task_started_events.back().get()),
699 Unretained(&tasks_can_exit_event)));
700 }
701 for (const auto& task_started_event : task_started_events)
702 task_started_event->Wait();
703
704 // Verify that counts were recorded to the histogram as expected.
705 // - The "0" bucket has a count of 1 because the SchedulerWorker on top of
706 // the idle stack wasn't allowed to detach when its sleep timeout expired.
707 // Instead, it waited on its WaitableEvent again without running a task.
708 EXPECT_EQ(1,
709 num_tasks_between_waits_histogram_->SnapshotSamples()->GetCount(0));
710 // - The "1" bucket has a count of |kNumWorkersInWorkerPool| because each
711 // SchedulerWorker ran a task before waiting on its WaitableEvent at the
712 // beginning of the test.
713 EXPECT_EQ(kNumWorkersInWorkerPool,
714 num_tasks_between_waits_histogram_->SnapshotSamples()->GetCount(1));
715 EXPECT_EQ(
716 0, num_tasks_between_waits_histogram_->SnapshotSamples()->GetCount(10));
717
718 tasks_can_exit_event.Signal();
719 worker_pool_->DisallowWorkerDetachmentForTesting();
720 }
721
581 } // namespace internal 722 } // namespace internal
582 } // namespace base 723 } // namespace base
OLDNEW
« no previous file with comments | « base/task_scheduler/scheduler_worker_pool_impl.cc ('k') | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698