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

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

Issue 2362743002: Add TaskScheduler.NumTasksBetweenWaits.[pool] histogram. (Closed)
Patch Set: fix test error Created 4 years, 2 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(500);
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 private:
603 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolHistogramTest);
604 };
605
606 } // namespace
607
608 TEST_F(TaskSchedulerWorkerPoolHistogramTest, NumTasksBetweenWaits) {
609 WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
610 WaitableEvent::InitialState::NOT_SIGNALED);
611 InitializeWorkerPool(TimeDelta::Max(), kNumWorkersInWorkerPool);
612 auto task_runner = worker_pool_->CreateTaskRunnerWithTraits(
613 TaskTraits(), ExecutionMode::SEQUENCED);
614
615 // Post a task.
616 task_runner->PostTask(FROM_HERE,
617 Bind(&WaitableEvent::Wait, Unretained(&event)));
618
619 // Post 2 more tasks while the first task hasn't completed its execution. It
620 // is guaranteed that these tasks will run immediately after the first task,
621 // without allowing the worker to sleep.
622 task_runner->PostTask(FROM_HERE, Bind(&DoNothing));
623 task_runner->PostTask(FROM_HERE, Bind(&DoNothing));
624
625 // Allow tasks to run and wait until the SchedulerWorker is idle.
626 event.Signal();
627 worker_pool_->WaitForAllWorkersIdleForTesting();
628
629 // Wake up the SchedulerWorker that just became idle by posting a task and
630 // wait until it becomes idle again. The SchedulerWorker should record the
631 // TaskScheduler.NumTasksBetweenWaits.* histogram on wake up.
632 task_runner->PostTask(FROM_HERE, Bind(&DoNothing));
633 worker_pool_->WaitForAllWorkersIdleForTesting();
634
635 // Verify that counts were recorded to the histogram as expected.
636 EXPECT_EQ(0, worker_pool_->num_tasks_between_waits_histogram_for_testing()
637 ->SnapshotSamples()
638 ->GetCount(0));
639 EXPECT_EQ(1, worker_pool_->num_tasks_between_waits_histogram_for_testing()
640 ->SnapshotSamples()
641 ->GetCount(3));
642 EXPECT_EQ(0, worker_pool_->num_tasks_between_waits_histogram_for_testing()
643 ->SnapshotSamples()
644 ->GetCount(10));
645 }
646
647 namespace {
648
649 void SignalAndWaitEvent(WaitableEvent* signal_event,
650 WaitableEvent* wait_event) {
651 signal_event->Signal();
652 wait_event->Wait();
653 }
654
655 } // namespace
656
657 TEST_F(TaskSchedulerWorkerPoolHistogramTest, NumTasksBetweenWaitsWithDetach) {
658 WaitableEvent tasks_can_exit_event(WaitableEvent::ResetPolicy::MANUAL,
659 WaitableEvent::InitialState::NOT_SIGNALED);
660 InitializeWorkerPool(kReclaimTimeForDetachTests, kNumWorkersInWorkerPool);
661 auto task_runner = worker_pool_->CreateTaskRunnerWithTraits(
662 TaskTraits(), ExecutionMode::PARALLEL);
663
664 // Post tasks to saturate the pool.
665 std::vector<std::unique_ptr<WaitableEvent>> task_started_events;
666 for (size_t i = 0; i < kNumWorkersInWorkerPool; ++i) {
667 task_started_events.push_back(
668 MakeUnique<WaitableEvent>(WaitableEvent::ResetPolicy::MANUAL,
669 WaitableEvent::InitialState::NOT_SIGNALED));
670 task_runner->PostTask(
671 FROM_HERE,
672 Bind(&SignalAndWaitEvent, Unretained(task_started_events.back().get()),
673 Unretained(&tasks_can_exit_event)));
674 }
675 for (const auto& task_started_event : task_started_events)
676 task_started_event->Wait();
677
678 // Allow tasks to complete their execution and wait to allow workers to
679 // detach.
680 tasks_can_exit_event.Signal();
681 worker_pool_->WaitForAllWorkersIdleForTesting();
682 PlatformThread::Sleep(kReclaimTimeForDetachTests + kExtraTimeToWaitForDetach);
683
684 // Wake up SchedulerWorkers by posting tasks. They should record the
685 // TaskScheduler.NumTasksBetweenWaits.* histogram on wake up.
686 tasks_can_exit_event.Reset();
687 task_started_events.clear();
688 for (size_t i = 0; i < kNumWorkersInWorkerPool; ++i) {
689 task_started_events.push_back(
690 MakeUnique<WaitableEvent>(WaitableEvent::ResetPolicy::MANUAL,
691 WaitableEvent::InitialState::NOT_SIGNALED));
692 task_runner->PostTask(
693 FROM_HERE,
694 Bind(&SignalAndWaitEvent, Unretained(task_started_events.back().get()),
695 Unretained(&tasks_can_exit_event)));
696 }
697 for (const auto& task_started_event : task_started_events)
698 task_started_event->Wait();
699
700 // Verify that counts were recorded to the histogram as expected.
701 // - The "0" bucket has a count of at least 1 because the SchedulerWorker on
702 // top of the idle stack isn't allowed to detach when its sleep timeout
703 // expires. Instead, it waits on its WaitableEvent again without running a
704 // task. The count may be higher than 1 because of spurious wake ups before
705 // the sleep timeout expires.
706 EXPECT_GE(worker_pool_->num_tasks_between_waits_histogram_for_testing()
707 ->SnapshotSamples()
708 ->GetCount(0),
709 1);
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(static_cast<int>(kNumWorkersInWorkerPool),
714 worker_pool_->num_tasks_between_waits_histogram_for_testing()
715 ->SnapshotSamples()
716 ->GetCount(1));
717 EXPECT_EQ(0, worker_pool_->num_tasks_between_waits_histogram_for_testing()
718 ->SnapshotSamples()
719 ->GetCount(10));
720
721 tasks_can_exit_event.Signal();
722 worker_pool_->WaitForAllWorkersIdleForTesting();
723 worker_pool_->DisallowWorkerDetachmentForTesting();
724 }
725
581 } // namespace internal 726 } // namespace internal
582 } // namespace base 727 } // 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