Chromium Code Reviews| 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_thread.h" | 5 #include "base/task_scheduler/scheduler_worker_thread.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <memory> | 9 #include <memory> |
| 10 #include <vector> | 10 #include <vector> |
| 11 | 11 |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/bind_helpers.h" | 13 #include "base/bind_helpers.h" |
| 14 #include "base/macros.h" | 14 #include "base/macros.h" |
| 15 #include "base/memory/ptr_util.h" | 15 #include "base/memory/ptr_util.h" |
| 16 #include "base/synchronization/condition_variable.h" | 16 #include "base/synchronization/condition_variable.h" |
| 17 #include "base/task_scheduler/scheduler_lock.h" | 17 #include "base/task_scheduler/scheduler_lock.h" |
| 18 #include "base/task_scheduler/scheduler_worker_thread_delegate.h" | |
| 18 #include "base/task_scheduler/sequence.h" | 19 #include "base/task_scheduler/sequence.h" |
| 19 #include "base/task_scheduler/task.h" | 20 #include "base/task_scheduler/task.h" |
| 20 #include "base/task_scheduler/task_tracker.h" | 21 #include "base/task_scheduler/task_tracker.h" |
| 21 #include "testing/gtest/include/gtest/gtest.h" | 22 #include "testing/gtest/include/gtest/gtest.h" |
| 22 | 23 |
| 23 namespace base { | 24 namespace base { |
| 24 namespace internal { | 25 namespace internal { |
| 25 namespace { | 26 namespace { |
| 26 | 27 |
| 27 const size_t kNumSequencesPerTest = 150; | 28 const size_t kNumSequencesPerTest = 150; |
| 28 | 29 |
| 29 class TaskSchedulerWorkerThreadTest : public testing::Test { | 30 class TaskSchedulerWorkerThreadTest : public testing::Test, |
| 31 public SchedulerWorkerThreadDelegate { | |
| 30 protected: | 32 protected: |
| 31 TaskSchedulerWorkerThreadTest() | 33 TaskSchedulerWorkerThreadTest() |
| 32 : num_main_entry_callback_cv_(lock_.CreateConditionVariable()), | 34 : num_main_entry_cv_(lock_.CreateConditionVariable()), |
| 33 num_get_work_callback_cv_(lock_.CreateConditionVariable()), | 35 num_get_work_cv_(lock_.CreateConditionVariable()), |
| 34 run_sequences_cv_(lock_.CreateConditionVariable()) {} | 36 run_sequences_cv_(lock_.CreateConditionVariable()) {} |
| 35 | 37 |
| 36 void SetUp() override { | 38 void SetUp() override { |
| 37 worker_thread_ = SchedulerWorkerThread::CreateSchedulerWorkerThread( | 39 worker_thread_ = SchedulerWorkerThread::CreateSchedulerWorkerThread( |
| 38 ThreadPriority::NORMAL, | 40 ThreadPriority::NORMAL, this, &task_tracker_); |
| 39 Bind(&TaskSchedulerWorkerThreadTest::MainEntryCallback, | |
| 40 Unretained(this)), | |
| 41 Bind(&TaskSchedulerWorkerThreadTest::GetWorkCallback, Unretained(this)), | |
| 42 Bind(&TaskSchedulerWorkerThreadTest::RanTaskFromSequenceCallback, | |
| 43 Unretained(this)), | |
| 44 &task_tracker_); | |
| 45 ASSERT_TRUE(worker_thread_); | 41 ASSERT_TRUE(worker_thread_); |
| 46 WaitForNumMainEntryCallback(1); | 42 WaitForMainEntry(); |
| 47 } | 43 } |
| 48 | 44 |
| 49 void TearDown() override { | 45 void TearDown() override { |
| 46 { | |
| 47 AutoSchedulerLock auto_lock(lock_); | |
| 48 EXPECT_EQ(0U, num_main_exit_); | |
| 49 } | |
| 50 | |
| 50 worker_thread_->JoinForTesting(); | 51 worker_thread_->JoinForTesting(); |
| 51 | 52 |
| 52 AutoSchedulerLock auto_lock(lock_); | 53 AutoSchedulerLock auto_lock(lock_); |
| 53 EXPECT_EQ(1U, num_main_entry_callback_); | 54 EXPECT_EQ(1U, num_main_entry_); |
|
gab
2016/04/07 16:42:08
Move to test before Join?
fdoray
2016/04/07 17:59:33
I no longer need this in TearDown(). SetUp() verif
| |
| 55 EXPECT_EQ(1U, num_main_exit_); | |
| 54 } | 56 } |
| 55 | 57 |
| 56 // Wait until MainEntryCallback() has been called |num_main_entry_callback| | 58 // Wait until OnMainEntry() has been called once. |
| 57 // times. | 59 void WaitForMainEntry() { |
| 58 void WaitForNumMainEntryCallback(size_t num_main_entry_callback) { | |
| 59 AutoSchedulerLock auto_lock(lock_); | 60 AutoSchedulerLock auto_lock(lock_); |
| 60 while (num_main_entry_callback_ < num_main_entry_callback) | 61 while (num_main_entry_ < 1) |
|
gab
2016/04/07 16:42:08
Make |num_main_entry_| a bool (and thus probably a
fdoray
2016/04/07 17:59:33
Done.
| |
| 61 num_main_entry_callback_cv_->Wait(); | 62 num_main_entry_cv_->Wait(); |
| 62 } | 63 } |
| 63 | 64 |
| 64 // Wait until GetWorkCallback() has been called |num_get_work_callback| times. | 65 // Wait until GetWork() has been called |num_get_work| times. |
| 65 void WaitForNumGetWorkCallback(size_t num_get_work_callback) { | 66 void WaitForNumGetWork(size_t num_get_work) { |
| 66 AutoSchedulerLock auto_lock(lock_); | 67 AutoSchedulerLock auto_lock(lock_); |
| 67 while (num_get_work_callback_ < num_get_work_callback) | 68 while (num_get_work_ < num_get_work) |
| 68 num_get_work_callback_cv_->Wait(); | 69 num_get_work_cv_->Wait(); |
| 69 } | 70 } |
| 70 | 71 |
| 71 // Wait until there is no more Sequences to create and | 72 // Wait until there are no more Sequences to create and RanTaskFromSequence() |
| 72 // RanTaskFromSequenceCallback() has been invoked once for each Sequence | 73 // has been invoked once for each Sequence returned by GetWork(). |
| 73 // returned by GetWorkCallback(). | |
| 74 void WaitForAllSequencesToRun() { | 74 void WaitForAllSequencesToRun() { |
| 75 AutoSchedulerLock auto_lock(lock_); | 75 AutoSchedulerLock auto_lock(lock_); |
| 76 | 76 |
| 77 while (num_sequences_to_create_ > 0 || | 77 while (num_sequences_to_create_ > 0 || |
| 78 run_sequences_.size() < created_sequences_.size()) { | 78 run_sequences_.size() < created_sequences_.size()) { |
| 79 run_sequences_cv_->Wait(); | 79 run_sequences_cv_->Wait(); |
| 80 } | 80 } |
| 81 | 81 |
| 82 // Verify that RanTaskFromSequenceCallback() has been invoked with the | 82 // Verify that RanTaskFromSequence() has been invoked with the same |
| 83 // same Sequences that were returned by GetWorkCallback(). | 83 // Sequences that were returned by GetWork(). |
| 84 EXPECT_EQ(created_sequences_, run_sequences_); | 84 EXPECT_EQ(created_sequences_, run_sequences_); |
| 85 | 85 |
| 86 // Verify that RunTaskCallback() has been invoked once for each Sequence | 86 // Verify that RunTaskCallback() has been invoked once for each Sequence |
| 87 // returned by GetWorkCallback(). | 87 // returned by GetWork(). |
| 88 EXPECT_EQ(created_sequences_.size(), num_run_tasks_); | 88 EXPECT_EQ(created_sequences_.size(), num_run_tasks_); |
| 89 } | 89 } |
| 90 | 90 |
| 91 void SetNumSequencesToCreate(size_t num_sequences_to_create) { | 91 void SetNumSequencesToCreate(size_t num_sequences_to_create) { |
| 92 AutoSchedulerLock auto_lock(lock_); | 92 AutoSchedulerLock auto_lock(lock_); |
| 93 EXPECT_EQ(0U, num_sequences_to_create_); | 93 EXPECT_EQ(0U, num_sequences_to_create_); |
| 94 num_sequences_to_create_ = num_sequences_to_create; | 94 num_sequences_to_create_ = num_sequences_to_create; |
| 95 } | 95 } |
| 96 | 96 |
| 97 size_t NumGetWorkCallback() const { | 97 size_t NumGetWork() const { |
| 98 AutoSchedulerLock auto_lock(lock_); | 98 AutoSchedulerLock auto_lock(lock_); |
| 99 return num_get_work_callback_; | 99 return num_get_work_; |
| 100 } | 100 } |
| 101 | 101 |
| 102 std::unique_ptr<SchedulerWorkerThread> worker_thread_; | 102 std::unique_ptr<SchedulerWorkerThread> worker_thread_; |
| 103 | 103 |
| 104 private: | 104 private: |
| 105 void MainEntryCallback() { | 105 // SchedulerWorkerThreadDelegate: |
| 106 void OnMainEntry() override { | |
| 106 AutoSchedulerLock auto_lock(lock_); | 107 AutoSchedulerLock auto_lock(lock_); |
| 107 ++num_main_entry_callback_; | 108 ++num_main_entry_; |
| 108 num_main_entry_callback_cv_->Signal(); | 109 num_main_entry_cv_->Signal(); |
| 109 } | 110 } |
| 110 | 111 |
| 111 // Returns a Sequence that contains 1 Task if |num_sequences_to_create_| is | 112 void OnMainExit() override { |
| 112 // greater than 0. | 113 AutoSchedulerLock auto_lock(lock_); |
| 113 scoped_refptr<Sequence> GetWorkCallback( | 114 ++num_main_exit_; |
| 114 SchedulerWorkerThread* worker_thread) { | 115 } |
| 115 EXPECT_EQ(worker_thread_.get(), worker_thread); | |
| 116 | 116 |
| 117 scoped_refptr<Sequence> GetWork() override { | |
| 117 { | 118 { |
| 118 AutoSchedulerLock auto_lock(lock_); | 119 AutoSchedulerLock auto_lock(lock_); |
| 119 | 120 |
| 120 // Increment the number of times that this callback has been invoked. | 121 // Increment the number of times that this method has been called. |
| 121 ++num_get_work_callback_; | 122 ++num_get_work_; |
| 122 num_get_work_callback_cv_->Signal(); | 123 num_get_work_cv_->Signal(); |
| 123 | 124 |
| 124 // Check if a Sequence should be returned. | 125 // Check if a Sequence should be returned. |
| 125 if (num_sequences_to_create_ == 0) | 126 if (num_sequences_to_create_ == 0) |
| 126 return nullptr; | 127 return nullptr; |
| 127 --num_sequences_to_create_; | 128 --num_sequences_to_create_; |
| 128 } | 129 } |
| 129 | 130 |
| 130 // Create a Sequence that contains 1 Task. | 131 // Create a Sequence that contains 1 Task. |
| 131 scoped_refptr<Sequence> sequence(new Sequence); | 132 scoped_refptr<Sequence> sequence(new Sequence); |
| 132 task_tracker_.PostTask( | 133 task_tracker_.PostTask( |
| 133 Bind(IgnoreResult(&Sequence::PushTask), Unretained(sequence.get())), | 134 Bind(IgnoreResult(&Sequence::PushTask), Unretained(sequence.get())), |
| 134 WrapUnique(new Task( | 135 WrapUnique(new Task( |
| 135 FROM_HERE, Bind(&TaskSchedulerWorkerThreadTest::RunTaskCallback, | 136 FROM_HERE, Bind(&TaskSchedulerWorkerThreadTest::RunTaskCallback, |
| 136 Unretained(this)), | 137 Unretained(this)), |
| 137 TaskTraits()))); | 138 TaskTraits()))); |
| 138 | 139 |
| 139 { | 140 { |
| 140 // Add the Sequence to the vector of created Sequences. | 141 // Add the Sequence to the vector of created Sequences. |
| 141 AutoSchedulerLock auto_lock(lock_); | 142 AutoSchedulerLock auto_lock(lock_); |
| 142 created_sequences_.push_back(sequence); | 143 created_sequences_.push_back(sequence); |
| 143 } | 144 } |
| 144 | 145 |
| 145 return sequence; | 146 return sequence; |
| 146 } | 147 } |
| 147 | 148 |
| 148 void RanTaskFromSequenceCallback(const SchedulerWorkerThread* worker_thread, | 149 // Called after the SchedulerWorkerThread has tried to run a Task from |
| 149 scoped_refptr<Sequence> sequence) { | 150 // |sequence| (a TaskTracker might have prevented the Task from running). |
|
gab
2016/04/07 16:42:08
No need to re-state comment from override.
fdoray
2016/04/07 17:59:33
Done.
| |
| 150 EXPECT_EQ(worker_thread_.get(), worker_thread); | 151 void RanTaskFromSequence(scoped_refptr<Sequence> sequence) override { |
| 151 | |
| 152 AutoSchedulerLock auto_lock(lock_); | 152 AutoSchedulerLock auto_lock(lock_); |
| 153 run_sequences_.push_back(std::move(sequence)); | 153 run_sequences_.push_back(std::move(sequence)); |
| 154 run_sequences_cv_->Signal(); | 154 run_sequences_cv_->Signal(); |
| 155 } | 155 } |
| 156 | 156 |
| 157 void RunTaskCallback() { | 157 void RunTaskCallback() { |
| 158 AutoSchedulerLock auto_lock(lock_); | 158 AutoSchedulerLock auto_lock(lock_); |
| 159 ++num_run_tasks_; | 159 ++num_run_tasks_; |
| 160 } | 160 } |
| 161 | 161 |
| 162 TaskTracker task_tracker_; | 162 TaskTracker task_tracker_; |
| 163 | 163 |
| 164 // Synchronizes access to all members below. | 164 // Synchronizes access to all members below. |
| 165 mutable SchedulerLock lock_; | 165 mutable SchedulerLock lock_; |
| 166 | 166 |
| 167 // Number of times that MainEntryCallback() has been called. | 167 // Number of times that OnMainEntry() has been called. |
| 168 size_t num_main_entry_callback_ = 0; | 168 size_t num_main_entry_ = 0; |
| 169 | 169 |
| 170 // Condition variable signaled when |num_main_entry_callback_| is incremented. | 170 // Condition variable signaled when |num_main_entry_| is incremented. |
| 171 std::unique_ptr<ConditionVariable> num_main_entry_callback_cv_; | 171 std::unique_ptr<ConditionVariable> num_main_entry_cv_; |
| 172 | 172 |
| 173 // Number of Sequences that should be created by GetWorkCallback(). When this | 173 // Number of times that OnMainExit() has been called. |
| 174 // is 0, GetWorkCallback() returns nullptr. | 174 size_t num_main_exit_ = 0; |
| 175 | |
| 176 // Number of Sequences that should be created by GetWork(). When this | |
| 177 // is 0, GetWork() returns nullptr. | |
| 175 size_t num_sequences_to_create_ = 0; | 178 size_t num_sequences_to_create_ = 0; |
| 176 | 179 |
| 177 // Number of times that GetWorkCallback() has been called. | 180 // Number of times that GetWork() has been called. |
| 178 size_t num_get_work_callback_ = 0; | 181 size_t num_get_work_ = 0; |
| 179 | 182 |
| 180 // Condition variable signaled when |num_get_work_callback_| is incremented. | 183 // Condition variable signaled when |num_get_work_| is incremented. |
| 181 std::unique_ptr<ConditionVariable> num_get_work_callback_cv_; | 184 std::unique_ptr<ConditionVariable> num_get_work_cv_; |
| 182 | 185 |
| 183 // Sequences created by GetWorkCallback(). | 186 // Sequences created by GetWork(). |
| 184 std::vector<scoped_refptr<Sequence>> created_sequences_; | 187 std::vector<scoped_refptr<Sequence>> created_sequences_; |
| 185 | 188 |
| 186 // Sequences passed to RanTaskFromSequenceCallback(). | 189 // Sequences passed to RanTaskFromSequence(). |
| 187 std::vector<scoped_refptr<Sequence>> run_sequences_; | 190 std::vector<scoped_refptr<Sequence>> run_sequences_; |
| 188 | 191 |
| 189 // Condition variable signaled when a Sequence is added to |run_sequences_|. | 192 // Condition variable signaled when a Sequence is added to |run_sequences_|. |
| 190 std::unique_ptr<ConditionVariable> run_sequences_cv_; | 193 std::unique_ptr<ConditionVariable> run_sequences_cv_; |
| 191 | 194 |
| 192 // Number of times that RunTaskCallback() has been called. | 195 // Number of times that RunTaskCallback() has been called. |
| 193 size_t num_run_tasks_ = 0; | 196 size_t num_run_tasks_ = 0; |
| 194 | 197 |
| 195 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); | 198 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); |
| 196 }; | 199 }; |
| 197 | 200 |
| 198 // Verify that when GetWorkCallback() continuously returns Sequences, all Tasks | 201 // Verify that when GetWork() continuously returns Sequences, all Tasks in these |
| 199 // in these Sequences run successfully. The SchedulerWorkerThread is woken up | 202 // Sequences run successfully. The SchedulerWorkerThread is woken up once. |
| 200 // once. | |
| 201 TEST_F(TaskSchedulerWorkerThreadTest, ContinousWork) { | 203 TEST_F(TaskSchedulerWorkerThreadTest, ContinousWork) { |
| 202 // Set GetWorkCallback() to return |kNumSequencesPerTest| Sequences before | 204 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to |
| 203 // starting to return nullptr. | 205 // return nullptr. |
| 204 SetNumSequencesToCreate(kNumSequencesPerTest); | 206 SetNumSequencesToCreate(kNumSequencesPerTest); |
| 205 | 207 |
| 206 // Wake up |worker_thread_| and wait until it has run all the Tasks returned | 208 // Wake up |worker_thread_| and wait until it has run all the Tasks returned |
| 207 // by GetWorkCallback(). | 209 // by GetWork(). |
| 208 worker_thread_->WakeUp(); | 210 worker_thread_->WakeUp(); |
| 209 WaitForAllSequencesToRun(); | 211 WaitForAllSequencesToRun(); |
| 210 | 212 |
| 211 // Expect |kNumSequencesPerTest| calls to GetWorkCallback() in which it | 213 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returned a |
| 212 // returned a Sequence and 1 call in which it returned nullptr. | 214 // Sequence and 1 call in which it returned nullptr. |
| 213 const size_t expected_num_get_work_callback = kNumSequencesPerTest + 1; | 215 const size_t expected_num_get_work = kNumSequencesPerTest + 1; |
| 214 WaitForNumGetWorkCallback(expected_num_get_work_callback); | 216 WaitForNumGetWork(expected_num_get_work); |
| 215 EXPECT_EQ(expected_num_get_work_callback, NumGetWorkCallback()); | 217 EXPECT_EQ(expected_num_get_work, NumGetWork()); |
| 216 } | 218 } |
| 217 | 219 |
| 218 // Verify that when GetWorkCallback() alternates between returning a Sequence | 220 // Verify that when GetWork() alternates between returning a Sequence and |
| 219 // and returning nullptr, all Tasks in the returned Sequences run successfully. | 221 // returning nullptr, all Tasks in the returned Sequences run successfully. The |
| 220 // The SchedulerWorkerThread is woken up once for each Sequence. | 222 // SchedulerWorkerThread is woken up once for each Sequence. |
| 221 TEST_F(TaskSchedulerWorkerThreadTest, IntermittentWork) { | 223 TEST_F(TaskSchedulerWorkerThreadTest, IntermittentWork) { |
| 222 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { | 224 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { |
| 223 // Set GetWorkCallback() to return 1 Sequence before starting to return | 225 // Set GetWork() to return 1 Sequence before starting to return |
| 224 // nullptr. | 226 // nullptr. |
| 225 SetNumSequencesToCreate(1); | 227 SetNumSequencesToCreate(1); |
| 226 | 228 |
| 227 // Wake up |worker_thread_| and wait until it has run all the Tasks returned | 229 // Wake up |worker_thread_| and wait until it has run all the Tasks returned |
|
gab
2016/04/07 16:42:08
s/all the Tasks/the Task/ ?
fdoray
2016/04/07 17:59:33
Done.
| |
| 228 // by GetWorkCallback(). | 230 // by GetWork(). |
| 229 worker_thread_->WakeUp(); | 231 worker_thread_->WakeUp(); |
| 230 WaitForAllSequencesToRun(); | 232 WaitForAllSequencesToRun(); |
| 231 | 233 |
| 232 // Expect |i| calls to GetWorkCallback() in which it returned a Sequence and | 234 // Expect |i| calls to GetWork() in which it returned a Sequence and |
| 233 // |i| calls in which it returned nullptr. | 235 // |i| calls in which it returned nullptr. |
| 234 const size_t expected_num_get_work_callback = 2 * (i + 1); | 236 const size_t expected_num_get_work = 2 * (i + 1); |
| 235 WaitForNumGetWorkCallback(expected_num_get_work_callback); | 237 WaitForNumGetWork(expected_num_get_work); |
| 236 EXPECT_EQ(expected_num_get_work_callback, NumGetWorkCallback()); | 238 EXPECT_EQ(expected_num_get_work, NumGetWork()); |
| 237 } | 239 } |
| 238 } | 240 } |
| 239 | 241 |
| 240 } // namespace | 242 } // namespace |
| 241 } // namespace internal | 243 } // namespace internal |
| 242 } // namespace base | 244 } // namespace base |
| OLD | NEW |