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 class TaskSchedulerWorkerThreadTest : public testing::Test, |
| 30 public SchedulerWorkerThread::Delegate { |
30 protected: | 31 protected: |
31 TaskSchedulerWorkerThreadTest() | 32 TaskSchedulerWorkerThreadTest() |
32 : num_main_entry_callback_cv_(lock_.CreateConditionVariable()), | 33 : main_entry_called_(true, false), |
33 num_get_work_callback_cv_(lock_.CreateConditionVariable()), | 34 num_get_work_cv_(lock_.CreateConditionVariable()), |
34 run_sequences_cv_(lock_.CreateConditionVariable()) {} | 35 run_sequences_cv_(lock_.CreateConditionVariable()) {} |
35 | 36 |
36 void SetUp() override { | 37 void SetUp() override { |
37 worker_thread_ = SchedulerWorkerThread::CreateSchedulerWorkerThread( | 38 worker_thread_ = SchedulerWorkerThread::CreateSchedulerWorkerThread( |
38 ThreadPriority::NORMAL, | 39 ThreadPriority::NORMAL, this, &task_tracker_); |
39 Bind(&TaskSchedulerWorkerThreadTest::MainEntryCallback, | |
40 Unretained(this)), | |
41 Bind(&TaskSchedulerWorkerThreadTest::GetWorkCallback, Unretained(this)), | |
42 Bind(&TaskSchedulerWorkerThreadTest::RanTaskFromSequenceCallback, | |
43 Unretained(this)), | |
44 &task_tracker_); | |
45 ASSERT_TRUE(worker_thread_); | 40 ASSERT_TRUE(worker_thread_); |
46 WaitForNumMainEntryCallback(1); | 41 main_entry_called_.Wait(); |
47 } | 42 } |
48 | 43 |
49 void TearDown() override { | 44 void TearDown() override { |
| 45 { |
| 46 AutoSchedulerLock auto_lock(lock_); |
| 47 EXPECT_FALSE(main_exit_called_); |
| 48 } |
| 49 |
50 worker_thread_->JoinForTesting(); | 50 worker_thread_->JoinForTesting(); |
51 | 51 |
52 AutoSchedulerLock auto_lock(lock_); | 52 AutoSchedulerLock auto_lock(lock_); |
53 EXPECT_EQ(1U, num_main_entry_callback_); | 53 EXPECT_TRUE(main_exit_called_); |
54 } | 54 } |
55 | 55 |
56 // Wait until MainEntryCallback() has been called |num_main_entry_callback| | 56 // Wait until GetWork() has been called |num_get_work| times. |
57 // times. | 57 void WaitForNumGetWork(size_t num_get_work) { |
58 void WaitForNumMainEntryCallback(size_t num_main_entry_callback) { | |
59 AutoSchedulerLock auto_lock(lock_); | 58 AutoSchedulerLock auto_lock(lock_); |
60 while (num_main_entry_callback_ < num_main_entry_callback) | 59 while (num_get_work_ < num_get_work) |
61 num_main_entry_callback_cv_->Wait(); | 60 num_get_work_cv_->Wait(); |
62 } | 61 } |
63 | 62 |
64 // Wait until GetWorkCallback() has been called |num_get_work_callback| times. | 63 // Wait until there are no more Sequences to create and RanTaskFromSequence() |
65 void WaitForNumGetWorkCallback(size_t num_get_work_callback) { | 64 // has been invoked once for each Sequence returned by GetWork(). |
66 AutoSchedulerLock auto_lock(lock_); | |
67 while (num_get_work_callback_ < num_get_work_callback) | |
68 num_get_work_callback_cv_->Wait(); | |
69 } | |
70 | |
71 // Wait until there is no more Sequences to create and | |
72 // RanTaskFromSequenceCallback() has been invoked once for each Sequence | |
73 // returned by GetWorkCallback(). | |
74 void WaitForAllSequencesToRun() { | 65 void WaitForAllSequencesToRun() { |
75 AutoSchedulerLock auto_lock(lock_); | 66 AutoSchedulerLock auto_lock(lock_); |
76 | 67 |
77 while (num_sequences_to_create_ > 0 || | 68 while (num_sequences_to_create_ > 0 || |
78 run_sequences_.size() < created_sequences_.size()) { | 69 run_sequences_.size() < created_sequences_.size()) { |
79 run_sequences_cv_->Wait(); | 70 run_sequences_cv_->Wait(); |
80 } | 71 } |
81 | 72 |
82 // Verify that RanTaskFromSequenceCallback() has been invoked with the | 73 // Verify that RanTaskFromSequence() has been invoked with the same |
83 // same Sequences that were returned by GetWorkCallback(). | 74 // Sequences that were returned by GetWork(). |
84 EXPECT_EQ(created_sequences_, run_sequences_); | 75 EXPECT_EQ(created_sequences_, run_sequences_); |
85 | 76 |
86 // Verify that RunTaskCallback() has been invoked once for each Sequence | 77 // Verify that RunTaskCallback() has been invoked once for each Sequence |
87 // returned by GetWorkCallback(). | 78 // returned by GetWork(). |
88 EXPECT_EQ(created_sequences_.size(), num_run_tasks_); | 79 EXPECT_EQ(created_sequences_.size(), num_run_tasks_); |
89 } | 80 } |
90 | 81 |
91 void SetNumSequencesToCreate(size_t num_sequences_to_create) { | 82 void SetNumSequencesToCreate(size_t num_sequences_to_create) { |
92 AutoSchedulerLock auto_lock(lock_); | 83 AutoSchedulerLock auto_lock(lock_); |
93 EXPECT_EQ(0U, num_sequences_to_create_); | 84 EXPECT_EQ(0U, num_sequences_to_create_); |
94 num_sequences_to_create_ = num_sequences_to_create; | 85 num_sequences_to_create_ = num_sequences_to_create; |
95 } | 86 } |
96 | 87 |
97 size_t NumGetWorkCallback() const { | 88 size_t NumGetWork() const { |
98 AutoSchedulerLock auto_lock(lock_); | 89 AutoSchedulerLock auto_lock(lock_); |
99 return num_get_work_callback_; | 90 return num_get_work_; |
100 } | 91 } |
101 | 92 |
102 std::unique_ptr<SchedulerWorkerThread> worker_thread_; | 93 std::unique_ptr<SchedulerWorkerThread> worker_thread_; |
103 | 94 |
104 private: | 95 private: |
105 void MainEntryCallback() { | 96 // SchedulerWorkerThread::Delegate: |
| 97 void OnMainEntry() override { |
| 98 // Without this |auto_lock|, OnMainEntry() could be called twice without |
| 99 // generating an error. |
106 AutoSchedulerLock auto_lock(lock_); | 100 AutoSchedulerLock auto_lock(lock_); |
107 ++num_main_entry_callback_; | 101 EXPECT_FALSE(main_entry_called_.IsSignaled()); |
108 num_main_entry_callback_cv_->Signal(); | 102 main_entry_called_.Signal(); |
109 } | 103 } |
110 | 104 |
111 // Returns a Sequence that contains 1 Task if |num_sequences_to_create_| is | 105 void OnMainExit() override { |
112 // greater than 0. | 106 AutoSchedulerLock auto_lock(lock_); |
113 scoped_refptr<Sequence> GetWorkCallback( | 107 EXPECT_FALSE(main_exit_called_); |
114 SchedulerWorkerThread* worker_thread) { | 108 main_exit_called_ = true; |
| 109 } |
| 110 |
| 111 scoped_refptr<Sequence> GetWork( |
| 112 SchedulerWorkerThread* worker_thread) override { |
115 EXPECT_EQ(worker_thread_.get(), worker_thread); | 113 EXPECT_EQ(worker_thread_.get(), worker_thread); |
116 | 114 |
117 { | 115 { |
118 AutoSchedulerLock auto_lock(lock_); | 116 AutoSchedulerLock auto_lock(lock_); |
119 | 117 |
120 // Increment the number of times that this callback has been invoked. | 118 // Increment the number of times that this method has been called. |
121 ++num_get_work_callback_; | 119 ++num_get_work_; |
122 num_get_work_callback_cv_->Signal(); | 120 num_get_work_cv_->Signal(); |
123 | 121 |
124 // Check if a Sequence should be returned. | 122 // Check if a Sequence should be returned. |
125 if (num_sequences_to_create_ == 0) | 123 if (num_sequences_to_create_ == 0) |
126 return nullptr; | 124 return nullptr; |
127 --num_sequences_to_create_; | 125 --num_sequences_to_create_; |
128 } | 126 } |
129 | 127 |
130 // Create a Sequence that contains 1 Task. | 128 // Create a Sequence that contains 1 Task. |
131 scoped_refptr<Sequence> sequence(new Sequence); | 129 scoped_refptr<Sequence> sequence(new Sequence); |
132 task_tracker_.PostTask( | 130 task_tracker_.PostTask( |
133 Bind(IgnoreResult(&Sequence::PushTask), Unretained(sequence.get())), | 131 Bind(IgnoreResult(&Sequence::PushTask), Unretained(sequence.get())), |
134 WrapUnique(new Task( | 132 WrapUnique(new Task( |
135 FROM_HERE, Bind(&TaskSchedulerWorkerThreadTest::RunTaskCallback, | 133 FROM_HERE, Bind(&TaskSchedulerWorkerThreadTest::RunTaskCallback, |
136 Unretained(this)), | 134 Unretained(this)), |
137 TaskTraits()))); | 135 TaskTraits()))); |
138 | 136 |
139 { | 137 { |
140 // Add the Sequence to the vector of created Sequences. | 138 // Add the Sequence to the vector of created Sequences. |
141 AutoSchedulerLock auto_lock(lock_); | 139 AutoSchedulerLock auto_lock(lock_); |
142 created_sequences_.push_back(sequence); | 140 created_sequences_.push_back(sequence); |
143 } | 141 } |
144 | 142 |
145 return sequence; | 143 return sequence; |
146 } | 144 } |
147 | 145 |
148 void RanTaskFromSequenceCallback(const SchedulerWorkerThread* worker_thread, | 146 void RanTaskFromSequence(scoped_refptr<Sequence> sequence) override { |
149 scoped_refptr<Sequence> sequence) { | |
150 EXPECT_EQ(worker_thread_.get(), worker_thread); | |
151 | |
152 AutoSchedulerLock auto_lock(lock_); | 147 AutoSchedulerLock auto_lock(lock_); |
153 run_sequences_.push_back(std::move(sequence)); | 148 run_sequences_.push_back(std::move(sequence)); |
154 run_sequences_cv_->Signal(); | 149 run_sequences_cv_->Signal(); |
155 } | 150 } |
156 | 151 |
157 void RunTaskCallback() { | 152 void RunTaskCallback() { |
158 AutoSchedulerLock auto_lock(lock_); | 153 AutoSchedulerLock auto_lock(lock_); |
159 ++num_run_tasks_; | 154 ++num_run_tasks_; |
160 } | 155 } |
161 | 156 |
162 TaskTracker task_tracker_; | 157 TaskTracker task_tracker_; |
163 | 158 |
164 // Synchronizes access to all members below. | 159 // Synchronizes access to all members below. |
165 mutable SchedulerLock lock_; | 160 mutable SchedulerLock lock_; |
166 | 161 |
167 // Number of times that MainEntryCallback() has been called. | 162 // Signaled once OnMainEntry() has been called. |
168 size_t num_main_entry_callback_ = 0; | 163 WaitableEvent main_entry_called_; |
169 | 164 |
170 // Condition variable signaled when |num_main_entry_callback_| is incremented. | 165 // True once OnMainExit() has been called. |
171 std::unique_ptr<ConditionVariable> num_main_entry_callback_cv_; | 166 bool main_exit_called_ = false; |
172 | 167 |
173 // Number of Sequences that should be created by GetWorkCallback(). When this | 168 // Number of Sequences that should be created by GetWork(). When this |
174 // is 0, GetWorkCallback() returns nullptr. | 169 // is 0, GetWork() returns nullptr. |
175 size_t num_sequences_to_create_ = 0; | 170 size_t num_sequences_to_create_ = 0; |
176 | 171 |
177 // Number of times that GetWorkCallback() has been called. | 172 // Number of times that GetWork() has been called. |
178 size_t num_get_work_callback_ = 0; | 173 size_t num_get_work_ = 0; |
179 | 174 |
180 // Condition variable signaled when |num_get_work_callback_| is incremented. | 175 // Condition variable signaled when |num_get_work_| is incremented. |
181 std::unique_ptr<ConditionVariable> num_get_work_callback_cv_; | 176 std::unique_ptr<ConditionVariable> num_get_work_cv_; |
182 | 177 |
183 // Sequences created by GetWorkCallback(). | 178 // Sequences created by GetWork(). |
184 std::vector<scoped_refptr<Sequence>> created_sequences_; | 179 std::vector<scoped_refptr<Sequence>> created_sequences_; |
185 | 180 |
186 // Sequences passed to RanTaskFromSequenceCallback(). | 181 // Sequences passed to RanTaskFromSequence(). |
187 std::vector<scoped_refptr<Sequence>> run_sequences_; | 182 std::vector<scoped_refptr<Sequence>> run_sequences_; |
188 | 183 |
189 // Condition variable signaled when a Sequence is added to |run_sequences_|. | 184 // Condition variable signaled when a Sequence is added to |run_sequences_|. |
190 std::unique_ptr<ConditionVariable> run_sequences_cv_; | 185 std::unique_ptr<ConditionVariable> run_sequences_cv_; |
191 | 186 |
192 // Number of times that RunTaskCallback() has been called. | 187 // Number of times that RunTaskCallback() has been called. |
193 size_t num_run_tasks_ = 0; | 188 size_t num_run_tasks_ = 0; |
194 | 189 |
195 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); | 190 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); |
196 }; | 191 }; |
197 | 192 |
198 // Verify that when GetWorkCallback() continuously returns Sequences, all Tasks | 193 // Verify that when GetWork() continuously returns Sequences, all Tasks in these |
199 // in these Sequences run successfully. The SchedulerWorkerThread is woken up | 194 // Sequences run successfully. The SchedulerWorkerThread is woken up once. |
200 // once. | |
201 TEST_F(TaskSchedulerWorkerThreadTest, ContinousWork) { | 195 TEST_F(TaskSchedulerWorkerThreadTest, ContinousWork) { |
202 // Set GetWorkCallback() to return |kNumSequencesPerTest| Sequences before | 196 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to |
203 // starting to return nullptr. | 197 // return nullptr. |
204 SetNumSequencesToCreate(kNumSequencesPerTest); | 198 SetNumSequencesToCreate(kNumSequencesPerTest); |
205 | 199 |
206 // Wake up |worker_thread_| and wait until it has run all the Tasks returned | 200 // Wake up |worker_thread_| and wait until it has run all the Tasks returned |
207 // by GetWorkCallback(). | 201 // by GetWork(). |
208 worker_thread_->WakeUp(); | 202 worker_thread_->WakeUp(); |
209 WaitForAllSequencesToRun(); | 203 WaitForAllSequencesToRun(); |
210 | 204 |
211 // Expect |kNumSequencesPerTest| calls to GetWorkCallback() in which it | 205 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returned a |
212 // returned a Sequence and 1 call in which it returned nullptr. | 206 // Sequence and 1 call in which it returned nullptr. |
213 const size_t expected_num_get_work_callback = kNumSequencesPerTest + 1; | 207 const size_t expected_num_get_work = kNumSequencesPerTest + 1; |
214 WaitForNumGetWorkCallback(expected_num_get_work_callback); | 208 WaitForNumGetWork(expected_num_get_work); |
215 EXPECT_EQ(expected_num_get_work_callback, NumGetWorkCallback()); | 209 EXPECT_EQ(expected_num_get_work, NumGetWork()); |
216 } | 210 } |
217 | 211 |
218 // Verify that when GetWorkCallback() alternates between returning a Sequence | 212 // Verify that when GetWork() alternates between returning a Sequence and |
219 // and returning nullptr, all Tasks in the returned Sequences run successfully. | 213 // returning nullptr, all Tasks in the returned Sequences run successfully. The |
220 // The SchedulerWorkerThread is woken up once for each Sequence. | 214 // SchedulerWorkerThread is woken up once for each Sequence. |
221 TEST_F(TaskSchedulerWorkerThreadTest, IntermittentWork) { | 215 TEST_F(TaskSchedulerWorkerThreadTest, IntermittentWork) { |
222 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { | 216 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { |
223 // Set GetWorkCallback() to return 1 Sequence before starting to return | 217 // Set GetWork() to return 1 Sequence before starting to return |
224 // nullptr. | 218 // nullptr. |
225 SetNumSequencesToCreate(1); | 219 SetNumSequencesToCreate(1); |
226 | 220 |
227 // Wake up |worker_thread_| and wait until it has run all the Tasks returned | 221 // Wake up |worker_thread_| and wait until it has run the Task returned by |
228 // by GetWorkCallback(). | 222 // GetWork(). |
229 worker_thread_->WakeUp(); | 223 worker_thread_->WakeUp(); |
230 WaitForAllSequencesToRun(); | 224 WaitForAllSequencesToRun(); |
231 | 225 |
232 // Expect |i| calls to GetWorkCallback() in which it returned a Sequence and | 226 // Expect |i| calls to GetWork() in which it returned a Sequence and |
233 // |i| calls in which it returned nullptr. | 227 // |i| calls in which it returned nullptr. |
234 const size_t expected_num_get_work_callback = 2 * (i + 1); | 228 const size_t expected_num_get_work = 2 * (i + 1); |
235 WaitForNumGetWorkCallback(expected_num_get_work_callback); | 229 WaitForNumGetWork(expected_num_get_work); |
236 EXPECT_EQ(expected_num_get_work_callback, NumGetWorkCallback()); | 230 EXPECT_EQ(expected_num_get_work, NumGetWork()); |
237 } | 231 } |
238 } | 232 } |
239 | 233 |
240 } // namespace | 234 } // namespace |
241 } // namespace internal | 235 } // namespace internal |
242 } // namespace base | 236 } // namespace base |
OLD | NEW |