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/delayed_task_manager.h" |
| 18 #include "base/task_scheduler/priority_queue.h" |
17 #include "base/task_scheduler/scheduler_lock.h" | 19 #include "base/task_scheduler/scheduler_lock.h" |
18 #include "base/task_scheduler/sequence.h" | 20 #include "base/task_scheduler/sequence.h" |
19 #include "base/task_scheduler/task.h" | 21 #include "base/task_scheduler/task.h" |
20 #include "base/task_scheduler/task_tracker.h" | 22 #include "base/task_scheduler/task_tracker.h" |
21 #include "testing/gtest/include/gtest/gtest.h" | 23 #include "testing/gtest/include/gtest/gtest.h" |
22 | 24 |
23 namespace base { | 25 namespace base { |
24 namespace internal { | 26 namespace internal { |
25 namespace { | 27 namespace { |
26 | 28 |
27 const size_t kNumSequencesPerTest = 150; | 29 constexpr size_t kNumTasksPerTest = 150; |
28 | 30 |
29 // The test parameter is the number of Tasks per Sequence returned by GetWork(). | 31 // The test parameter is the number of Tasks per Sequence returned by GetWork(). |
30 class TaskSchedulerWorkerThreadTest : public testing::TestWithParam<size_t>, | 32 class TaskSchedulerWorkerThreadDelegateWorkTest |
31 public SchedulerWorkerThread::Delegate { | 33 : public testing::TestWithParam<size_t>, |
| 34 public SchedulerWorkerThread::Delegate { |
32 protected: | 35 protected: |
33 TaskSchedulerWorkerThreadTest() | 36 TaskSchedulerWorkerThreadDelegateWorkTest() |
34 : main_entry_called_(true, false), | 37 : delayed_task_manager_(Bind(&DoNothing)), |
| 38 main_entry_called_(true, false), |
35 num_get_work_cv_(lock_.CreateConditionVariable()) {} | 39 num_get_work_cv_(lock_.CreateConditionVariable()) {} |
36 | 40 |
37 void SetUp() override { | 41 void SetUp() override { |
38 worker_thread_ = SchedulerWorkerThread::CreateSchedulerWorkerThread( | 42 worker_thread_ = SchedulerWorkerThread::CreateSchedulerWorkerThread( |
39 ThreadPriority::NORMAL, this, &task_tracker_); | 43 ThreadPriority::NORMAL, this, &task_tracker_, &delayed_task_manager_, |
| 44 &dummy_priority_queue_); |
40 ASSERT_TRUE(worker_thread_); | 45 ASSERT_TRUE(worker_thread_); |
41 main_entry_called_.Wait(); | 46 main_entry_called_.Wait(); |
42 } | 47 } |
43 | 48 |
44 void TearDown() override { | 49 void TearDown() override { |
45 worker_thread_->JoinForTesting(); | 50 worker_thread_->JoinForTesting(); |
46 } | 51 } |
47 | 52 |
48 size_t TasksPerSequence() const { return GetParam(); } | 53 size_t TasksPerSequence() const { return GetParam(); } |
49 | 54 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
86 // SchedulerWorkerThread::Delegate: | 91 // SchedulerWorkerThread::Delegate: |
87 void OnMainEntry() override { | 92 void OnMainEntry() override { |
88 // Without this |auto_lock|, OnMainEntry() could be called twice without | 93 // Without this |auto_lock|, OnMainEntry() could be called twice without |
89 // generating an error. | 94 // generating an error. |
90 AutoSchedulerLock auto_lock(lock_); | 95 AutoSchedulerLock auto_lock(lock_); |
91 EXPECT_FALSE(main_entry_called_.IsSignaled()); | 96 EXPECT_FALSE(main_entry_called_.IsSignaled()); |
92 main_entry_called_.Signal(); | 97 main_entry_called_.Signal(); |
93 } | 98 } |
94 | 99 |
95 scoped_refptr<Sequence> GetWork( | 100 scoped_refptr<Sequence> GetWork( |
96 SchedulerWorkerThread* worker_thread) override { | 101 SchedulerWorkerThread* worker_thread, |
| 102 PriorityQueue* alternate_priority_queue, |
| 103 bool* alternate_priority_queue_used) override { |
97 EXPECT_EQ(worker_thread_.get(), worker_thread); | 104 EXPECT_EQ(worker_thread_.get(), worker_thread); |
98 | 105 |
99 { | 106 { |
100 AutoSchedulerLock auto_lock(lock_); | 107 AutoSchedulerLock auto_lock(lock_); |
101 | 108 |
102 // Increment the number of times that this method has been called. | 109 // Increment the number of times that this method has been called. |
103 ++num_get_work_; | 110 ++num_get_work_; |
104 num_get_work_cv_->Signal(); | 111 num_get_work_cv_->Signal(); |
105 | 112 |
106 // Verify that this method isn't called more times than expected. | 113 // Verify that this method isn't called more times than expected. |
107 EXPECT_LE(num_get_work_, max_get_work_); | 114 EXPECT_LE(num_get_work_, max_get_work_); |
108 | 115 |
109 // Check if a Sequence should be returned. | 116 // Check if a Sequence should be returned. |
110 if (num_sequences_to_create_ == 0) | 117 if (num_sequences_to_create_ == 0) |
111 return nullptr; | 118 return nullptr; |
112 --num_sequences_to_create_; | 119 --num_sequences_to_create_; |
113 } | 120 } |
114 | 121 |
115 // Create a Sequence with TasksPerSequence() Tasks. | 122 // Create a Sequence with TasksPerSequence() Tasks. |
116 scoped_refptr<Sequence> sequence(new Sequence); | 123 scoped_refptr<Sequence> sequence(new Sequence); |
117 for (size_t i = 0; i < TasksPerSequence(); ++i) { | 124 for (size_t i = 0; i < TasksPerSequence(); ++i) { |
118 std::unique_ptr<Task> task(new Task( | 125 std::unique_ptr<Task> task(new Task( |
119 FROM_HERE, Bind(&TaskSchedulerWorkerThreadTest::RunTaskCallback, | 126 FROM_HERE, |
120 Unretained(this)), | 127 Bind(&TaskSchedulerWorkerThreadDelegateWorkTest::RunTaskCallback, |
| 128 Unretained(this)), |
121 TaskTraits(), TimeTicks())); | 129 TaskTraits(), TimeTicks())); |
122 EXPECT_TRUE(task_tracker_.WillPostTask(task.get())); | 130 EXPECT_TRUE(task_tracker_.WillPostTask(task.get())); |
123 sequence->PushTask(std::move(task)); | 131 sequence->PushTask(std::move(task)); |
124 } | 132 } |
125 | 133 |
126 { | 134 { |
127 // Add the Sequence to the vector of created Sequences. | 135 // Add the Sequence to the vector of created Sequences. |
128 AutoSchedulerLock auto_lock(lock_); | 136 AutoSchedulerLock auto_lock(lock_); |
129 created_sequences_.push_back(sequence); | 137 created_sequences_.push_back(sequence); |
130 } | 138 } |
(...skipping 21 matching lines...) Expand all Loading... |
152 EXPECT_LE(enqueued_sequences_.size(), created_sequences_.size()); | 160 EXPECT_LE(enqueued_sequences_.size(), created_sequences_.size()); |
153 } | 161 } |
154 | 162 |
155 void RunTaskCallback() { | 163 void RunTaskCallback() { |
156 AutoSchedulerLock auto_lock(lock_); | 164 AutoSchedulerLock auto_lock(lock_); |
157 ++num_run_tasks_; | 165 ++num_run_tasks_; |
158 EXPECT_LE(num_run_tasks_, created_sequences_.size()); | 166 EXPECT_LE(num_run_tasks_, created_sequences_.size()); |
159 } | 167 } |
160 | 168 |
161 TaskTracker task_tracker_; | 169 TaskTracker task_tracker_; |
| 170 DelayedTaskManager delayed_task_manager_; |
| 171 PriorityQueue dummy_priority_queue_; |
162 | 172 |
163 // Synchronizes access to all members below. | 173 // Synchronizes access to all members below. |
164 mutable SchedulerLock lock_; | 174 mutable SchedulerLock lock_; |
165 | 175 |
166 // Signaled once OnMainEntry() has been called. | 176 // Signaled once OnMainEntry() has been called. |
167 WaitableEvent main_entry_called_; | 177 WaitableEvent main_entry_called_; |
168 | 178 |
169 // Number of Sequences that should be created by GetWork(). When this | 179 // Number of Sequences that should be created by GetWork(). When this |
170 // is 0, GetWork() returns nullptr. | 180 // is 0, GetWork() returns nullptr. |
171 size_t num_sequences_to_create_ = 0; | 181 size_t num_sequences_to_create_ = 0; |
172 | 182 |
173 // Number of times that GetWork() has been called. | 183 // Number of times that GetWork() has been called. |
174 size_t num_get_work_ = 0; | 184 size_t num_get_work_ = 0; |
175 | 185 |
176 // Maximum number of times that GetWork() can be called. | 186 // Maximum number of times that GetWork() can be called. |
177 size_t max_get_work_ = 0; | 187 size_t max_get_work_ = 0; |
178 | 188 |
179 // Condition variable signaled when |num_get_work_| is incremented. | 189 // Condition variable signaled when |num_get_work_| is incremented. |
180 std::unique_ptr<ConditionVariable> num_get_work_cv_; | 190 std::unique_ptr<ConditionVariable> num_get_work_cv_; |
181 | 191 |
182 // Sequences created by GetWork(). | 192 // Sequences created by GetWork(). |
183 std::vector<scoped_refptr<Sequence>> created_sequences_; | 193 std::vector<scoped_refptr<Sequence>> created_sequences_; |
184 | 194 |
185 // Sequences passed to EnqueueSequence(). | 195 // Sequences passed to EnqueueSequence(). |
186 std::vector<scoped_refptr<Sequence>> enqueued_sequences_; | 196 std::vector<scoped_refptr<Sequence>> enqueued_sequences_; |
187 | 197 |
188 // Number of times that RunTaskCallback() has been called. | 198 // Number of times that RunTaskCallback() has been called. |
189 size_t num_run_tasks_ = 0; | 199 size_t num_run_tasks_ = 0; |
190 | 200 |
191 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); | 201 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadDelegateWorkTest); |
192 }; | 202 }; |
193 | 203 |
194 // Verify that when GetWork() continuously returns Sequences, all Tasks in these | 204 // Verify that when GetWork() continuously returns Sequences, all Tasks in these |
195 // Sequences run successfully. The test wakes up the SchedulerWorkerThread once. | 205 // Sequences run successfully. The test wakes up the SchedulerWorkerThread once. |
196 TEST_P(TaskSchedulerWorkerThreadTest, ContinuousWork) { | 206 TEST_P(TaskSchedulerWorkerThreadDelegateWorkTest, ContinuousWork) { |
197 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to | 207 // Set GetWork() to return |kNumTasksPerTest| Sequences before starting to |
198 // return nullptr. | 208 // return nullptr. |
199 SetNumSequencesToCreate(kNumSequencesPerTest); | 209 SetNumSequencesToCreate(kNumTasksPerTest); |
200 | 210 |
201 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a | 211 // Expect |kNumTasksPerTest| calls to GetWork() in which it returns a |
202 // Sequence and one call in which its returns nullptr. | 212 // Sequence and one call in which its returns nullptr. |
203 const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1; | 213 constexpr size_t kExpectedNumGetWork = kNumTasksPerTest + 1; |
204 SetMaxGetWork(kExpectedNumGetWork); | 214 SetMaxGetWork(kExpectedNumGetWork); |
205 | 215 |
206 // Wake up |worker_thread_| and wait until GetWork() has been invoked the | 216 // Wake up |worker_thread_| and wait until GetWork() has been invoked the |
207 // expected amount of times. | 217 // expected amount of times. |
208 worker_thread_->WakeUp(); | 218 worker_thread_->WakeUp(); |
209 WaitForNumGetWork(kExpectedNumGetWork); | 219 WaitForNumGetWork(kExpectedNumGetWork); |
210 | 220 |
211 // All tasks should have run. | 221 // All tasks should have run. |
212 EXPECT_EQ(kNumSequencesPerTest, NumRunTasks()); | 222 EXPECT_EQ(kNumTasksPerTest, NumRunTasks()); |
213 | 223 |
214 // If Sequences returned by GetWork() contain more than one Task, they aren't | 224 // If Sequences returned by GetWork() contain more than one Task, they aren't |
215 // empty after the worker thread pops Tasks from them and thus should be | 225 // empty after the worker thread pops Tasks from them and thus should be |
216 // returned to EnqueueSequence(). | 226 // returned to EnqueueSequence(). |
217 if (TasksPerSequence() > 1) | 227 if (TasksPerSequence() > 1) |
218 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); | 228 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); |
219 else | 229 else |
220 EXPECT_TRUE(EnqueuedSequences().empty()); | 230 EXPECT_TRUE(EnqueuedSequences().empty()); |
221 } | 231 } |
222 | 232 |
223 // Verify that when GetWork() alternates between returning a Sequence and | 233 // Verify that when GetWork() alternates between returning a Sequence and |
224 // returning nullptr, all Tasks in the returned Sequences run successfully. The | 234 // returning nullptr, all Tasks in the returned Sequences run successfully. The |
225 // test wakes up the SchedulerWorkerThread once for each Sequence. | 235 // test wakes up the SchedulerWorkerThread once for each Sequence. |
226 TEST_P(TaskSchedulerWorkerThreadTest, IntermittentWork) { | 236 TEST_P(TaskSchedulerWorkerThreadDelegateWorkTest, IntermittentWork) { |
227 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { | 237 for (size_t i = 0; i < kNumTasksPerTest; ++i) { |
228 // Set GetWork() to return 1 Sequence before starting to return | 238 // Set GetWork() to return 1 Sequence before starting to return |
229 // nullptr. | 239 // nullptr. |
230 SetNumSequencesToCreate(1); | 240 SetNumSequencesToCreate(1); |
231 | 241 |
232 // Expect |i + 1| calls to GetWork() in which it returns a Sequence and | 242 // Expect |i + 1| calls to GetWork() in which it returns a Sequence and |
233 // |i + 1| calls in which it returns nullptr. | 243 // |i + 1| calls in which it returns nullptr. |
234 const size_t expected_num_get_work = 2 * (i + 1); | 244 const size_t expected_num_get_work = 2 * (i + 1); |
235 SetMaxGetWork(expected_num_get_work); | 245 SetMaxGetWork(expected_num_get_work); |
236 | 246 |
237 // Wake up |worker_thread_| and wait until GetWork() has been invoked | 247 // Wake up |worker_thread_| and wait until GetWork() has been invoked |
238 // the expected amount of times. | 248 // the expected amount of times. |
239 worker_thread_->WakeUp(); | 249 worker_thread_->WakeUp(); |
240 WaitForNumGetWork(expected_num_get_work); | 250 WaitForNumGetWork(expected_num_get_work); |
241 | 251 |
242 // The Task should have run | 252 // The Task should have run |
243 EXPECT_EQ(i + 1, NumRunTasks()); | 253 EXPECT_EQ(i + 1, NumRunTasks()); |
244 | 254 |
245 // If Sequences returned by GetWork() contain more than one Task, they | 255 // If Sequences returned by GetWork() contain more than one Task, they |
246 // aren't empty after the worker thread pops Tasks from them and thus should | 256 // aren't empty after the worker thread pops Tasks from them and thus should |
247 // be returned to EnqueueSequence(). | 257 // be returned to EnqueueSequence(). |
248 if (TasksPerSequence() > 1) | 258 if (TasksPerSequence() > 1) |
249 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); | 259 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); |
250 else | 260 else |
251 EXPECT_TRUE(EnqueuedSequences().empty()); | 261 EXPECT_TRUE(EnqueuedSequences().empty()); |
252 } | 262 } |
253 } | 263 } |
254 | 264 |
255 INSTANTIATE_TEST_CASE_P(OneTaskPerSequence, | 265 INSTANTIATE_TEST_CASE_P(OneTaskPerSequence, |
256 TaskSchedulerWorkerThreadTest, | 266 TaskSchedulerWorkerThreadDelegateWorkTest, |
257 ::testing::Values(1)); | 267 ::testing::Values(1)); |
258 INSTANTIATE_TEST_CASE_P(TwoTasksPerSequence, | 268 INSTANTIATE_TEST_CASE_P(TwoTasksPerSequence, |
259 TaskSchedulerWorkerThreadTest, | 269 TaskSchedulerWorkerThreadDelegateWorkTest, |
260 ::testing::Values(2)); | 270 ::testing::Values(2)); |
261 | 271 |
| 272 class TaskSchedulerWorkerThreadSingleThreadedWorkTest |
| 273 : public testing::Test, |
| 274 public SchedulerWorkerThread::Delegate { |
| 275 protected: |
| 276 TaskSchedulerWorkerThreadSingleThreadedWorkTest() |
| 277 : delayed_task_manager_(Bind(&DoNothing)), |
| 278 num_run_tasks_cv_(lock_.CreateConditionVariable()) {} |
| 279 |
| 280 void SetUp() override { |
| 281 worker_thread_ = SchedulerWorkerThread::CreateSchedulerWorkerThread( |
| 282 ThreadPriority::NORMAL, this, &task_tracker_, &delayed_task_manager_, |
| 283 &dummy_priority_queue_); |
| 284 ASSERT_TRUE(worker_thread_); |
| 285 } |
| 286 |
| 287 void TearDown() override { worker_thread_->JoinForTesting(); } |
| 288 |
| 289 void PostTestTask(scoped_refptr<TaskRunner> task_runner) { |
| 290 size_t task_index; |
| 291 { |
| 292 AutoSchedulerLock auto_lock(lock_); |
| 293 task_index = num_posted_tasks_++; |
| 294 } |
| 295 task_runner->PostTask( |
| 296 FROM_HERE, |
| 297 Bind(&TaskSchedulerWorkerThreadSingleThreadedWorkTest::RunTaskCallback, |
| 298 Unretained(this), task_index, task_runner)); |
| 299 } |
| 300 |
| 301 void WaitForAllTasksToRun() { |
| 302 AutoSchedulerLock auto_lock(lock_); |
| 303 while (num_run_tasks_ < num_posted_tasks_) |
| 304 num_run_tasks_cv_->Wait(); |
| 305 } |
| 306 |
| 307 std::unique_ptr<SchedulerWorkerThread> worker_thread_; |
| 308 |
| 309 private: |
| 310 // SchedulerWorkerThread::Delegate: |
| 311 void OnMainEntry() override {} |
| 312 |
| 313 scoped_refptr<Sequence> GetWork( |
| 314 SchedulerWorkerThread* worker_thread, |
| 315 PriorityQueue* alternate_priority_queue, |
| 316 bool* alternate_priority_queue_used) override { |
| 317 EXPECT_EQ(worker_thread_.get(), worker_thread); |
| 318 |
| 319 // Return a Sequence from |alternate_priority_queue| or nullptr if it is |
| 320 // empty. |
| 321 auto transaction = alternate_priority_queue->BeginTransaction(); |
| 322 auto sequence = transaction->Peek().sequence; |
| 323 if (!sequence) |
| 324 return nullptr; |
| 325 *alternate_priority_queue_used = true; |
| 326 transaction->Pop(); |
| 327 return sequence; |
| 328 } |
| 329 |
| 330 void EnqueueSequence(scoped_refptr<Sequence> sequence) override { |
| 331 ADD_FAILURE() << "EnqueueSequence shouldn't be called in a test that only " |
| 332 "posts single-threaded Tasks."; |
| 333 } |
| 334 |
| 335 void RunTaskCallback(size_t index, scoped_refptr<TaskRunner> task_runner) { |
| 336 AutoSchedulerLock auto_lock(lock_); |
| 337 |
| 338 EXPECT_TRUE(task_runner->RunsTasksOnCurrentThread()); |
| 339 |
| 340 // Verify that tasks run in posting order. |
| 341 EXPECT_EQ(num_run_tasks_, index); |
| 342 |
| 343 // Verify that we don't run more tasks than posted. |
| 344 ++num_run_tasks_; |
| 345 EXPECT_LE(num_run_tasks_, num_posted_tasks_); |
| 346 |
| 347 num_run_tasks_cv_->Signal(); |
| 348 } |
| 349 |
| 350 TaskTracker task_tracker_; |
| 351 DelayedTaskManager delayed_task_manager_; |
| 352 PriorityQueue dummy_priority_queue_; |
| 353 |
| 354 // Synchronizes access to all members below. |
| 355 mutable SchedulerLock lock_; |
| 356 |
| 357 // Number of tasks posted by PostTestTask(). |
| 358 size_t num_posted_tasks_ = 0; |
| 359 |
| 360 // Number of times that RunTaskCallback() has been called. |
| 361 size_t num_run_tasks_ = 0; |
| 362 |
| 363 // Condition variable signaled when |num_run_tasks_| is incremented. |
| 364 std::unique_ptr<ConditionVariable> num_run_tasks_cv_; |
| 365 |
| 366 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadSingleThreadedWorkTest); |
| 367 }; |
| 368 |
| 369 TEST_F(TaskSchedulerWorkerThreadSingleThreadedWorkTest, ContinuousWork) { |
| 370 auto task_runner = worker_thread_->CreateTaskRunnerWithTraits(TaskTraits()); |
| 371 EXPECT_FALSE(task_runner->RunsTasksOnCurrentThread()); |
| 372 |
| 373 for (size_t i = 0; i < kNumTasksPerTest; ++i) |
| 374 PostTestTask(task_runner); |
| 375 WaitForAllTasksToRun(); |
| 376 } |
| 377 |
| 378 TEST_F(TaskSchedulerWorkerThreadSingleThreadedWorkTest, IntermittentWork) { |
| 379 auto task_runner = worker_thread_->CreateTaskRunnerWithTraits(TaskTraits()); |
| 380 EXPECT_FALSE(task_runner->RunsTasksOnCurrentThread()); |
| 381 |
| 382 for (size_t i = 0; i < kNumTasksPerTest; ++i) { |
| 383 PostTestTask(task_runner); |
| 384 WaitForAllTasksToRun(); |
| 385 } |
| 386 } |
| 387 |
262 } // namespace | 388 } // namespace |
263 } // namespace internal | 389 } // namespace internal |
264 } // namespace base | 390 } // namespace base |
OLD | NEW |