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 one 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 // This override verifies that |sequence| contains the expected number of |
| 150 // Tasks and adds it to |enqueued_sequences_|. Unlike a normal EnqueueSequence |
| 151 // implementation, it doesn't reinsert |sequence| into a queue for further |
| 152 // execution. |
| 153 void EnqueueSequence(scoped_refptr<Sequence> sequence) override { |
| 154 EXPECT_GT(TasksPerSequence(), 1U); |
| 155 |
| 156 // Verify that |sequence| contains TasksPerSequence() - 1 Tasks. |
| 157 for (size_t i = 0; i < TasksPerSequence() - 1; ++i) { |
| 158 EXPECT_TRUE(sequence->PeekTask()); |
| 159 sequence->PopTask(); |
| 160 } |
| 161 EXPECT_FALSE(sequence->PeekTask()); |
| 162 |
| 163 // Add |sequence| to |enqueued_sequences_|. |
150 AutoSchedulerLock auto_lock(lock_); | 164 AutoSchedulerLock auto_lock(lock_); |
151 run_sequences_.push_back(std::move(sequence)); | 165 enqueued_sequences_.push_back(std::move(sequence)); |
152 EXPECT_LE(run_sequences_.size(), created_sequences_.size()); | 166 EXPECT_LE(enqueued_sequences_.size(), created_sequences_.size()); |
153 run_sequences_cv_->Signal(); | |
154 } | 167 } |
155 | 168 |
156 void RunTaskCallback() { | 169 void RunTaskCallback() { |
157 AutoSchedulerLock auto_lock(lock_); | 170 AutoSchedulerLock auto_lock(lock_); |
158 ++num_run_tasks_; | 171 ++num_run_tasks_; |
159 EXPECT_LE(num_run_tasks_, created_sequences_.size()); | 172 EXPECT_LE(num_run_tasks_, created_sequences_.size()); |
160 } | 173 } |
161 | 174 |
162 TaskTracker task_tracker_; | 175 TaskTracker task_tracker_; |
163 | 176 |
(...skipping 15 matching lines...) Expand all Loading... |
179 | 192 |
180 // Maximum number of times that GetWork() can be called. | 193 // Maximum number of times that GetWork() can be called. |
181 size_t max_get_work_ = 0; | 194 size_t max_get_work_ = 0; |
182 | 195 |
183 // Condition variable signaled when |num_get_work_| is incremented. | 196 // Condition variable signaled when |num_get_work_| is incremented. |
184 std::unique_ptr<ConditionVariable> num_get_work_cv_; | 197 std::unique_ptr<ConditionVariable> num_get_work_cv_; |
185 | 198 |
186 // Sequences created by GetWork(). | 199 // Sequences created by GetWork(). |
187 std::vector<scoped_refptr<Sequence>> created_sequences_; | 200 std::vector<scoped_refptr<Sequence>> created_sequences_; |
188 | 201 |
189 // Sequences passed to RanTaskFromSequence(). | 202 // Sequences passed to EnqueueSequence(). |
190 std::vector<scoped_refptr<Sequence>> run_sequences_; | 203 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 | 204 |
195 // Number of times that RunTaskCallback() has been called. | 205 // Number of times that RunTaskCallback() has been called. |
196 size_t num_run_tasks_ = 0; | 206 size_t num_run_tasks_ = 0; |
197 | 207 |
198 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); | 208 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); |
199 }; | 209 }; |
200 | 210 |
201 // Verify that when GetWork() continuously returns Sequences, all Tasks in these | 211 // Verify that when GetWork() continuously returns Sequences, all Tasks in these |
202 // Sequences run successfully. The test wakes up the SchedulerWorkerThread once. | 212 // Sequences run successfully. The test wakes up the SchedulerWorkerThread once. |
203 TEST_F(TaskSchedulerWorkerThreadTest, ContinuousWork) { | 213 TEST_P(TaskSchedulerWorkerThreadTest, ContinuousWork) { |
204 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to | 214 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to |
205 // return nullptr. | 215 // return nullptr. |
206 SetNumSequencesToCreate(kNumSequencesPerTest); | 216 SetNumSequencesToCreate(kNumSequencesPerTest); |
207 | 217 |
208 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a | 218 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a |
209 // Sequence and one call in which its returns nullptr. | 219 // Sequence and one call in which its returns nullptr. |
210 const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1; | 220 const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1; |
211 SetMaxGetWork(kExpectedNumGetWork); | 221 SetMaxGetWork(kExpectedNumGetWork); |
212 | 222 |
213 // Wake up |worker_thread_| and wait until it has run all the Tasks returned | 223 // Wake up |worker_thread_| and wait until GetWork() has been invoked the |
214 // by GetWork(). | 224 // expected amount of times. |
215 worker_thread_->WakeUp(); | 225 worker_thread_->WakeUp(); |
216 WaitForAllSequencesToRun(); | |
217 WaitForNumGetWork(kExpectedNumGetWork); | 226 WaitForNumGetWork(kExpectedNumGetWork); |
| 227 |
| 228 // All tasks should have run. |
| 229 EXPECT_EQ(kNumSequencesPerTest, NumRunTasks()); |
| 230 |
| 231 // If Sequences returned by GetWork() contain more than one Task, they aren't |
| 232 // empty after the worker thread pops Tasks from them and thus should be |
| 233 // returned to EnqueueSequence(). |
| 234 if (TasksPerSequence() > 1) |
| 235 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); |
| 236 else |
| 237 EXPECT_TRUE(EnqueuedSequences().empty()); |
218 } | 238 } |
219 | 239 |
220 // Verify that when GetWork() alternates between returning a Sequence and | 240 // Verify that when GetWork() alternates between returning a Sequence and |
221 // returning nullptr, all Tasks in the returned Sequences run successfully. The | 241 // returning nullptr, all Tasks in the returned Sequences run successfully. The |
222 // test wakes up the SchedulerWorkerThread once for each Sequence. | 242 // test wakes up the SchedulerWorkerThread once for each Sequence. |
223 TEST_F(TaskSchedulerWorkerThreadTest, IntermittentWork) { | 243 TEST_P(TaskSchedulerWorkerThreadTest, IntermittentWork) { |
224 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { | 244 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { |
225 // Set GetWork() to return 1 Sequence before starting to return | 245 // Set GetWork() to return 1 Sequence before starting to return |
226 // nullptr. | 246 // nullptr. |
227 SetNumSequencesToCreate(1); | 247 SetNumSequencesToCreate(1); |
228 | 248 |
229 // Expect |i + 1| calls to GetWork() in which it returns a Sequence and | 249 // Expect |i + 1| calls to GetWork() in which it returns a Sequence and |
230 // |i + 1| calls in which it returns nullptr. | 250 // |i + 1| calls in which it returns nullptr. |
231 const size_t expected_num_get_work = 2 * (i + 1); | 251 const size_t expected_num_get_work = 2 * (i + 1); |
232 SetMaxGetWork(expected_num_get_work); | 252 SetMaxGetWork(expected_num_get_work); |
233 | 253 |
234 // Wake up |worker_thread_| and wait until it has run the Task returned by | 254 // Wake up |worker_thread_| and wait until GetWork() has been invoked |
235 // GetWork(). | 255 // the expected amount of times. |
236 worker_thread_->WakeUp(); | 256 worker_thread_->WakeUp(); |
237 WaitForAllSequencesToRun(); | |
238 WaitForNumGetWork(expected_num_get_work); | 257 WaitForNumGetWork(expected_num_get_work); |
| 258 |
| 259 // The Task should have run |
| 260 EXPECT_EQ(i + 1, NumRunTasks()); |
| 261 |
| 262 // If Sequences returned by GetWork() contain more than one Task, they |
| 263 // aren't empty after the worker thread pops Tasks from them and thus should |
| 264 // be returned to EnqueueSequence(). |
| 265 if (TasksPerSequence() > 1) |
| 266 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); |
| 267 else |
| 268 EXPECT_TRUE(EnqueuedSequences().empty()); |
239 } | 269 } |
240 } | 270 } |
241 | 271 |
| 272 INSTANTIATE_TEST_CASE_P(OneTaskPerSequence, |
| 273 TaskSchedulerWorkerThreadTest, |
| 274 ::testing::Values(1)); |
| 275 INSTANTIATE_TEST_CASE_P(TwoTasksPerSequence, |
| 276 TaskSchedulerWorkerThreadTest, |
| 277 ::testing::Values(2)); |
| 278 |
242 } // namespace | 279 } // namespace |
243 } // namespace internal | 280 } // namespace internal |
244 } // namespace base | 281 } // namespace base |
OLD | NEW |