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