OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/task_scheduler/scheduler_worker_thread.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <memory> | |
10 #include <vector> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/bind_helpers.h" | |
14 #include "base/macros.h" | |
15 #include "base/memory/ptr_util.h" | |
16 #include "base/synchronization/condition_variable.h" | |
17 #include "base/task_scheduler/scheduler_lock.h" | |
18 #include "base/task_scheduler/sequence.h" | |
19 #include "base/task_scheduler/task.h" | |
20 #include "base/task_scheduler/task_tracker.h" | |
21 #include "base/time/time.h" | |
22 #include "testing/gtest/include/gtest/gtest.h" | |
23 | |
24 namespace base { | |
25 namespace internal { | |
26 namespace { | |
27 | |
28 const size_t kNumSequencesPerTest = 150; | |
29 | |
30 // The test parameter is the number of Tasks per Sequence returned by GetWork(). | |
31 class TaskSchedulerWorkerThreadTest : public testing::TestWithParam<size_t> { | |
32 protected: | |
33 TaskSchedulerWorkerThreadTest() | |
34 : main_entry_called_(WaitableEvent::ResetPolicy::MANUAL, | |
35 WaitableEvent::InitialState::NOT_SIGNALED), | |
36 num_get_work_cv_(lock_.CreateConditionVariable()), | |
37 worker_thread_set_(WaitableEvent::ResetPolicy::MANUAL, | |
38 WaitableEvent::InitialState::NOT_SIGNALED) {} | |
39 | |
40 void SetUp() override { | |
41 worker_thread_ = SchedulerWorkerThread::Create( | |
42 ThreadPriority::NORMAL, | |
43 WrapUnique(new TestSchedulerWorkerThreadDelegate(this)), | |
44 &task_tracker_); | |
45 ASSERT_TRUE(worker_thread_); | |
46 worker_thread_set_.Signal(); | |
47 main_entry_called_.Wait(); | |
48 } | |
49 | |
50 void TearDown() override { | |
51 worker_thread_->JoinForTesting(); | |
52 } | |
53 | |
54 size_t TasksPerSequence() const { return GetParam(); } | |
55 | |
56 // Wait until GetWork() has been called |num_get_work| times. | |
57 void WaitForNumGetWork(size_t num_get_work) { | |
58 AutoSchedulerLock auto_lock(lock_); | |
59 while (num_get_work_ < num_get_work) | |
60 num_get_work_cv_->Wait(); | |
61 } | |
62 | |
63 void SetMaxGetWork(size_t max_get_work) { | |
64 AutoSchedulerLock auto_lock(lock_); | |
65 max_get_work_ = max_get_work; | |
66 } | |
67 | |
68 void SetNumSequencesToCreate(size_t num_sequences_to_create) { | |
69 AutoSchedulerLock auto_lock(lock_); | |
70 EXPECT_EQ(0U, num_sequences_to_create_); | |
71 num_sequences_to_create_ = num_sequences_to_create; | |
72 } | |
73 | |
74 size_t NumRunTasks() { | |
75 AutoSchedulerLock auto_lock(lock_); | |
76 return num_run_tasks_; | |
77 } | |
78 | |
79 std::vector<scoped_refptr<Sequence>> CreatedSequences() { | |
80 AutoSchedulerLock auto_lock(lock_); | |
81 return created_sequences_; | |
82 } | |
83 | |
84 std::vector<scoped_refptr<Sequence>> EnqueuedSequences() { | |
85 AutoSchedulerLock auto_lock(lock_); | |
86 return re_enqueued_sequences_; | |
87 } | |
88 | |
89 std::unique_ptr<SchedulerWorkerThread> worker_thread_; | |
90 | |
91 private: | |
92 class TestSchedulerWorkerThreadDelegate | |
93 : public SchedulerWorkerThread::Delegate { | |
94 public: | |
95 TestSchedulerWorkerThreadDelegate(TaskSchedulerWorkerThreadTest* outer) | |
96 : outer_(outer) {} | |
97 | |
98 // SchedulerWorkerThread::Delegate: | |
99 void OnMainEntry(SchedulerWorkerThread* worker_thread) override { | |
100 outer_->worker_thread_set_.Wait(); | |
101 EXPECT_EQ(outer_->worker_thread_.get(), worker_thread); | |
102 | |
103 // Without synchronization, OnMainEntry() could be called twice without | |
104 // generating an error. | |
105 AutoSchedulerLock auto_lock(outer_->lock_); | |
106 EXPECT_FALSE(outer_->main_entry_called_.IsSignaled()); | |
107 outer_->main_entry_called_.Signal(); | |
108 } | |
109 | |
110 scoped_refptr<Sequence> GetWork( | |
111 SchedulerWorkerThread* worker_thread) override { | |
112 EXPECT_EQ(outer_->worker_thread_.get(), worker_thread); | |
113 | |
114 { | |
115 AutoSchedulerLock auto_lock(outer_->lock_); | |
116 | |
117 // Increment the number of times that this method has been called. | |
118 ++outer_->num_get_work_; | |
119 outer_->num_get_work_cv_->Signal(); | |
120 | |
121 // Verify that this method isn't called more times than expected. | |
122 EXPECT_LE(outer_->num_get_work_, outer_->max_get_work_); | |
123 | |
124 // Check if a Sequence should be returned. | |
125 if (outer_->num_sequences_to_create_ == 0) | |
126 return nullptr; | |
127 --outer_->num_sequences_to_create_; | |
128 } | |
129 | |
130 // Create a Sequence with TasksPerSequence() Tasks. | |
131 scoped_refptr<Sequence> sequence(new Sequence); | |
132 for (size_t i = 0; i < outer_->TasksPerSequence(); ++i) { | |
133 std::unique_ptr<Task> task(new Task( | |
134 FROM_HERE, Bind(&TaskSchedulerWorkerThreadTest::RunTaskCallback, | |
135 Unretained(outer_)), | |
136 TaskTraits(), TimeDelta())); | |
137 EXPECT_TRUE(outer_->task_tracker_.WillPostTask(task.get())); | |
138 sequence->PushTask(std::move(task)); | |
139 } | |
140 | |
141 { | |
142 // Add the Sequence to the vector of created Sequences. | |
143 AutoSchedulerLock auto_lock(outer_->lock_); | |
144 outer_->created_sequences_.push_back(sequence); | |
145 } | |
146 | |
147 return sequence; | |
148 } | |
149 | |
150 // This override verifies that |sequence| contains the expected number of | |
151 // Tasks and adds it to |enqueued_sequences_|. Unlike a normal | |
152 // EnqueueSequence implementation, it doesn't reinsert |sequence| into a | |
153 // queue for further execution. | |
154 void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override { | |
155 EXPECT_GT(outer_->TasksPerSequence(), 1U); | |
156 | |
157 // Verify that |sequence| contains TasksPerSequence() - 1 Tasks. | |
158 for (size_t i = 0; i < outer_->TasksPerSequence() - 1; ++i) { | |
159 EXPECT_TRUE(sequence->PeekTask()); | |
160 sequence->PopTask(); | |
161 } | |
162 EXPECT_FALSE(sequence->PeekTask()); | |
163 | |
164 // Add |sequence| to |re_enqueued_sequences_|. | |
165 AutoSchedulerLock auto_lock(outer_->lock_); | |
166 outer_->re_enqueued_sequences_.push_back(std::move(sequence)); | |
167 EXPECT_LE(outer_->re_enqueued_sequences_.size(), | |
168 outer_->created_sequences_.size()); | |
169 } | |
170 | |
171 TimeDelta GetSleepTimeout() override { | |
172 return TimeDelta::Max(); | |
173 } | |
174 | |
175 private: | |
176 TaskSchedulerWorkerThreadTest* outer_; | |
177 }; | |
178 | |
179 void RunTaskCallback() { | |
180 AutoSchedulerLock auto_lock(lock_); | |
181 ++num_run_tasks_; | |
182 EXPECT_LE(num_run_tasks_, created_sequences_.size()); | |
183 } | |
184 | |
185 TaskTracker task_tracker_; | |
186 | |
187 // Synchronizes access to all members below. | |
188 mutable SchedulerLock lock_; | |
189 | |
190 // Signaled once OnMainEntry() has been called. | |
191 WaitableEvent main_entry_called_; | |
192 | |
193 // Number of Sequences that should be created by GetWork(). When this | |
194 // is 0, GetWork() returns nullptr. | |
195 size_t num_sequences_to_create_ = 0; | |
196 | |
197 // Number of times that GetWork() has been called. | |
198 size_t num_get_work_ = 0; | |
199 | |
200 // Maximum number of times that GetWork() can be called. | |
201 size_t max_get_work_ = 0; | |
202 | |
203 // Condition variable signaled when |num_get_work_| is incremented. | |
204 std::unique_ptr<ConditionVariable> num_get_work_cv_; | |
205 | |
206 // Sequences created by GetWork(). | |
207 std::vector<scoped_refptr<Sequence>> created_sequences_; | |
208 | |
209 // Sequences passed to EnqueueSequence(). | |
210 std::vector<scoped_refptr<Sequence>> re_enqueued_sequences_; | |
211 | |
212 // Number of times that RunTaskCallback() has been called. | |
213 size_t num_run_tasks_ = 0; | |
214 | |
215 // Signaled after |worker_thread_| is set. | |
216 WaitableEvent worker_thread_set_; | |
217 | |
218 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); | |
219 }; | |
220 | |
221 // Verify that when GetWork() continuously returns Sequences, all Tasks in these | |
222 // Sequences run successfully. The test wakes up the SchedulerWorkerThread once. | |
223 TEST_P(TaskSchedulerWorkerThreadTest, ContinuousWork) { | |
224 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to | |
225 // return nullptr. | |
226 SetNumSequencesToCreate(kNumSequencesPerTest); | |
227 | |
228 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a | |
229 // Sequence and one call in which its returns nullptr. | |
230 const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1; | |
231 SetMaxGetWork(kExpectedNumGetWork); | |
232 | |
233 // Wake up |worker_thread_| and wait until GetWork() has been invoked the | |
234 // expected amount of times. | |
235 worker_thread_->WakeUp(); | |
236 WaitForNumGetWork(kExpectedNumGetWork); | |
237 | |
238 // All tasks should have run. | |
239 EXPECT_EQ(kNumSequencesPerTest, NumRunTasks()); | |
240 | |
241 // 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 | |
243 // returned to EnqueueSequence(). | |
244 if (TasksPerSequence() > 1) | |
245 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); | |
246 else | |
247 EXPECT_TRUE(EnqueuedSequences().empty()); | |
248 } | |
249 | |
250 // Verify that when GetWork() alternates between returning a Sequence and | |
251 // returning nullptr, all Tasks in the returned Sequences run successfully. The | |
252 // test wakes up the SchedulerWorkerThread once for each Sequence. | |
253 TEST_P(TaskSchedulerWorkerThreadTest, IntermittentWork) { | |
254 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { | |
255 // Set GetWork() to return 1 Sequence before starting to return | |
256 // nullptr. | |
257 SetNumSequencesToCreate(1); | |
258 | |
259 // Expect |i + 1| calls to GetWork() in which it returns a Sequence and | |
260 // |i + 1| calls in which it returns nullptr. | |
261 const size_t expected_num_get_work = 2 * (i + 1); | |
262 SetMaxGetWork(expected_num_get_work); | |
263 | |
264 // Wake up |worker_thread_| and wait until GetWork() has been invoked | |
265 // the expected amount of times. | |
266 worker_thread_->WakeUp(); | |
267 WaitForNumGetWork(expected_num_get_work); | |
268 | |
269 // The Task should have run | |
270 EXPECT_EQ(i + 1, NumRunTasks()); | |
271 | |
272 // 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 | |
274 // be returned to EnqueueSequence(). | |
275 if (TasksPerSequence() > 1) | |
276 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); | |
277 else | |
278 EXPECT_TRUE(EnqueuedSequences().empty()); | |
279 } | |
280 } | |
281 | |
282 INSTANTIATE_TEST_CASE_P(OneTaskPerSequence, | |
283 TaskSchedulerWorkerThreadTest, | |
284 ::testing::Values(1)); | |
285 INSTANTIATE_TEST_CASE_P(TwoTasksPerSequence, | |
286 TaskSchedulerWorkerThreadTest, | |
287 ::testing::Values(2)); | |
288 | |
289 } // namespace | |
290 } // namespace internal | |
291 } // namespace base | |
OLD | NEW |