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/sequence.h" | 18 #include "base/task_scheduler/sequence.h" |
| 19 #include "base/task_scheduler/task.h" | 19 #include "base/task_scheduler/task.h" |
| 20 #include "base/task_scheduler/task_tracker.h" | 20 #include "base/task_scheduler/task_tracker.h" |
| 21 #include "testing/gtest/include/gtest/gtest.h" | 21 #include "testing/gtest/include/gtest/gtest.h" |
| 22 | 22 |
| 23 namespace base { | 23 namespace base { |
| 24 namespace internal { | 24 namespace internal { |
| 25 namespace { | 25 namespace { |
| 26 | 26 |
| 27 const size_t kNumSequencesPerTest = 150; | 27 const size_t kNumSequencesPerTest = 150; |
| 28 | 28 |
| 29 class TaskSchedulerWorkerThreadTest : public testing::Test, | 29 // The test parameter is the number of Tasks per Sequence returned by GetWork(). |
| 30 class TaskSchedulerWorkerThreadTest : public testing::TestWithParam<size_t>, | |
| 30 public SchedulerWorkerThread::Delegate { | 31 public SchedulerWorkerThread::Delegate { |
| 31 protected: | 32 protected: |
| 32 TaskSchedulerWorkerThreadTest() | 33 TaskSchedulerWorkerThreadTest() |
| 33 : main_entry_called_(true, false), | 34 : main_entry_called_(true, false), |
| 34 num_get_work_cv_(lock_.CreateConditionVariable()), | 35 num_get_work_cv_(lock_.CreateConditionVariable()) {} |
| 35 run_sequences_cv_(lock_.CreateConditionVariable()) {} | |
| 36 | 36 |
| 37 void SetUp() override { | 37 void SetUp() override { |
| 38 worker_thread_ = SchedulerWorkerThread::CreateSchedulerWorkerThread( | 38 worker_thread_ = SchedulerWorkerThread::CreateSchedulerWorkerThread( |
| 39 ThreadPriority::NORMAL, this, &task_tracker_); | 39 ThreadPriority::NORMAL, this, &task_tracker_); |
| 40 ASSERT_TRUE(worker_thread_); | 40 ASSERT_TRUE(worker_thread_); |
| 41 main_entry_called_.Wait(); | 41 main_entry_called_.Wait(); |
| 42 } | 42 } |
| 43 | 43 |
| 44 void TearDown() override { | 44 void TearDown() override { |
| 45 { | 45 { |
| 46 AutoSchedulerLock auto_lock(lock_); | 46 AutoSchedulerLock auto_lock(lock_); |
| 47 EXPECT_FALSE(main_exit_called_); | 47 EXPECT_FALSE(main_exit_called_); |
| 48 } | 48 } |
| 49 | 49 |
| 50 worker_thread_->JoinForTesting(); | 50 worker_thread_->JoinForTesting(); |
| 51 | 51 |
| 52 AutoSchedulerLock auto_lock(lock_); | 52 AutoSchedulerLock auto_lock(lock_); |
| 53 EXPECT_TRUE(main_exit_called_); | 53 EXPECT_TRUE(main_exit_called_); |
| 54 } | 54 } |
| 55 | 55 |
| 56 size_t TasksPerSequence() const { return GetParam(); } | |
| 57 | |
| 56 // Wait until GetWork() has been called |num_get_work| times. | 58 // Wait until GetWork() has been called |num_get_work| times. |
| 57 void WaitForNumGetWork(size_t num_get_work) { | 59 void WaitForNumGetWork(size_t num_get_work) { |
| 58 AutoSchedulerLock auto_lock(lock_); | 60 AutoSchedulerLock auto_lock(lock_); |
| 59 while (num_get_work_ < num_get_work) | 61 while (num_get_work_ < num_get_work) |
| 60 num_get_work_cv_->Wait(); | 62 num_get_work_cv_->Wait(); |
| 61 } | 63 } |
| 62 | 64 |
| 63 // Wait until there are no more Sequences to create and RanTaskFromSequence() | 65 void SetMaxGetWork(size_t max_get_work) { |
| 64 // has been invoked once for each Sequence returned by GetWork(). | |
| 65 void WaitForAllSequencesToRun() { | |
| 66 AutoSchedulerLock auto_lock(lock_); | 66 AutoSchedulerLock auto_lock(lock_); |
| 67 | 67 max_get_work_ = max_get_work; |
| 68 while (num_sequences_to_create_ > 0 || | |
| 69 run_sequences_.size() < created_sequences_.size()) { | |
| 70 run_sequences_cv_->Wait(); | |
| 71 } | |
| 72 | |
| 73 // Verify that RanTaskFromSequence() has been invoked with the same | |
| 74 // Sequences that were returned by GetWork(). | |
| 75 EXPECT_EQ(created_sequences_, run_sequences_); | |
| 76 | |
| 77 // Verify that RunTaskCallback() has been invoked once for each Sequence | |
| 78 // returned by GetWork(). | |
| 79 EXPECT_EQ(created_sequences_.size(), num_run_tasks_); | |
| 80 } | 68 } |
| 81 | 69 |
| 82 void SetNumSequencesToCreate(size_t num_sequences_to_create) { | 70 void SetNumSequencesToCreate(size_t num_sequences_to_create) { |
| 83 AutoSchedulerLock auto_lock(lock_); | 71 AutoSchedulerLock auto_lock(lock_); |
| 84 EXPECT_EQ(0U, num_sequences_to_create_); | 72 EXPECT_EQ(0U, num_sequences_to_create_); |
| 85 num_sequences_to_create_ = num_sequences_to_create; | 73 num_sequences_to_create_ = num_sequences_to_create; |
| 86 } | 74 } |
| 87 | 75 |
| 88 void SetMaxGetWork(size_t max_get_work) { | 76 size_t NumRunTasks() { |
| 89 AutoSchedulerLock auto_lock(lock_); | 77 AutoSchedulerLock auto_lock(lock_); |
| 90 max_get_work_ = max_get_work; | 78 return num_run_tasks_; |
| 79 } | |
| 80 | |
| 81 std::vector<scoped_refptr<Sequence>> CreatedSequences() { | |
| 82 AutoSchedulerLock auto_lock(lock_); | |
| 83 return created_sequences_; | |
| 84 } | |
| 85 | |
| 86 std::vector<scoped_refptr<Sequence>> EnqueuedSequences() { | |
| 87 AutoSchedulerLock auto_lock(lock_); | |
| 88 return enqueued_sequences_; | |
| 91 } | 89 } |
| 92 | 90 |
| 93 std::unique_ptr<SchedulerWorkerThread> worker_thread_; | 91 std::unique_ptr<SchedulerWorkerThread> worker_thread_; |
| 94 | 92 |
| 95 private: | 93 private: |
| 96 // SchedulerWorkerThread::Delegate: | 94 // SchedulerWorkerThread::Delegate: |
| 97 void OnMainEntry() override { | 95 void OnMainEntry() override { |
| 98 // Without this |auto_lock|, OnMainEntry() could be called twice without | 96 // Without this |auto_lock|, OnMainEntry() could be called twice without |
| 99 // generating an error. | 97 // generating an error. |
| 100 AutoSchedulerLock auto_lock(lock_); | 98 AutoSchedulerLock auto_lock(lock_); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 121 | 119 |
| 122 // Verify that this method isn't called more times than expected. | 120 // Verify that this method isn't called more times than expected. |
| 123 EXPECT_LE(num_get_work_, max_get_work_); | 121 EXPECT_LE(num_get_work_, max_get_work_); |
| 124 | 122 |
| 125 // Check if a Sequence should be returned. | 123 // Check if a Sequence should be returned. |
| 126 if (num_sequences_to_create_ == 0) | 124 if (num_sequences_to_create_ == 0) |
| 127 return nullptr; | 125 return nullptr; |
| 128 --num_sequences_to_create_; | 126 --num_sequences_to_create_; |
| 129 } | 127 } |
| 130 | 128 |
| 131 // Create a Sequence that contains 1 Task. | 129 // Create a Sequence with TasksPerSequence() Tasks. |
| 132 scoped_refptr<Sequence> sequence(new Sequence); | 130 scoped_refptr<Sequence> sequence(new Sequence); |
| 133 std::unique_ptr<Task> task(new Task( | 131 for (size_t i = 0; i < TasksPerSequence(); ++i) { |
| 134 FROM_HERE, | 132 std::unique_ptr<Task> task(new Task( |
| 135 Bind(&TaskSchedulerWorkerThreadTest::RunTaskCallback, Unretained(this)), | 133 FROM_HERE, Bind(&TaskSchedulerWorkerThreadTest::RunTaskCallback, |
| 136 TaskTraits())); | 134 Unretained(this)), |
| 137 EXPECT_TRUE(task_tracker_.WillPostTask(task.get())); | 135 TaskTraits())); |
| 138 sequence->PushTask(std::move(task)); | 136 EXPECT_TRUE(task_tracker_.WillPostTask(task.get())); |
| 137 sequence->PushTask(std::move(task)); | |
| 138 } | |
| 139 | 139 |
| 140 { | 140 { |
| 141 // Add the Sequence to the vector of created Sequences. | 141 // Add the Sequence to the vector of created Sequences. |
| 142 AutoSchedulerLock auto_lock(lock_); | 142 AutoSchedulerLock auto_lock(lock_); |
| 143 created_sequences_.push_back(sequence); | 143 created_sequences_.push_back(sequence); |
| 144 } | 144 } |
| 145 | 145 |
| 146 return sequence; | 146 return sequence; |
| 147 } | 147 } |
| 148 | 148 |
| 149 void RanTaskFromSequence(scoped_refptr<Sequence> sequence) override { | 149 void EnqueueSequence(scoped_refptr<Sequence> sequence) override { |
|
gab
2016/04/08 15:32:17
Add a comment above stating what this specific ove
fdoray
2016/04/08 15:59:12
Done.
| |
| 150 EXPECT_GT(TasksPerSequence(), 1U); | |
| 151 | |
| 152 // Verify that |sequence| contains TasksPerSequence() - 1 Tasks. | |
| 153 for (size_t i = 0; i < TasksPerSequence() - 1; ++i) { | |
| 154 EXPECT_TRUE(sequence->PeekTask()); | |
| 155 sequence->PopTask(); | |
| 156 } | |
| 157 EXPECT_FALSE(sequence->PeekTask()); | |
| 158 | |
| 159 // Add |sequence| to |enqueued_sequences_|. | |
| 150 AutoSchedulerLock auto_lock(lock_); | 160 AutoSchedulerLock auto_lock(lock_); |
| 151 run_sequences_.push_back(std::move(sequence)); | 161 enqueued_sequences_.push_back(std::move(sequence)); |
| 152 EXPECT_LE(run_sequences_.size(), created_sequences_.size()); | 162 EXPECT_LE(enqueued_sequences_.size(), created_sequences_.size()); |
| 153 run_sequences_cv_->Signal(); | |
| 154 } | 163 } |
| 155 | 164 |
| 156 void RunTaskCallback() { | 165 void RunTaskCallback() { |
| 157 AutoSchedulerLock auto_lock(lock_); | 166 AutoSchedulerLock auto_lock(lock_); |
| 158 ++num_run_tasks_; | 167 ++num_run_tasks_; |
| 159 EXPECT_LE(num_run_tasks_, created_sequences_.size()); | 168 EXPECT_LE(num_run_tasks_, created_sequences_.size()); |
| 160 } | 169 } |
| 161 | 170 |
| 162 TaskTracker task_tracker_; | 171 TaskTracker task_tracker_; |
| 163 | 172 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 179 | 188 |
| 180 // Maximum number of times that GetWork() can be called. | 189 // Maximum number of times that GetWork() can be called. |
| 181 size_t max_get_work_ = 0; | 190 size_t max_get_work_ = 0; |
| 182 | 191 |
| 183 // Condition variable signaled when |num_get_work_| is incremented. | 192 // Condition variable signaled when |num_get_work_| is incremented. |
| 184 std::unique_ptr<ConditionVariable> num_get_work_cv_; | 193 std::unique_ptr<ConditionVariable> num_get_work_cv_; |
| 185 | 194 |
| 186 // Sequences created by GetWork(). | 195 // Sequences created by GetWork(). |
| 187 std::vector<scoped_refptr<Sequence>> created_sequences_; | 196 std::vector<scoped_refptr<Sequence>> created_sequences_; |
| 188 | 197 |
| 189 // Sequences passed to RanTaskFromSequence(). | 198 // Sequences passed to EnqueueSequence(). |
| 190 std::vector<scoped_refptr<Sequence>> run_sequences_; | 199 std::vector<scoped_refptr<Sequence>> enqueued_sequences_; |
| 191 | |
| 192 // Condition variable signaled when a Sequence is added to |run_sequences_|. | |
| 193 std::unique_ptr<ConditionVariable> run_sequences_cv_; | |
| 194 | 200 |
| 195 // Number of times that RunTaskCallback() has been called. | 201 // Number of times that RunTaskCallback() has been called. |
| 196 size_t num_run_tasks_ = 0; | 202 size_t num_run_tasks_ = 0; |
| 197 | 203 |
| 198 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); | 204 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); |
| 199 }; | 205 }; |
| 200 | 206 |
| 201 // Verify that when GetWork() continuously returns Sequences, all Tasks in these | 207 // Verify that when GetWork() continuously returns Sequences, all Tasks in these |
| 202 // Sequences run successfully. The test wakes up the SchedulerWorkerThread once. | 208 // Sequences run successfully. The test wakes up the SchedulerWorkerThread once. |
| 203 TEST_F(TaskSchedulerWorkerThreadTest, ContinuousWork) { | 209 TEST_P(TaskSchedulerWorkerThreadTest, ContinuousWork) { |
| 204 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to | 210 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to |
| 205 // return nullptr. | 211 // return nullptr. |
| 206 SetNumSequencesToCreate(kNumSequencesPerTest); | 212 SetNumSequencesToCreate(kNumSequencesPerTest); |
| 207 | 213 |
| 208 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a | 214 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a |
| 209 // Sequence and 1 call in which its returns nullptr. | 215 // Sequence and 1 call in which its returns nullptr. |
| 210 const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1; | 216 const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1; |
| 211 SetMaxGetWork(kExpectedNumGetWork); | 217 SetMaxGetWork(kExpectedNumGetWork); |
| 212 | 218 |
| 213 // Wake up |worker_thread_| and wait until it has run all the Tasks returned | 219 // Wake up |worker_thread_| and wait until the GetWork() call that returns |
| 214 // by GetWork(). | 220 // nullptr. |
|
gab
2016/04/08 15:32:17
"and wait until the GetWork() call that returns nu
fdoray
2016/04/08 15:59:12
Done.
| |
| 215 worker_thread_->WakeUp(); | 221 worker_thread_->WakeUp(); |
| 216 WaitForAllSequencesToRun(); | |
| 217 WaitForNumGetWork(kExpectedNumGetWork); | 222 WaitForNumGetWork(kExpectedNumGetWork); |
| 223 | |
| 224 // All tasks should have run. | |
| 225 EXPECT_EQ(kNumSequencesPerTest, NumRunTasks()); | |
| 226 | |
| 227 // If Sequences returned by GetWork() contain more than one Task, they aren't | |
| 228 // empty after the worker thread pops Tasks from them and thus should be | |
| 229 // returned to EnqueueSequence(). | |
| 230 if (TasksPerSequence() > 1) | |
| 231 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); | |
| 232 else | |
| 233 EXPECT_TRUE(EnqueuedSequences().empty()); | |
| 218 } | 234 } |
| 219 | 235 |
| 220 // Verify that when GetWork() alternates between returning a Sequence and | 236 // Verify that when GetWork() alternates between returning a Sequence and |
| 221 // returning nullptr, all Tasks in the returned Sequences run successfully. The | 237 // returning nullptr, all Tasks in the returned Sequences run successfully. The |
| 222 // test wakes up the SchedulerWorkerThread once for each Sequence. | 238 // test wakes up the SchedulerWorkerThread once for each Sequence. |
| 223 TEST_F(TaskSchedulerWorkerThreadTest, IntermittentWork) { | 239 TEST_P(TaskSchedulerWorkerThreadTest, IntermittentWork) { |
| 224 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { | 240 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { |
| 225 // Set GetWork() to return 1 Sequence before starting to return | 241 // Set GetWork() to return 1 Sequence before starting to return |
| 226 // nullptr. | 242 // nullptr. |
| 227 SetNumSequencesToCreate(1); | 243 SetNumSequencesToCreate(1); |
| 228 | 244 |
| 229 // Expect |i + 1| calls to GetWork() in which it returns a Sequence and | 245 // Expect |i + 1| calls to GetWork() in which it returns a Sequence and |
| 230 // |i + 1| calls in which it returns nullptr. | 246 // |i + 1| calls in which it returns nullptr. |
| 231 const size_t expected_num_get_work = 2 * (i + 1); | 247 const size_t expected_num_get_work = 2 * (i + 1); |
| 232 SetMaxGetWork(expected_num_get_work); | 248 SetMaxGetWork(expected_num_get_work); |
| 233 | 249 |
| 234 // Wake up |worker_thread_| and wait until it has run the Task returned by | 250 // Wake up |worker_thread_| and wait until the GetWork() call that returns |
| 235 // GetWork(). | 251 // nullptr. |
| 236 worker_thread_->WakeUp(); | 252 worker_thread_->WakeUp(); |
| 237 WaitForAllSequencesToRun(); | |
| 238 WaitForNumGetWork(expected_num_get_work); | 253 WaitForNumGetWork(expected_num_get_work); |
| 254 | |
| 255 // The Task should have run | |
| 256 EXPECT_EQ(i + 1, NumRunTasks()); | |
| 257 | |
| 258 // If Sequences returned by GetWork() contain more than one Task, they | |
| 259 // aren't empty after the worker thread pops Tasks from them and thus should | |
| 260 // be returned to EnqueueSequence(). | |
| 261 if (TasksPerSequence() > 1) | |
| 262 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); | |
| 263 else | |
| 264 EXPECT_TRUE(EnqueuedSequences().empty()); | |
| 239 } | 265 } |
| 240 } | 266 } |
| 241 | 267 |
| 268 INSTANTIATE_TEST_CASE_P(OneTaskPerSequence, | |
| 269 TaskSchedulerWorkerThreadTest, | |
| 270 ::testing::Values(1)); | |
| 271 INSTANTIATE_TEST_CASE_P(TwoTasksPerSequence, | |
| 272 TaskSchedulerWorkerThreadTest, | |
| 273 ::testing::Values(2)); | |
| 274 | |
| 242 } // namespace | 275 } // namespace |
| 243 } // namespace internal | 276 } // namespace internal |
| 244 } // namespace base | 277 } // namespace base |
| OLD | NEW |