OLD | NEW |
---|---|
(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 } | |
OLD | NEW |