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/worker_thread.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <vector> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/bind_helpers.h" | |
13 #include "base/macros.h" | |
14 #include "base/memory/scoped_ptr.h" | |
15 #include "base/synchronization/condition_variable.h" | |
16 #include "base/task_scheduler/scheduler_lock.h" | |
17 #include "base/task_scheduler/sequence.h" | |
18 #include "base/task_scheduler/task.h" | |
19 #include "base/task_scheduler/task_tracker.h" | |
20 #include "testing/gtest/include/gtest/gtest.h" | |
21 | |
22 namespace base { | |
23 namespace internal { | |
24 namespace { | |
25 | |
26 const size_t kNumSequencesPerTest = 150; | |
27 | |
28 class TaskSchedulerWorkerThreadTest : public testing::Test { | |
29 protected: | |
30 TaskSchedulerWorkerThreadTest() | |
31 : num_get_work_callback_cv_(lock_.CreateConditionVariable()), | |
32 run_sequences_cv_(lock_.CreateConditionVariable()) {} | |
33 | |
34 void SetUp() override { | |
35 worker_thread_ = WorkerThread::CreateWorkerThread( | |
36 ThreadPriority::NORMAL, | |
37 Bind(&TaskSchedulerWorkerThreadTest::GetWorkCallback, Unretained(this)), | |
38 Bind(&TaskSchedulerWorkerThreadTest::RanTaskFromSequenceCallback, | |
39 Unretained(this)), | |
40 &task_tracker_); | |
41 ASSERT_TRUE(worker_thread_); | |
42 } | |
43 | |
44 void TearDown() override { worker_thread_->JoinForTesting(); } | |
45 | |
46 // Wait until GetWorkCallback() has been called |num_get_work_callback| times. | |
47 void WaitForNumGetWorkCallback(size_t num_get_work_callback) { | |
48 AutoSchedulerLock auto_lock(lock_); | |
49 while (num_get_work_callback_ < num_get_work_callback) | |
50 num_get_work_callback_cv_->Wait(); | |
51 } | |
52 | |
53 // Wait until there is no more Sequences to create and | |
54 // RanTaskFromSequenceCallback() has been invoked once for each Sequence | |
55 // returned by GetWorkCallback(). | |
56 void WaitForAllSequencesToRun() { | |
57 AutoSchedulerLock auto_lock(lock_); | |
58 | |
59 while (num_sequences_to_create_ > 0 || | |
60 run_sequences_.size() < created_sequences_.size()) { | |
61 run_sequences_cv_->Wait(); | |
62 } | |
63 | |
64 // Verify that RanTaskFromSequenceCallback() has been invoked with the | |
65 // same Sequences that were returned by GetWorkCallback(). | |
66 EXPECT_EQ(created_sequences_, run_sequences_); | |
67 | |
68 // Verify that RunTaskCallback() has been invoked once for each Sequence | |
69 // returned by GetWorkCallback(). | |
70 EXPECT_EQ(created_sequences_.size(), num_run_tasks_); | |
71 } | |
72 | |
73 void set_num_sequences_to_create(size_t num_sequences_to_create) { | |
danakj
2016/03/31 00:25:14
nit: SetNumSequencesToCreate. this isn't just a si
fdoray
2016/03/31 03:26:31
Done.
| |
74 AutoSchedulerLock auto_lock(lock_); | |
75 EXPECT_EQ(0U, num_sequences_to_create_); | |
76 num_sequences_to_create_ = num_sequences_to_create; | |
77 } | |
78 | |
79 size_t num_get_work_callback() const { | |
danakj
2016/03/31 00:25:13
nitto, NumGetWorkCallback, locking make this not "
fdoray
2016/03/31 03:26:31
Done.
| |
80 AutoSchedulerLock auto_lock(lock_); | |
81 return num_get_work_callback_; | |
82 } | |
83 | |
84 scoped_ptr<WorkerThread> worker_thread_; | |
85 | |
86 private: | |
87 // Returns a Sequence that contains 1 Task if |num_sequences_to_create_| is | |
88 // greater than 0. | |
89 scoped_refptr<Sequence> GetWorkCallback(const WorkerThread* worker_thread) { | |
90 EXPECT_EQ(worker_thread_.get(), worker_thread); | |
91 | |
92 { | |
93 AutoSchedulerLock auto_lock(lock_); | |
danakj
2016/03/31 00:25:13
Am I imagining deadlocks here? WaitForNumGetWorkCa
fdoray
2016/03/31 03:26:31
It is a requirement of ConditionVariable to acquir
fdoray
2016/03/31 13:29:24
In fact, holding the ConditionVariable's lock is o
danakj
2016/03/31 22:33:49
oh.. right I was missing that the CV is tied to th
| |
94 | |
95 // Increment the number of times that this callback has been invoked. | |
96 ++num_get_work_callback_; | |
97 num_get_work_callback_cv_->Signal(); | |
98 | |
99 // Check if a Sequence should be returned. | |
100 if (num_sequences_to_create_ == 0) | |
101 return scoped_refptr<Sequence>(); | |
danakj
2016/03/31 00:25:13
return nullptr;
fdoray
2016/03/31 03:26:31
Done.
| |
102 --num_sequences_to_create_; | |
103 } | |
104 | |
105 // Create a Sequence that contains 1 Task. | |
106 scoped_refptr<Sequence> sequence(new Sequence); | |
107 task_tracker_.PostTask( | |
108 Bind(IgnoreResult(&Sequence::PushTask), Unretained(sequence.get())), | |
109 make_scoped_ptr(new Task( | |
110 FROM_HERE, Bind(&TaskSchedulerWorkerThreadTest::RunTaskCallback, | |
111 Unretained(this)), | |
112 TaskTraits()))); | |
113 | |
114 { | |
115 // Add the Sequence to the vector of created Sequences. | |
116 AutoSchedulerLock auto_lock(lock_); | |
117 created_sequences_.push_back(sequence); | |
118 } | |
119 | |
120 return sequence; | |
121 } | |
122 | |
123 void RanTaskFromSequenceCallback(const WorkerThread* worker_thread, | |
124 scoped_refptr<Sequence> sequence) { | |
125 EXPECT_EQ(worker_thread_.get(), worker_thread); | |
126 | |
127 AutoSchedulerLock auto_lock(lock_); | |
128 run_sequences_.push_back(std::move(sequence)); | |
129 run_sequences_cv_->Signal(); | |
130 } | |
131 | |
132 void RunTaskCallback() { | |
133 AutoSchedulerLock auto_lock(lock_); | |
134 ++num_run_tasks_; | |
135 } | |
136 | |
137 TaskTracker task_tracker_; | |
138 | |
139 // Synchronizes access to all members below. | |
140 mutable SchedulerLock lock_; | |
141 | |
142 // Number of Sequences that should be created by GetWorkCallback(). When this | |
143 // is 0, GetWorkCallback() returns nullptr. | |
144 size_t num_sequences_to_create_ = 0; | |
145 | |
146 // Number of times that GetWorkCallback() has been called. | |
147 size_t num_get_work_callback_ = 0; | |
148 | |
149 // Condition variable signaled when |num_get_work_callback_| is incremented. | |
150 scoped_ptr<ConditionVariable> num_get_work_callback_cv_; | |
151 | |
152 // Sequences created by GetWorkCallback(). | |
153 std::vector<scoped_refptr<Sequence>> created_sequences_; | |
154 | |
155 // Sequences passed to RanTaskFromSequenceCallback(). | |
156 std::vector<scoped_refptr<Sequence>> run_sequences_; | |
157 | |
158 // Condition variable signaled when a Sequence is added to |run_sequences_|. | |
159 scoped_ptr<ConditionVariable> run_sequences_cv_; | |
160 | |
161 // Number of times that RunTaskCallback() has been called. | |
162 size_t num_run_tasks_ = 0; | |
163 | |
164 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); | |
165 }; | |
166 | |
167 // Verify that when GetWorkCallback() continuously returns Sequences, all Tasks | |
168 // in these Sequences run successfully. The WorkerThread is woken up once. | |
169 TEST_F(TaskSchedulerWorkerThreadTest, ContinousWork) { | |
170 // Set GetWorkCallback() to return |kNumSequencesPerTest| Sequences before | |
171 // starting to return nullptr. | |
172 set_num_sequences_to_create(kNumSequencesPerTest); | |
173 | |
174 // Wake up |worker_thread_| and wait until it has run all the Tasks returned | |
175 // by GetWorkCallback(). | |
176 worker_thread_->WakeUp(); | |
177 WaitForAllSequencesToRun(); | |
178 | |
179 // Expect |kNumSequencesPerTest| calls to GetWorkCallback() in which it | |
180 // returned a Sequence and 1 call in which it returned nullptr. | |
181 const size_t expected_num_get_work_callback = kNumSequencesPerTest + 1; | |
182 WaitForNumGetWorkCallback(expected_num_get_work_callback); | |
183 EXPECT_EQ(expected_num_get_work_callback, num_get_work_callback()); | |
184 } | |
185 | |
186 // Verify that when GetWorkCallback() alternates between returning a Sequence | |
187 // and returning nullptr, all Tasks in the returned Sequences run successfully. | |
188 // The WorkerThread is woken up once for each Sequence. | |
189 TEST_F(TaskSchedulerWorkerThreadTest, IntermittentWork) { | |
190 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { | |
191 // Set GetWorkCallback() to return 1 Sequence before starting to return | |
192 // nullptr. | |
193 set_num_sequences_to_create(1); | |
194 | |
195 // Wake up |worker_thread_| and wait until it has run all the Tasks returned | |
196 // by GetWorkCallback(). | |
197 worker_thread_->WakeUp(); | |
198 WaitForAllSequencesToRun(); | |
199 | |
200 // Let the WorkerThread go to sleep. | |
danakj
2016/03/31 00:25:13
?
This looks like a flakey way to avoid races, bu
fdoray
2016/03/31 03:26:31
Removed it. The goal was to give enough time to Wo
| |
201 PlatformThread::Sleep(TimeDelta::FromMilliseconds(25)); | |
202 | |
203 // Expect |i| calls to GetWorkCallback() in which it returned a Sequence and | |
204 // |i| calls in which it returned nullptr. | |
205 const size_t expected_num_get_work_callback = 2 * (i + 1); | |
206 WaitForNumGetWorkCallback(expected_num_get_work_callback); | |
207 EXPECT_EQ(expected_num_get_work_callback, num_get_work_callback()); | |
208 } | |
209 } | |
210 | |
211 } // namespace | |
212 } // namespace internal | |
213 } // namespace base | |
OLD | NEW |