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

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, 1 month 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
jar (doing other things) 2011/11/23 20:08:16 nit: typo hangling->hanging
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 posting a bunch of tasks (many more than the number of worker
125 // threads) runs them all.
126 TEST_F(SequencedWorkerPoolTest, LotsOfTasks) {
127 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
128 base::Bind(&TestTracker::SlowTask, tracker(), 0));
129
130 const size_t kNumTasks = 17;
131 for (size_t i = 1; i < kNumTasks; i++) {
132 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
133 base::Bind(&TestTracker::FastTask, tracker(), i));
134 }
135
136 std::vector<int> result = tracker()->WaitUntilTasksComplete(kNumTasks);
137 EXPECT_EQ(kNumTasks, result.size());
138 }
139
140 // Test that tasks with the same sequence token are executed in order but don't
141 // affect other tasks.
142 TEST_F(SequencedWorkerPoolTest, Sequence) {
143 // Fill all the worker threads except one.
144 base::WaitableEvent background_event(false, false);
145 const size_t kNumBackgroundTasks = kNumWorkerThreads - 1;
146 for (size_t i = 0; i < kNumBackgroundTasks; i++) {
147 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
148 base::Bind(&TestTracker::BlockOnEventTask,
149 tracker(), i, &background_event));
150 }
151
152 // Create two tasks with the same sequence token, one that will block on the
153 // event, and one which will just complete quickly when it's run. Since there
154 // is one worker thread free, the first task will start and then block, and
155 // the second task should be waiting.
156 base::WaitableEvent event1(false, false);
157 SequencedWorkerPool::SequenceToken token1 = pool().GetSequenceToken();
158 pool().PostSequencedWorkerTask(
159 token1, SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
160 base::Bind(&TestTracker::BlockOnEventTask, tracker(), 100,
161 &event1));
162 pool().PostSequencedWorkerTask(
163 token1, SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
164 base::Bind(&TestTracker::FastTask, tracker(), 101));
165 EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
166
167 // Create another two tasks as above with a different token. These will be
168 // blocked since there are no slots to run.
169 SequencedWorkerPool::SequenceToken token2 = pool().GetSequenceToken();
170 pool().PostSequencedWorkerTask(
171 token2, SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
172 base::Bind(&TestTracker::FastTask, tracker(), 200));
173 pool().PostSequencedWorkerTask(
174 token2, SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
175 base::Bind(&TestTracker::FastTask, tracker(), 201));
176 EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
177
178 // Let one background task complete. This should then let both tasks of
179 // token2 run to completion in order. The second task of token1 should still
180 // be blocked.
181 background_event.Signal();
182 std::vector<int> result = tracker()->WaitUntilTasksComplete(3);
183 ASSERT_EQ(3u, result.size());
184 EXPECT_EQ(200, result[1]);
185 EXPECT_EQ(201, result[2]);
186
187 // Finish the rest of the background tasks. This should leave some workers
188 // free with the second token1 task still blocked on the first.
189 for (size_t i = 0; i < kNumBackgroundTasks - 1; i++)
190 background_event.Signal();
191 EXPECT_EQ(kNumBackgroundTasks + 2,
192 tracker()->WaitUntilTasksComplete(kNumBackgroundTasks + 2).size());
193
194 // Allow the first task of token1 to complete. This should run the second.
195 event1.Signal();
196 result = tracker()->WaitUntilTasksComplete(kNumBackgroundTasks + 4);
197 ASSERT_EQ(kNumBackgroundTasks + 4, result.size());
198 EXPECT_EQ(100, result[result.size() - 2]);
199 EXPECT_EQ(101, result[result.size() - 1]);
200 }
201
202 // Tests that unrun tasks are discarded properly according to their shutdown
203 // mode.
204 TEST_F(SequencedWorkerPoolTest, DiscardOnShutdown) {
205 // Start tasks to take all the threads and block them.
206 base::WaitableEvent background_event(false, false);
207 for (size_t i = 0; i < kNumWorkerThreads; i++) {
208 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
209 base::Bind(&TestTracker::BlockOnEventTask,
210 tracker(), i, &background_event));
211 }
212
213 // Create some tasks with different shutdown modes.
214 pool().PostWorkerTask(SequencedWorkerPool::CONTINUE_ON_SHUTDOWN, FROM_HERE,
215 base::Bind(&TestTracker::FastTask, tracker(), 100));
216 pool().PostWorkerTask(SequencedWorkerPool::SKIP_ON_SHUTDOWN, FROM_HERE,
217 base::Bind(&TestTracker::FastTask, tracker(), 101));
218 pool().PostWorkerTask(SequencedWorkerPool::BLOCK_SHUTDOWN, FROM_HERE,
219 base::Bind(&TestTracker::FastTask, tracker(), 102));
220
221 // Shutdown the woeker pool. This should discard all non-blocking tasks.
222 before_wait_for_shutdown_ =
223 base::Bind(&EnsureTasksToCompleteCountAndSignal, tracker(), 0,
224 &background_event, kNumWorkerThreads);
225 pool().Shutdown();
226
227 std::vector<int> result = tracker()->WaitUntilTasksComplete(4);
228 ASSERT_EQ(4, result.size());
229
230 // The last item should be the BLOCK_SHUTDOWN task, the other two should have
231 // been dropped.
232 EXPECT_EQ(102, result[3]);
233 }
234
235 // Tests that CONTINUE_ON_SHUTDOWN tasks don't block shutdown.
236 TEST_F(SequencedWorkerPoolTest, ContinueOnShutdown) {
237 base::WaitableEvent background_event(false, false);
238 pool().PostWorkerTask(SequencedWorkerPool::CONTINUE_ON_SHUTDOWN, FROM_HERE,
239 base::Bind(&TestTracker::BlockOnEventTask,
240 tracker(), 0, &background_event));
241
242 // This should not block. If this test hangs, it means it failed.
243 pool().Shutdown();
244
245 // The task should not have completed yet.
246 EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
247
248 // Posting more tasks should fail.
249 EXPECT_FALSE(pool().PostWorkerTask(SequencedWorkerPool::CONTINUE_ON_SHUTDOWN,
250 FROM_HERE, base::Bind(&TestTracker::FastTask, tracker(), 0)));
251
252 // Continue the background thread and make sure the task can complete.
253 background_event.Signal();
254 std::vector<int> result = tracker()->WaitUntilTasksComplete(1);
255 EXPECT_EQ(1, result.size());
256 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698