1 // Copyright 2013 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/basictypes.h" 6 #include "base/bind.h" 7 #include "base/bind_helpers.h" 8 #include "base/location.h" 9 #include "base/logging.h" 10 #include "base/memory/ref_counted.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/sequence_checker.h" 14 #include "base/test/sequenced_worker_pool_owner.h" 15 #include "base/threading/thread.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 18 // Duplicated from base/sequence_checker.h so that we can be good citizens 19 // there and undef the macro. 20 #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) 21 #define ENABLE_SEQUENCE_CHECKER 1 22 #else 23 #define ENABLE_SEQUENCE_CHECKER 0 24 #endif 25 26 namespace base { 27 28 namespace { 29 30 const size_t kNumWorkerThreads = 3; 31 32 // Simple class to exercise the basics of SequenceChecker. 33 // DoStuff should verify that it's called on a valid sequenced thread. 34 // SequenceCheckedObject can be destroyed on any thread (like WeakPtr). 35 class SequenceCheckedObject { 36 public: 37 SequenceCheckedObject() {} 38 ~SequenceCheckedObject() {} 39 40 // Verifies that it was called on the same thread as the constructor. 41 void DoStuff() { 42 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 43 } 44 45 void DetachFromSequence() { 46 sequence_checker_.DetachFromSequence(); 47 } 48 49 private: 50 SequenceChecker sequence_checker_; 51 52 DISALLOW_COPY_AND_ASSIGN(SequenceCheckedObject); 53 }; 54 55 class SequenceCheckerTest : public testing::Test { 56 public: 57 SequenceCheckerTest() : other_thread_("sequence_checker_test_other_thread") {} 58 59 virtual ~SequenceCheckerTest() {} 60 61 virtual void SetUp() OVERRIDE { 62 other_thread_.Start(); 63 ResetPool(); 64 } 65 66 virtual void TearDown() OVERRIDE { 67 other_thread_.Stop(); 68 pool()->Shutdown(); 69 } 70 71 protected: 72 base::Thread* other_thread() { return &other_thread_; } 73 74 const scoped_refptr<SequencedWorkerPool>& pool() { 75 return pool_owner_->pool(); 76 } 77 78 void PostDoStuffToWorkerPool(SequenceCheckedObject* sequence_checked_object, 79 const std::string& token_name) { 80 pool()->PostNamedSequencedWorkerTask( 81 token_name, 82 FROM_HERE, 83 base::Bind(&SequenceCheckedObject::DoStuff, 84 base::Unretained(sequence_checked_object))); 85 } 86 87 void PostDoStuffToOtherThread( 88 SequenceCheckedObject* sequence_checked_object) { 89 other_thread()->message_loop()->PostTask( 90 FROM_HERE, 91 base::Bind(&SequenceCheckedObject::DoStuff, 92 base::Unretained(sequence_checked_object))); 93 } 94 95 void PostDeleteToOtherThread( 96 scoped_ptr<SequenceCheckedObject> sequence_checked_object) { 97 other_thread()->message_loop()->DeleteSoon( 98 FROM_HERE, 99 sequence_checked_object.release()); 100 } 101 102 // Destroys the SequencedWorkerPool instance, blocking until it is fully shut 103 // down, and creates a new instance. 104 void ResetPool() { 105 pool_owner_.reset(new SequencedWorkerPoolOwner(kNumWorkerThreads, "test")); 106 } 107 108 void MethodOnDifferentThreadDeathTest(); 109 void DetachThenCallFromDifferentThreadDeathTest(); 110 void DifferentSequenceTokensDeathTest(); 111 void WorkerPoolAndSimpleThreadDeathTest(); 112 void TwoDifferentWorkerPoolsDeathTest(); 113 114 private: 115 MessageLoop message_loop_; // Needed by SequencedWorkerPool to function. 116 base::Thread other_thread_; 117 scoped_ptr<SequencedWorkerPoolOwner> pool_owner_; 118 }; 119 120 TEST_F(SequenceCheckerTest, CallsAllowedOnSameThread) { 121 scoped_ptr<SequenceCheckedObject> sequence_checked_object( 122 new SequenceCheckedObject); 123 124 // Verify that DoStuff doesn't assert. 125 sequence_checked_object->DoStuff(); 126 127 // Verify that the destructor doesn't assert. 128 sequence_checked_object.reset(); 129 } 130 131 TEST_F(SequenceCheckerTest, DestructorAllowedOnDifferentThread) { 132 scoped_ptr<SequenceCheckedObject> sequence_checked_object( 133 new SequenceCheckedObject); 134 135 // Verify the destructor doesn't assert when called on a different thread. 136 PostDeleteToOtherThread(sequence_checked_object.Pass()); 137 other_thread()->Stop(); 138 } 139 140 TEST_F(SequenceCheckerTest, DetachFromSequence) { 141 scoped_ptr<SequenceCheckedObject> sequence_checked_object( 142 new SequenceCheckedObject); 143 144 // Verify that DoStuff doesn't assert when called on a different thread after 145 // a call to DetachFromSequence. 146 sequence_checked_object->DetachFromSequence(); 147 148 PostDoStuffToOtherThread(sequence_checked_object.get()); 149 other_thread()->Stop(); 150 } 151 152 TEST_F(SequenceCheckerTest, SameSequenceTokenValid) { 153 scoped_ptr<SequenceCheckedObject> sequence_checked_object( 154 new SequenceCheckedObject); 155 156 sequence_checked_object->DetachFromSequence(); 157 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); 158 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); 159 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); 160 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); 161 pool()->FlushForTesting(); 162 163 PostDeleteToOtherThread(sequence_checked_object.Pass()); 164 other_thread()->Stop(); 165 } 166 167 TEST_F(SequenceCheckerTest, DetachSequenceTokenValid) { 168 scoped_ptr<SequenceCheckedObject> sequence_checked_object( 169 new SequenceCheckedObject); 170 171 sequence_checked_object->DetachFromSequence(); 172 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); 173 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); 174 pool()->FlushForTesting(); 175 176 sequence_checked_object->DetachFromSequence(); 177 PostDoStuffToWorkerPool(sequence_checked_object.get(), "B"); 178 PostDoStuffToWorkerPool(sequence_checked_object.get(), "B"); 179 pool()->FlushForTesting(); 180 181 PostDeleteToOtherThread(sequence_checked_object.Pass()); 182 other_thread()->Stop(); 183 } 184 185 #if GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER 186 187 void SequenceCheckerTest::MethodOnDifferentThreadDeathTest() { 188 scoped_ptr<SequenceCheckedObject> sequence_checked_object( 189 new SequenceCheckedObject); 190 191 // DoStuff should assert in debug builds only when called on a 192 // different thread. 193 PostDoStuffToOtherThread(sequence_checked_object.get()); 194 other_thread()->Stop(); 195 } 196 197 #if ENABLE_SEQUENCE_CHECKER 198 TEST_F(SequenceCheckerTest, MethodNotAllowedOnDifferentThreadDeathTestInDebug) { 199 // The default style "fast" does not support multi-threaded tests. 200 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; 201 ASSERT_DEATH({ 202 MethodOnDifferentThreadDeathTest(); 203 }, ""); 204 } 205 #else 206 TEST_F(SequenceCheckerTest, MethodAllowedOnDifferentThreadDeathTestInRelease) { 207 MethodOnDifferentThreadDeathTest(); 208 } 209 #endif // ENABLE_SEQUENCE_CHECKER 210 211 void SequenceCheckerTest::DetachThenCallFromDifferentThreadDeathTest() { 212 scoped_ptr<SequenceCheckedObject> sequence_checked_object( 213 new SequenceCheckedObject); 214 215 // DoStuff doesn't assert when called on a different thread 216 // after a call to DetachFromSequence. 217 sequence_checked_object->DetachFromSequence(); 218 PostDoStuffToOtherThread(sequence_checked_object.get()); 219 other_thread()->Stop(); 220 221 // DoStuff should assert in debug builds only after moving to 222 // another thread. 223 sequence_checked_object->DoStuff(); 224 } 225 226 #if ENABLE_SEQUENCE_CHECKER 227 TEST_F(SequenceCheckerTest, DetachFromSequenceDeathTestInDebug) { 228 // The default style "fast" does not support multi-threaded tests. 229 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; 230 ASSERT_DEATH({ 231 DetachThenCallFromDifferentThreadDeathTest(); 232 }, ""); 233 } 234 #else 235 TEST_F(SequenceCheckerTest, DetachFromThreadDeathTestInRelease) { 236 DetachThenCallFromDifferentThreadDeathTest(); 237 } 238 #endif // ENABLE_SEQUENCE_CHECKER 239 240 void SequenceCheckerTest::DifferentSequenceTokensDeathTest() { 241 scoped_ptr<SequenceCheckedObject> sequence_checked_object( 242 new SequenceCheckedObject); 243 244 sequence_checked_object->DetachFromSequence(); 245 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); 246 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); 247 PostDoStuffToWorkerPool(sequence_checked_object.get(), "B"); 248 PostDoStuffToWorkerPool(sequence_checked_object.get(), "B"); 249 pool()->FlushForTesting(); 250 251 PostDeleteToOtherThread(sequence_checked_object.Pass()); 252 other_thread()->Stop(); 253 } 254 255 #if ENABLE_SEQUENCE_CHECKER 256 TEST_F(SequenceCheckerTest, DifferentSequenceTokensDeathTestInDebug) { 257 // The default style "fast" does not support multi-threaded tests. 258 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; 259 ASSERT_DEATH({ 260 DifferentSequenceTokensDeathTest(); 261 }, ""); 262 } 263 #else 264 TEST_F(SequenceCheckerTest, 265 DifferentSequenceTokensDeathTestInRelease) { 266 DifferentSequenceTokensDeathTest(); 267 } 268 #endif // ENABLE_SEQUENCE_CHECKER 269 270 void SequenceCheckerTest::WorkerPoolAndSimpleThreadDeathTest() { 271 scoped_ptr<SequenceCheckedObject> sequence_checked_object( 272 new SequenceCheckedObject); 273 274 sequence_checked_object->DetachFromSequence(); 275 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); 276 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); 277 pool()->FlushForTesting(); 278 279 PostDoStuffToOtherThread(sequence_checked_object.get()); 280 other_thread()->Stop(); 281 } 282 283 #if ENABLE_SEQUENCE_CHECKER 284 TEST_F(SequenceCheckerTest, WorkerPoolAndSimpleThreadDeathTestInDebug) { 285 // The default style "fast" does not support multi-threaded tests. 286 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; 287 ASSERT_DEATH({ 288 WorkerPoolAndSimpleThreadDeathTest(); 289 }, ""); 290 } 291 #else 292 TEST_F(SequenceCheckerTest, 293 WorkerPoolAndSimpleThreadDeathTestInRelease) { 294 WorkerPoolAndSimpleThreadDeathTest(); 295 } 296 #endif // ENABLE_SEQUENCE_CHECKER 297 298 void SequenceCheckerTest::TwoDifferentWorkerPoolsDeathTest() { 299 scoped_ptr<SequenceCheckedObject> sequence_checked_object( 300 new SequenceCheckedObject); 301 302 sequence_checked_object->DetachFromSequence(); 303 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); 304 PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); 305 pool()->FlushForTesting(); 306 307 SequencedWorkerPoolOwner second_pool_owner(kNumWorkerThreads, "test2"); 308 second_pool_owner.pool()->PostNamedSequencedWorkerTask( 309 "A", 310 FROM_HERE, 311 base::Bind(&SequenceCheckedObject::DoStuff, 312 base::Unretained(sequence_checked_object.get()))); 313 second_pool_owner.pool()->FlushForTesting(); 314 second_pool_owner.pool()->Shutdown(); 315 } 316 317 #if ENABLE_SEQUENCE_CHECKER 318 TEST_F(SequenceCheckerTest, TwoDifferentWorkerPoolsDeathTestInDebug) { 319 // The default style "fast" does not support multi-threaded tests. 320 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; 321 ASSERT_DEATH({ 322 TwoDifferentWorkerPoolsDeathTest(); 323 }, ""); 324 } 325 #else 326 TEST_F(SequenceCheckerTest, 327 TwoDifferentWorkerPoolsDeathTestInRelease) { 328 TwoDifferentWorkerPoolsDeathTest(); 329 } 330 #endif // ENABLE_SEQUENCE_CHECKER 331 332 #endif // GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER 333 334 } // namespace 335 336 } // namespace base 337 338 // Just in case we ever get lumped together with other compilation units. 339 #undef ENABLE_SEQUENCE_CHECKER 340