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

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 pool_.Shutdown();
87 }
88
89 SequencedWorkerPool& pool() { return pool_; }
90 TestTracker* tracker() { return tracker_.get(); }
91
92 protected:
93 // This closure will be executed right before the pool blocks on shutdown.
94 base::Closure before_wait_for_shutdown_;
95
96 private:
97 // SequencedWorkerPool::TestingObserver implementation.
98 virtual void WillWaitForShutdown() {
99 if (!before_wait_for_shutdown_.is_null())
100 before_wait_for_shutdown_.Run();
101 }
102
103 SequencedWorkerPool pool_;
104 scoped_refptr<TestTracker> tracker_;
105 };
106
107 // Checks that the given number of entries are in the tasks to complete of
108 // the given tracker, and then signals the given event the given number of
109 // times. This is used to wakt up blocked background threads before blocking
110 // on shutdown.
111 void EnsureTasksToCompleteCountAndSignal(TestTracker* tracker,
112 size_t expected_tasks_to_complete,
113 base::WaitableEvent* event,
114 size_t times_to_signal_event) {
115 EXPECT_EQ(
116 expected_tasks_to_complete,
117 tracker->WaitUntilTasksComplete(expected_tasks_to_complete).size());
118
119 for (size_t i = 0; i < times_to_signal_event; i++)
120 event->Signal();
121 }
122
123 } // namespace
124
125 // Tests that same-named tokens have the same ID.
126 TEST_F(SequencedWorkerPoolTest, NamedTokens) {
127 const std::string name1("hello");
128 SequencedWorkerPool::SequenceToken token1 =
129 pool().GetNamedSequenceToken(name1);
130
131 SequencedWorkerPool::SequenceToken token2 = pool().GetSequenceToken();
132
133 const std::string name3("goodbye");
134 SequencedWorkerPool::SequenceToken token3 =
135 pool().GetNamedSequenceToken(name3);
136
137 // All 3 tokens should be different.
138 EXPECT_FALSE(token1.Equals(token2));
139 EXPECT_FALSE(token1.Equals(token3));
140 EXPECT_FALSE(token2.Equals(token3));
141
142 // Requesting the same name again should give the same value.
143 SequencedWorkerPool::SequenceToken token1again =
144 pool().GetNamedSequenceToken(name1);
145 EXPECT_TRUE(token1.Equals(token1again));
146
147 SequencedWorkerPool::SequenceToken token3again =
148 pool().GetNamedSequenceToken(name3);
149 EXPECT_TRUE(token3.Equals(token3again));
150 }
151
152 // Tests that posting a bunch of tasks (many more than the number of worker
153 // threads) runs them all.
154 TEST_F(SequencedWorkerPoolTest, LotsOfTasks) {
155 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
156 base::Bind(&TestTracker::SlowTask, tracker(), 0));
157
158 const size_t kNumTasks = 20;
159 for (size_t i = 1; i < kNumTasks; i++) {
160 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
161 base::Bind(&TestTracker::FastTask, tracker(), i));
162 }
163
164 std::vector<int> result = tracker()->WaitUntilTasksComplete(kNumTasks);
165 EXPECT_EQ(kNumTasks, result.size());
166 }
167
168 // Test that tasks with the same sequence token are executed in order but don't
169 // affect other tasks.
170 TEST_F(SequencedWorkerPoolTest, Sequence) {
171 // Fill all the worker threads except one.
172 base::WaitableEvent background_event(false, false);
173 const size_t kNumBackgroundTasks = kNumWorkerThreads - 1;
174 for (size_t i = 0; i < kNumBackgroundTasks; i++) {
175 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
176 base::Bind(&TestTracker::BlockOnEventTask,
177 tracker(), i, &background_event));
178 }
179
180 // Create two tasks with the same sequence token, one that will block on the
181 // event, and one which will just complete quickly when it's run. Since there
182 // is one worker thread free, the first task will start and then block, and
183 // the second task should be waiting.
184 base::WaitableEvent event1(false, false);
185 SequencedWorkerPool::SequenceToken token1 = pool().GetSequenceToken();
186 pool().PostSequencedWorkerTask(
187 token1, SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
188 base::Bind(&TestTracker::BlockOnEventTask, tracker(), 100,
189 &event1));
190 pool().PostSequencedWorkerTask(
191 token1, SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
192 base::Bind(&TestTracker::FastTask, tracker(), 101));
193 EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
194
195 // Create another two tasks as above with a different token. These will be
196 // blocked since there are no slots to run.
197 SequencedWorkerPool::SequenceToken token2 = pool().GetSequenceToken();
198 pool().PostSequencedWorkerTask(
199 token2, SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
200 base::Bind(&TestTracker::FastTask, tracker(), 200));
201 pool().PostSequencedWorkerTask(
202 token2, SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
203 base::Bind(&TestTracker::FastTask, tracker(), 201));
204 EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
205
206 // Let one background task complete. This should then let both tasks of
207 // token2 run to completion in order. The second task of token1 should still
208 // be blocked.
209 background_event.Signal();
210 std::vector<int> result = tracker()->WaitUntilTasksComplete(3);
211 ASSERT_EQ(3u, result.size());
212 EXPECT_EQ(200, result[1]);
213 EXPECT_EQ(201, result[2]);
214
215 // Finish the rest of the background tasks. This should leave some workers
216 // free with the second token1 task still blocked on the first.
217 for (size_t i = 0; i < kNumBackgroundTasks - 1; i++)
218 background_event.Signal();
219 EXPECT_EQ(kNumBackgroundTasks + 2,
220 tracker()->WaitUntilTasksComplete(kNumBackgroundTasks + 2).size());
221
222 // Allow the first task of token1 to complete. This should run the second.
223 event1.Signal();
224 result = tracker()->WaitUntilTasksComplete(kNumBackgroundTasks + 4);
225 ASSERT_EQ(kNumBackgroundTasks + 4, result.size());
226 EXPECT_EQ(100, result[result.size() - 2]);
227 EXPECT_EQ(101, result[result.size() - 1]);
228 }
229
230 // Tests that unrun tasks are discarded properly according to their shutdown
231 // mode.
232 TEST_F(SequencedWorkerPoolTest, DiscardOnShutdown) {
233 // Start tasks to take all the threads and block them.
234 base::WaitableEvent background_event(false, false);
235 for (size_t i = 0; i < kNumWorkerThreads; i++) {
236 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
237 base::Bind(&TestTracker::BlockOnEventTask,
238 tracker(), i, &background_event));
239 }
240
241 // Create some tasks with different shutdown modes.
242 pool().PostWorkerTask(SequencedWorkerPool::CONTINUE_ON_SHUTDOWN, FROM_HERE,
243 base::Bind(&TestTracker::FastTask, tracker(), 100));
244 pool().PostWorkerTask(SequencedWorkerPool::SKIP_ON_SHUTDOWN, FROM_HERE,
245 base::Bind(&TestTracker::FastTask, tracker(), 101));
246 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
247 base::Bind(&TestTracker::FastTask, tracker(), 102));
248
249 // Shutdown the woeker pool. This should discard all non-blocking tasks.
250 before_wait_for_shutdown_ =
251 base::Bind(&EnsureTasksToCompleteCountAndSignal, tracker(), 0,
252 &background_event, kNumWorkerThreads);
253 pool().Shutdown();
254
255 std::vector<int> result = tracker()->WaitUntilTasksComplete(4);
256 ASSERT_EQ(4, result.size());
257
258 // The last item should be the BLOCK_SHUTDOWN task, the other two should have
259 // been dropped.
260 EXPECT_EQ(102, result[3]);
261 }
262
263 // Tests that CONTINUE_ON_SHUTDOWN tasks don't block shutdown.
264 TEST_F(SequencedWorkerPoolTest, ContinueOnShutdown) {
265 base::WaitableEvent background_event(false, false);
266 pool().PostWorkerTask(SequencedWorkerPool::CONTINUE_ON_SHUTDOWN, FROM_HERE,
267 base::Bind(&TestTracker::BlockOnEventTask,
268 tracker(), 0, &background_event));
269
270 // This should not block. If this test hangs, it means it failed.
271 pool().Shutdown();
272
273 // The task should not have completed yet.
274 EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
275
276 // Posting more tasks should fail.
277 EXPECT_FALSE(pool().PostWorkerTask(SequencedWorkerPool::CONTINUE_ON_SHUTDOWN,
278 FROM_HERE, base::Bind(&TestTracker::FastTask, tracker(), 0)));
279
280 // Continue the background thread and make sure the task can complete.
281 background_event.Signal();
282 std::vector<int> result = tracker()->WaitUntilTasksComplete(1);
283 EXPECT_EQ(1, result.size());
284 }
285
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698