| 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.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 "base/time/time.h" | 21 #include "base/time/time.h" |
| 22 #include "testing/gtest/include/gtest/gtest.h" | 22 #include "testing/gtest/include/gtest/gtest.h" |
| 23 | 23 |
| 24 namespace base { | 24 namespace base { |
| 25 namespace internal { | 25 namespace internal { |
| 26 namespace { | 26 namespace { |
| 27 | 27 |
| 28 const size_t kNumSequencesPerTest = 150; | 28 const size_t kNumSequencesPerTest = 150; |
| 29 | 29 |
| 30 // The test parameter is the number of Tasks per Sequence returned by GetWork(). | 30 // The test parameter is the number of Tasks per Sequence returned by GetWork(). |
| 31 class TaskSchedulerWorkerThreadTest : public testing::TestWithParam<size_t> { | 31 class TaskSchedulerWorkerTest : public testing::TestWithParam<size_t> { |
| 32 protected: | 32 protected: |
| 33 TaskSchedulerWorkerThreadTest() | 33 TaskSchedulerWorkerTest() |
| 34 : main_entry_called_(WaitableEvent::ResetPolicy::MANUAL, | 34 : main_entry_called_(WaitableEvent::ResetPolicy::MANUAL, |
| 35 WaitableEvent::InitialState::NOT_SIGNALED), | 35 WaitableEvent::InitialState::NOT_SIGNALED), |
| 36 num_get_work_cv_(lock_.CreateConditionVariable()), | 36 num_get_work_cv_(lock_.CreateConditionVariable()), |
| 37 worker_thread_set_(WaitableEvent::ResetPolicy::MANUAL, | 37 worker_set_(WaitableEvent::ResetPolicy::MANUAL, |
| 38 WaitableEvent::InitialState::NOT_SIGNALED) {} | 38 WaitableEvent::InitialState::NOT_SIGNALED) {} |
| 39 | 39 |
| 40 void SetUp() override { | 40 void SetUp() override { |
| 41 worker_thread_ = SchedulerWorkerThread::Create( | 41 worker_ = SchedulerWorker::Create( |
| 42 ThreadPriority::NORMAL, | 42 ThreadPriority::NORMAL, |
| 43 WrapUnique(new TestSchedulerWorkerThreadDelegate(this)), | 43 WrapUnique(new TestSchedulerWorkerDelegate(this)), |
| 44 &task_tracker_); | 44 &task_tracker_); |
| 45 ASSERT_TRUE(worker_thread_); | 45 ASSERT_TRUE(worker_); |
| 46 worker_thread_set_.Signal(); | 46 worker_set_.Signal(); |
| 47 main_entry_called_.Wait(); | 47 main_entry_called_.Wait(); |
| 48 } | 48 } |
| 49 | 49 |
| 50 void TearDown() override { | 50 void TearDown() override { |
| 51 worker_thread_->JoinForTesting(); | 51 worker_->JoinForTesting(); |
| 52 } | 52 } |
| 53 | 53 |
| 54 size_t TasksPerSequence() const { return GetParam(); } | 54 size_t TasksPerSequence() const { return GetParam(); } |
| 55 | 55 |
| 56 // Wait until GetWork() has been called |num_get_work| times. | 56 // Wait until GetWork() has been called |num_get_work| times. |
| 57 void WaitForNumGetWork(size_t num_get_work) { | 57 void WaitForNumGetWork(size_t num_get_work) { |
| 58 AutoSchedulerLock auto_lock(lock_); | 58 AutoSchedulerLock auto_lock(lock_); |
| 59 while (num_get_work_ < num_get_work) | 59 while (num_get_work_ < num_get_work) |
| 60 num_get_work_cv_->Wait(); | 60 num_get_work_cv_->Wait(); |
| 61 } | 61 } |
| (...skipping 17 matching lines...) Expand all Loading... |
| 79 std::vector<scoped_refptr<Sequence>> CreatedSequences() { | 79 std::vector<scoped_refptr<Sequence>> CreatedSequences() { |
| 80 AutoSchedulerLock auto_lock(lock_); | 80 AutoSchedulerLock auto_lock(lock_); |
| 81 return created_sequences_; | 81 return created_sequences_; |
| 82 } | 82 } |
| 83 | 83 |
| 84 std::vector<scoped_refptr<Sequence>> EnqueuedSequences() { | 84 std::vector<scoped_refptr<Sequence>> EnqueuedSequences() { |
| 85 AutoSchedulerLock auto_lock(lock_); | 85 AutoSchedulerLock auto_lock(lock_); |
| 86 return re_enqueued_sequences_; | 86 return re_enqueued_sequences_; |
| 87 } | 87 } |
| 88 | 88 |
| 89 std::unique_ptr<SchedulerWorkerThread> worker_thread_; | 89 std::unique_ptr<SchedulerWorker> worker_; |
| 90 | 90 |
| 91 private: | 91 private: |
| 92 class TestSchedulerWorkerThreadDelegate | 92 class TestSchedulerWorkerDelegate |
| 93 : public SchedulerWorkerThread::Delegate { | 93 : public SchedulerWorker::Delegate { |
| 94 public: | 94 public: |
| 95 TestSchedulerWorkerThreadDelegate(TaskSchedulerWorkerThreadTest* outer) | 95 TestSchedulerWorkerDelegate(TaskSchedulerWorkerTest* outer) |
| 96 : outer_(outer) {} | 96 : outer_(outer) {} |
| 97 | 97 |
| 98 // SchedulerWorkerThread::Delegate: | 98 // SchedulerWorker::Delegate: |
| 99 void OnMainEntry(SchedulerWorkerThread* worker_thread) override { | 99 void OnMainEntry(SchedulerWorker* worker) override { |
| 100 outer_->worker_thread_set_.Wait(); | 100 outer_->worker_set_.Wait(); |
| 101 EXPECT_EQ(outer_->worker_thread_.get(), worker_thread); | 101 EXPECT_EQ(outer_->worker_.get(), worker); |
| 102 | 102 |
| 103 // Without synchronization, OnMainEntry() could be called twice without | 103 // Without synchronization, OnMainEntry() could be called twice without |
| 104 // generating an error. | 104 // generating an error. |
| 105 AutoSchedulerLock auto_lock(outer_->lock_); | 105 AutoSchedulerLock auto_lock(outer_->lock_); |
| 106 EXPECT_FALSE(outer_->main_entry_called_.IsSignaled()); | 106 EXPECT_FALSE(outer_->main_entry_called_.IsSignaled()); |
| 107 outer_->main_entry_called_.Signal(); | 107 outer_->main_entry_called_.Signal(); |
| 108 } | 108 } |
| 109 | 109 |
| 110 scoped_refptr<Sequence> GetWork( | 110 scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override { |
| 111 SchedulerWorkerThread* worker_thread) override { | 111 EXPECT_EQ(outer_->worker_.get(), worker); |
| 112 EXPECT_EQ(outer_->worker_thread_.get(), worker_thread); | |
| 113 | 112 |
| 114 { | 113 { |
| 115 AutoSchedulerLock auto_lock(outer_->lock_); | 114 AutoSchedulerLock auto_lock(outer_->lock_); |
| 116 | 115 |
| 117 // Increment the number of times that this method has been called. | 116 // Increment the number of times that this method has been called. |
| 118 ++outer_->num_get_work_; | 117 ++outer_->num_get_work_; |
| 119 outer_->num_get_work_cv_->Signal(); | 118 outer_->num_get_work_cv_->Signal(); |
| 120 | 119 |
| 121 // Verify that this method isn't called more times than expected. | 120 // Verify that this method isn't called more times than expected. |
| 122 EXPECT_LE(outer_->num_get_work_, outer_->max_get_work_); | 121 EXPECT_LE(outer_->num_get_work_, outer_->max_get_work_); |
| 123 | 122 |
| 124 // Check if a Sequence should be returned. | 123 // Check if a Sequence should be returned. |
| 125 if (outer_->num_sequences_to_create_ == 0) | 124 if (outer_->num_sequences_to_create_ == 0) |
| 126 return nullptr; | 125 return nullptr; |
| 127 --outer_->num_sequences_to_create_; | 126 --outer_->num_sequences_to_create_; |
| 128 } | 127 } |
| 129 | 128 |
| 130 // Create a Sequence with TasksPerSequence() Tasks. | 129 // Create a Sequence with TasksPerSequence() Tasks. |
| 131 scoped_refptr<Sequence> sequence(new Sequence); | 130 scoped_refptr<Sequence> sequence(new Sequence); |
| 132 for (size_t i = 0; i < outer_->TasksPerSequence(); ++i) { | 131 for (size_t i = 0; i < outer_->TasksPerSequence(); ++i) { |
| 133 std::unique_ptr<Task> task(new Task( | 132 std::unique_ptr<Task> task(new Task( |
| 134 FROM_HERE, Bind(&TaskSchedulerWorkerThreadTest::RunTaskCallback, | 133 FROM_HERE, Bind(&TaskSchedulerWorkerTest::RunTaskCallback, |
| 135 Unretained(outer_)), | 134 Unretained(outer_)), |
| 136 TaskTraits(), TimeDelta())); | 135 TaskTraits(), TimeDelta())); |
| 137 EXPECT_TRUE(outer_->task_tracker_.WillPostTask(task.get())); | 136 EXPECT_TRUE(outer_->task_tracker_.WillPostTask(task.get())); |
| 138 sequence->PushTask(std::move(task)); | 137 sequence->PushTask(std::move(task)); |
| 139 } | 138 } |
| 140 | 139 |
| 141 { | 140 { |
| 142 // Add the Sequence to the vector of created Sequences. | 141 // Add the Sequence to the vector of created Sequences. |
| 143 AutoSchedulerLock auto_lock(outer_->lock_); | 142 AutoSchedulerLock auto_lock(outer_->lock_); |
| 144 outer_->created_sequences_.push_back(sequence); | 143 outer_->created_sequences_.push_back(sequence); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 166 outer_->re_enqueued_sequences_.push_back(std::move(sequence)); | 165 outer_->re_enqueued_sequences_.push_back(std::move(sequence)); |
| 167 EXPECT_LE(outer_->re_enqueued_sequences_.size(), | 166 EXPECT_LE(outer_->re_enqueued_sequences_.size(), |
| 168 outer_->created_sequences_.size()); | 167 outer_->created_sequences_.size()); |
| 169 } | 168 } |
| 170 | 169 |
| 171 TimeDelta GetSleepTimeout() override { | 170 TimeDelta GetSleepTimeout() override { |
| 172 return TimeDelta::Max(); | 171 return TimeDelta::Max(); |
| 173 } | 172 } |
| 174 | 173 |
| 175 private: | 174 private: |
| 176 TaskSchedulerWorkerThreadTest* outer_; | 175 TaskSchedulerWorkerTest* outer_; |
| 177 }; | 176 }; |
| 178 | 177 |
| 179 void RunTaskCallback() { | 178 void RunTaskCallback() { |
| 180 AutoSchedulerLock auto_lock(lock_); | 179 AutoSchedulerLock auto_lock(lock_); |
| 181 ++num_run_tasks_; | 180 ++num_run_tasks_; |
| 182 EXPECT_LE(num_run_tasks_, created_sequences_.size()); | 181 EXPECT_LE(num_run_tasks_, created_sequences_.size()); |
| 183 } | 182 } |
| 184 | 183 |
| 185 TaskTracker task_tracker_; | 184 TaskTracker task_tracker_; |
| 186 | 185 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 205 | 204 |
| 206 // Sequences created by GetWork(). | 205 // Sequences created by GetWork(). |
| 207 std::vector<scoped_refptr<Sequence>> created_sequences_; | 206 std::vector<scoped_refptr<Sequence>> created_sequences_; |
| 208 | 207 |
| 209 // Sequences passed to EnqueueSequence(). | 208 // Sequences passed to EnqueueSequence(). |
| 210 std::vector<scoped_refptr<Sequence>> re_enqueued_sequences_; | 209 std::vector<scoped_refptr<Sequence>> re_enqueued_sequences_; |
| 211 | 210 |
| 212 // Number of times that RunTaskCallback() has been called. | 211 // Number of times that RunTaskCallback() has been called. |
| 213 size_t num_run_tasks_ = 0; | 212 size_t num_run_tasks_ = 0; |
| 214 | 213 |
| 215 // Signaled after |worker_thread_| is set. | 214 // Signaled after |worker_| is set. |
| 216 WaitableEvent worker_thread_set_; | 215 WaitableEvent worker_set_; |
| 217 | 216 |
| 218 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); | 217 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerTest); |
| 219 }; | 218 }; |
| 220 | 219 |
| 221 // Verify that when GetWork() continuously returns Sequences, all Tasks in these | 220 // Verify that when GetWork() continuously returns Sequences, all Tasks in these |
| 222 // Sequences run successfully. The test wakes up the SchedulerWorkerThread once. | 221 // Sequences run successfully. The test wakes up the SchedulerWorker once. |
| 223 TEST_P(TaskSchedulerWorkerThreadTest, ContinuousWork) { | 222 TEST_P(TaskSchedulerWorkerTest, ContinuousWork) { |
| 224 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to | 223 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to |
| 225 // return nullptr. | 224 // return nullptr. |
| 226 SetNumSequencesToCreate(kNumSequencesPerTest); | 225 SetNumSequencesToCreate(kNumSequencesPerTest); |
| 227 | 226 |
| 228 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a | 227 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a |
| 229 // Sequence and one call in which its returns nullptr. | 228 // Sequence and one call in which its returns nullptr. |
| 230 const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1; | 229 const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1; |
| 231 SetMaxGetWork(kExpectedNumGetWork); | 230 SetMaxGetWork(kExpectedNumGetWork); |
| 232 | 231 |
| 233 // Wake up |worker_thread_| and wait until GetWork() has been invoked the | 232 // Wake up |worker_| and wait until GetWork() has been invoked the |
| 234 // expected amount of times. | 233 // expected amount of times. |
| 235 worker_thread_->WakeUp(); | 234 worker_->WakeUp(); |
| 236 WaitForNumGetWork(kExpectedNumGetWork); | 235 WaitForNumGetWork(kExpectedNumGetWork); |
| 237 | 236 |
| 238 // All tasks should have run. | 237 // All tasks should have run. |
| 239 EXPECT_EQ(kNumSequencesPerTest, NumRunTasks()); | 238 EXPECT_EQ(kNumSequencesPerTest, NumRunTasks()); |
| 240 | 239 |
| 241 // If Sequences returned by GetWork() contain more than one Task, they aren't | 240 // If Sequences returned by GetWork() contain more than one Task, they aren't |
| 242 // empty after the worker thread pops Tasks from them and thus should be | 241 // empty after the worker pops Tasks from them and thus should be returned to |
| 243 // returned to EnqueueSequence(). | 242 // EnqueueSequence(). |
| 244 if (TasksPerSequence() > 1) | 243 if (TasksPerSequence() > 1) |
| 245 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); | 244 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); |
| 246 else | 245 else |
| 247 EXPECT_TRUE(EnqueuedSequences().empty()); | 246 EXPECT_TRUE(EnqueuedSequences().empty()); |
| 248 } | 247 } |
| 249 | 248 |
| 250 // Verify that when GetWork() alternates between returning a Sequence and | 249 // Verify that when GetWork() alternates between returning a Sequence and |
| 251 // returning nullptr, all Tasks in the returned Sequences run successfully. The | 250 // returning nullptr, all Tasks in the returned Sequences run successfully. The |
| 252 // test wakes up the SchedulerWorkerThread once for each Sequence. | 251 // test wakes up the SchedulerWorker once for each Sequence. |
| 253 TEST_P(TaskSchedulerWorkerThreadTest, IntermittentWork) { | 252 TEST_P(TaskSchedulerWorkerTest, IntermittentWork) { |
| 254 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { | 253 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { |
| 255 // Set GetWork() to return 1 Sequence before starting to return | 254 // Set GetWork() to return 1 Sequence before starting to return |
| 256 // nullptr. | 255 // nullptr. |
| 257 SetNumSequencesToCreate(1); | 256 SetNumSequencesToCreate(1); |
| 258 | 257 |
| 259 // Expect |i + 1| calls to GetWork() in which it returns a Sequence and | 258 // Expect |i + 1| calls to GetWork() in which it returns a Sequence and |
| 260 // |i + 1| calls in which it returns nullptr. | 259 // |i + 1| calls in which it returns nullptr. |
| 261 const size_t expected_num_get_work = 2 * (i + 1); | 260 const size_t expected_num_get_work = 2 * (i + 1); |
| 262 SetMaxGetWork(expected_num_get_work); | 261 SetMaxGetWork(expected_num_get_work); |
| 263 | 262 |
| 264 // Wake up |worker_thread_| and wait until GetWork() has been invoked | 263 // Wake up |worker_| and wait until GetWork() has been invoked |
| 265 // the expected amount of times. | 264 // the expected amount of times. |
| 266 worker_thread_->WakeUp(); | 265 worker_->WakeUp(); |
| 267 WaitForNumGetWork(expected_num_get_work); | 266 WaitForNumGetWork(expected_num_get_work); |
| 268 | 267 |
| 269 // The Task should have run | 268 // The Task should have run |
| 270 EXPECT_EQ(i + 1, NumRunTasks()); | 269 EXPECT_EQ(i + 1, NumRunTasks()); |
| 271 | 270 |
| 272 // If Sequences returned by GetWork() contain more than one Task, they | 271 // If Sequences returned by GetWork() contain more than one Task, they |
| 273 // aren't empty after the worker thread pops Tasks from them and thus should | 272 // aren't empty after the worker pops Tasks from them and thus should be |
| 274 // be returned to EnqueueSequence(). | 273 // returned to EnqueueSequence(). |
| 275 if (TasksPerSequence() > 1) | 274 if (TasksPerSequence() > 1) |
| 276 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); | 275 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); |
| 277 else | 276 else |
| 278 EXPECT_TRUE(EnqueuedSequences().empty()); | 277 EXPECT_TRUE(EnqueuedSequences().empty()); |
| 279 } | 278 } |
| 280 } | 279 } |
| 281 | 280 |
| 282 INSTANTIATE_TEST_CASE_P(OneTaskPerSequence, | 281 INSTANTIATE_TEST_CASE_P(OneTaskPerSequence, |
| 283 TaskSchedulerWorkerThreadTest, | 282 TaskSchedulerWorkerTest, |
| 284 ::testing::Values(1)); | 283 ::testing::Values(1)); |
| 285 INSTANTIATE_TEST_CASE_P(TwoTasksPerSequence, | 284 INSTANTIATE_TEST_CASE_P(TwoTasksPerSequence, |
| 286 TaskSchedulerWorkerThreadTest, | 285 TaskSchedulerWorkerTest, |
| 287 ::testing::Values(2)); | 286 ::testing::Values(2)); |
| 288 | 287 |
| 289 } // namespace | 288 } // namespace |
| 290 } // namespace internal | 289 } // namespace internal |
| 291 } // namespace base | 290 } // namespace base |
| OLD | NEW |