Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(158)

Side by Side Diff: content/common/sequenced_worker_pool_unittest.cc

Issue 8416019: Add a sequenced worker pool (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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/bind.h"
6 #include "base/memory/ref_counted.h"
7 #include "base/synchronization/lock.h"
8 #include "base/synchronization/waitable_event.h"
9 #include "base/threading/platform_thread.h"
10 #include "content/common/sequenced_worker_pool.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12
13 // IMPORTANT NOTE:
14 //
15 // Many of these tests have failure modes where they'll hang forever. These
16 // tests should not be flaky, and hangling indicates a type of failure. Do not
17 // mark as flaky if they're hanging, it's likely an actual bug.
18
19 namespace {
20
21 const size_t kNumWorkerThreads = 3;
22
23 class TestTracker : public base::RefCountedThreadSafe<TestTracker> {
24 public:
25 TestTracker() : completed_event_(false, false) {
26 }
27
28 // Each of these tasks appends the argument to the complete sequence vector
29 // so calling code can see what order they finished in.
30 void FastTask(int id) {
31 SignalWorkerDone(id);
32 }
33 void SlowTask(int id) {
34 base::PlatformThread::Sleep(1000);
35 SignalWorkerDone(id);
36 }
37 void BlockOnEventTask(int id, base::WaitableEvent* event) {
38 event->Wait();
39 SignalWorkerDone(id);
40 }
41
42 // Blocks the current thread until at least the given number of tasks are in
43 // the completed vector, and then returns a copy.
44 std::vector<int> WaitUntilTasksComplete(size_t num_tasks) {
45 for (;;) {
46 {
47 base::AutoLock lock(lock_);
48 if (complete_sequence_.size() >= num_tasks)
49 return complete_sequence_;
50 }
51 completed_event_.Wait();
52 }
53 }
54
55 private:
56 void SignalWorkerDone(int id) {
57 base::AutoLock lock(lock_);
58 complete_sequence_.push_back(id);
59 completed_event_.Signal();
60 }
61
62 // Protects the complete_sequence.
63 base::Lock lock_;
64
65 // Signaled every time something is posted to the complete_sequence_.
66 base::WaitableEvent completed_event_;
67
68 // Protected by lock_.
69 std::vector<int> complete_sequence_;
70 };
71
72 class SequencedWorkerPoolTest : public testing::Test,
73 public SequencedWorkerPool::TestingObserver {
74 public:
75 SequencedWorkerPoolTest()
76 : pool_(kNumWorkerThreads),
77 tracker_(new TestTracker) {
78 pool_.SetTestingObserver(this);
79 }
80 ~SequencedWorkerPoolTest() {
81 }
82
83 virtual void SetUp() {
84 }
85 virtual void TearDown() {
86 }
87
88 SequencedWorkerPool& pool() { return pool_; }
89 TestTracker* tracker() { return tracker_.get(); }
90
91 protected:
92 // This closure will be executed right before the pool blocks on shutdown.
93 base::Closure before_wait_for_shutdown_;
94
95 private:
96 // SequencedWorkerPool::TestingObserver implementation.
97 virtual void WillWaitForShutdown() {
98 if (!before_wait_for_shutdown_.is_null())
99 before_wait_for_shutdown_.Run();
100 }
101
102 SequencedWorkerPool pool_;
103 scoped_refptr<TestTracker> tracker_;
104 };
105
106 // Checks that the given number of entries are in the tasks to complete of
107 // the given tracker, and then signals the given event the given number of
108 // times. This is used to wakt up blocked background threads before blocking
109 // on shutdown.
110 void EnsureTasksToCompleteCountAndSignal(TestTracker* tracker,
111 size_t expected_tasks_to_complete,
112 base::WaitableEvent* event,
113 size_t times_to_signal_event) {
114 EXPECT_EQ(
115 expected_tasks_to_complete,
116 tracker->WaitUntilTasksComplete(expected_tasks_to_complete).size());
117
118 for (size_t i = 0; i < times_to_signal_event; i++)
119 event->Signal();
120 }
121
122 } // namespace
123
124 // Tests that same-named tokens have the same ID.
125 TEST_F(SequencedWorkerPoolTest, NamedTokens) {
126 const std::string name1("hello");
127 SequencedWorkerPool::SequenceToken token1 =
128 pool().GetNamedSequenceToken(name1);
129
130 SequencedWorkerPool::SequenceToken token2 = pool().GetSequenceToken();
131
132 const std::string name3("goodbye");
133 SequencedWorkerPool::SequenceToken token3 =
134 pool().GetNamedSequenceToken(name3);
135
136 // All 3 tokens should be different.
137 EXPECT_FALSE(token1.Equals(token2));
138 EXPECT_FALSE(token1.Equals(token3));
139 EXPECT_FALSE(token2.Equals(token3));
140
141 // Requesting the same name again should give the same value.
142 SequencedWorkerPool::SequenceToken token1again =
143 pool().GetNamedSequenceToken(name1);
144 EXPECT_TRUE(token1.Equals(token1again));
145
146 SequencedWorkerPool::SequenceToken token3again =
147 pool().GetNamedSequenceToken(name3);
148 EXPECT_TRUE(token3.Equals(token3again));
149 }
150
151 // Tests that posting a bunch of tasks (many more than the number of worker
152 // threads) runs them all.
153 TEST_F(SequencedWorkerPoolTest, LotsOfTasks) {
154 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
155 base::Bind(&TestTracker::SlowTask, tracker(), 0));
156
157 const size_t kNumTasks = 20;
158 for (size_t i = 1; i < kNumTasks; i++) {
159 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
160 base::Bind(&TestTracker::FastTask, tracker(), i));
161 }
162
163 std::vector<int> result = tracker()->WaitUntilTasksComplete(kNumTasks);
164 EXPECT_EQ(kNumTasks, result.size());
165 }
166
167 // Test that tasks with the same sequence token are executed in order but don't
168 // affect other tasks.
169 TEST_F(SequencedWorkerPoolTest, Sequence) {
170 // Fill all the worker threads except one.
171 base::WaitableEvent background_event(false, false);
172 const size_t kNumBackgroundTasks = kNumWorkerThreads - 1;
173 for (size_t i = 0; i < kNumBackgroundTasks; i++) {
174 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
175 base::Bind(&TestTracker::BlockOnEventTask,
176 tracker(), i, &background_event));
177 }
178
179 // Create two tasks with the same sequence token, one that will block on the
180 // event, and one which will just complete quickly when it's run. Since there
181 // is one worker thread free, the first task will start and then block, and
182 // the second task should be waiting.
183 base::WaitableEvent event1(false, false);
184 SequencedWorkerPool::SequenceToken token1 = pool().GetSequenceToken();
185 pool().PostSequencedWorkerTask(
186 token1, SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
187 base::Bind(&TestTracker::BlockOnEventTask, tracker(), 100,
188 &event1));
189 pool().PostSequencedWorkerTask(
190 token1, SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
191 base::Bind(&TestTracker::FastTask, tracker(), 101));
192 EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
193
194 // Create another two tasks as above with a different token. These will be
195 // blocked since there are no slots to run.
196 SequencedWorkerPool::SequenceToken token2 = pool().GetSequenceToken();
197 pool().PostSequencedWorkerTask(
198 token2, SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
199 base::Bind(&TestTracker::FastTask, tracker(), 200));
200 pool().PostSequencedWorkerTask(
201 token2, SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
202 base::Bind(&TestTracker::FastTask, tracker(), 201));
203 EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
204
205 // Let one background task complete. This should then let both tasks of
206 // token2 run to completion in order. The second task of token1 should still
207 // be blocked.
208 background_event.Signal();
209 std::vector<int> result = tracker()->WaitUntilTasksComplete(3);
210 ASSERT_EQ(3u, result.size());
211 EXPECT_EQ(200, result[1]);
212 EXPECT_EQ(201, result[2]);
213
214 // Finish the rest of the background tasks. This should leave some workers
215 // free with the second token1 task still blocked on the first.
216 for (size_t i = 0; i < kNumBackgroundTasks - 1; i++)
217 background_event.Signal();
218 EXPECT_EQ(kNumBackgroundTasks + 2,
219 tracker()->WaitUntilTasksComplete(kNumBackgroundTasks + 2).size());
220
221 // Allow the first task of token1 to complete. This should run the second.
222 event1.Signal();
223 result = tracker()->WaitUntilTasksComplete(kNumBackgroundTasks + 4);
224 ASSERT_EQ(kNumBackgroundTasks + 4, result.size());
225 EXPECT_EQ(100, result[result.size() - 2]);
226 EXPECT_EQ(101, result[result.size() - 1]);
227 }
228
229 // Tests that unrun tasks are discarded properly according to their shutdown
230 // mode.
231 TEST_F(SequencedWorkerPoolTest, DiscardOnShutdown) {
232 // Start tasks to take all the threads and block them.
233 base::WaitableEvent background_event(false, false);
234 for (size_t i = 0; i < kNumWorkerThreads; i++) {
235 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
236 base::Bind(&TestTracker::BlockOnEventTask,
237 tracker(), i, &background_event));
238 }
239
240 // Create some tasks with different shutdown modes.
241 pool().PostWorkerTask(SequencedWorkerPool::CONTINUE_ON_SHUTDOWN, FROM_HERE,
242 base::Bind(&TestTracker::FastTask, tracker(), 100));
243 pool().PostWorkerTask(SequencedWorkerPool::SKIP_ON_SHUTDOWN, FROM_HERE,
244 base::Bind(&TestTracker::FastTask, tracker(), 101));
245 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
246 base::Bind(&TestTracker::FastTask, tracker(), 102));
247
248 // Shutdown the woeker pool. This should discard all non-blocking tasks.
249 before_wait_for_shutdown_ =
250 base::Bind(&EnsureTasksToCompleteCountAndSignal, tracker(), 0,
251 &background_event, kNumWorkerThreads);
252 pool().Shutdown();
253
254 std::vector<int> result = tracker()->WaitUntilTasksComplete(4);
255 ASSERT_EQ(4, result.size());
256
257 // The last item should be the BLOCK_SHUTDOWN task, the other two should have
258 // been dropped.
259 EXPECT_EQ(102, result[3]);
260 }
261
262 // Tests that CONTINUE_ON_SHUTDOWN tasks don't block shutdown.
263 TEST_F(SequencedWorkerPoolTest, ContinueOnShutdown) {
264 base::WaitableEvent background_event(false, false);
265 pool().PostWorkerTask(SequencedWorkerPool::CONTINUE_ON_SHUTDOWN, FROM_HERE,
266 base::Bind(&TestTracker::BlockOnEventTask,
267 tracker(), 0, &background_event));
268
269 // This should not block. If this test hangs, it means it failed.
270 pool().Shutdown();
271
272 // The task should not have completed yet.
273 EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
274
275 // Posting more tasks should fail.
276 EXPECT_FALSE(pool().PostWorkerTask(SequencedWorkerPool::CONTINUE_ON_SHUTDOWN,
277 FROM_HERE, base::Bind(&TestTracker::FastTask, tracker(), 0)));
278
279 // Continue the background thread and make sure the task can complete.
280 background_event.Signal();
281 std::vector<int> result = tracker()->WaitUntilTasksComplete(1);
282 EXPECT_EQ(1, result.size());
283 }
284
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698