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 |
| 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, "test"), |
| 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(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(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(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, FROM_HERE, |
| 188 base::Bind(&TestTracker::BlockOnEventTask, tracker(), 100, |
| 189 &event1)); |
| 190 pool().PostSequencedWorkerTask( |
| 191 token1, 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, FROM_HERE, |
| 200 base::Bind(&TestTracker::FastTask, tracker(), 200)); |
| 201 pool().PostSequencedWorkerTask( |
| 202 token2, 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(FROM_HERE, |
| 237 base::Bind(&TestTracker::BlockOnEventTask, |
| 238 tracker(), i, &background_event)); |
| 239 } |
| 240 |
| 241 // Create some tasks with different shutdown modes. |
| 242 pool().PostWorkerTaskWithShutdownBehavior( |
| 243 FROM_HERE, |
| 244 base::Bind(&TestTracker::FastTask, tracker(), 100), |
| 245 SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); |
| 246 pool().PostWorkerTaskWithShutdownBehavior( |
| 247 FROM_HERE, |
| 248 base::Bind(&TestTracker::FastTask, tracker(), 101), |
| 249 SequencedWorkerPool::SKIP_ON_SHUTDOWN); |
| 250 pool().PostWorkerTaskWithShutdownBehavior( |
| 251 FROM_HERE, |
| 252 base::Bind(&TestTracker::FastTask, tracker(), 102), |
| 253 SequencedWorkerPool::BLOCK_SHUTDOWN); |
| 254 |
| 255 // Shutdown the woeker pool. This should discard all non-blocking tasks. |
| 256 before_wait_for_shutdown_ = |
| 257 base::Bind(&EnsureTasksToCompleteCountAndSignal, tracker(), 0, |
| 258 &background_event, kNumWorkerThreads); |
| 259 pool().Shutdown(); |
| 260 |
| 261 std::vector<int> result = tracker()->WaitUntilTasksComplete(4); |
| 262 ASSERT_EQ(4, result.size()); |
| 263 |
| 264 // The last item should be the BLOCK_SHUTDOWN task, the other two should have |
| 265 // been dropped. |
| 266 EXPECT_EQ(102, result[3]); |
| 267 } |
| 268 |
| 269 // Tests that CONTINUE_ON_SHUTDOWN tasks don't block shutdown. |
| 270 TEST_F(SequencedWorkerPoolTest, ContinueOnShutdown) { |
| 271 base::WaitableEvent background_event(false, false); |
| 272 pool().PostWorkerTaskWithShutdownBehavior( |
| 273 FROM_HERE, |
| 274 base::Bind(&TestTracker::BlockOnEventTask, |
| 275 tracker(), 0, &background_event), |
| 276 SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); |
| 277 |
| 278 // This should not block. If this test hangs, it means it failed. |
| 279 pool().Shutdown(); |
| 280 |
| 281 // The task should not have completed yet. |
| 282 EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size()); |
| 283 |
| 284 // Posting more tasks should fail. |
| 285 EXPECT_FALSE(pool().PostWorkerTaskWithShutdownBehavior( |
| 286 FROM_HERE, base::Bind(&TestTracker::FastTask, tracker(), 0), |
| 287 SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)); |
| 288 |
| 289 // Continue the background thread and make sure the task can complete. |
| 290 background_event.Signal(); |
| 291 std::vector<int> result = tracker()->WaitUntilTasksComplete(1); |
| 292 EXPECT_EQ(1, result.size()); |
| 293 } |
| 294 |
OLD | NEW |