1 // Copyright (c) 2012 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/compiler_specific.h" 6 #include "base/macros.h" 7 #include "base/memory/scoped_ptr.h" 8 #include "base/synchronization/lock.h" 9 #include "base/threading/platform_thread.h" 10 #include "base/threading/simple_thread.h" 11 #include "base/threading/thread_collision_warner.h" 12 #include "testing/gtest/include/gtest/gtest.h" 13 14 // '' : local class member function does not have a body 15 MSVC_PUSH_DISABLE_WARNING(4822) 16 17 18 #if defined(NDEBUG) 19 20 // Would cause a memory leak otherwise. 21 #undef DFAKE_MUTEX 22 #define DFAKE_MUTEX(obj) scoped_ptr<base::AsserterBase> obj 23 24 // In Release, we expect the AsserterBase::warn() to not happen. 25 #define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_FALSE 26 27 #else 28 29 // In Debug, we expect the AsserterBase::warn() to happen. 30 #define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_TRUE 31 32 #endif 33 34 35 namespace { 36 37 // This is the asserter used with ThreadCollisionWarner instead of the default 38 // DCheckAsserter. The method fail_state is used to know if a collision took 39 // place. 40 class AssertReporter : public base::AsserterBase { 41 public: 42 AssertReporter() 43 : failed_(false) {} 44 45 void warn() override { failed_ = true; } 46 47 ~AssertReporter() override {} 48 49 bool fail_state() const { return failed_; } 50 void reset() { failed_ = false; } 51 52 private: 53 bool failed_; 54 }; 55 56 } // namespace 57 58 TEST(ThreadCollisionTest, BookCriticalSection) { 59 AssertReporter* local_reporter = new AssertReporter(); 60 61 base::ThreadCollisionWarner warner(local_reporter); 62 EXPECT_FALSE(local_reporter->fail_state()); 63 64 { // Pin section. 65 DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner); 66 EXPECT_FALSE(local_reporter->fail_state()); 67 { // Pin section. 68 DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner); 69 EXPECT_FALSE(local_reporter->fail_state()); 70 } 71 } 72 } 73 74 TEST(ThreadCollisionTest, ScopedRecursiveBookCriticalSection) { 75 AssertReporter* local_reporter = new AssertReporter(); 76 77 base::ThreadCollisionWarner warner(local_reporter); 78 EXPECT_FALSE(local_reporter->fail_state()); 79 80 { // Pin section. 81 DFAKE_SCOPED_RECURSIVE_LOCK(warner); 82 EXPECT_FALSE(local_reporter->fail_state()); 83 { // Pin section again (allowed by DFAKE_SCOPED_RECURSIVE_LOCK) 84 DFAKE_SCOPED_RECURSIVE_LOCK(warner); 85 EXPECT_FALSE(local_reporter->fail_state()); 86 } // Unpin section. 87 } // Unpin section. 88 89 // Check that section is not pinned 90 { // Pin section. 91 DFAKE_SCOPED_LOCK(warner); 92 EXPECT_FALSE(local_reporter->fail_state()); 93 } // Unpin section. 94 } 95 96 TEST(ThreadCollisionTest, ScopedBookCriticalSection) { 97 AssertReporter* local_reporter = new AssertReporter(); 98 99 base::ThreadCollisionWarner warner(local_reporter); 100 EXPECT_FALSE(local_reporter->fail_state()); 101 102 { // Pin section. 103 DFAKE_SCOPED_LOCK(warner); 104 EXPECT_FALSE(local_reporter->fail_state()); 105 } // Unpin section. 106 107 { // Pin section. 108 DFAKE_SCOPED_LOCK(warner); 109 EXPECT_FALSE(local_reporter->fail_state()); 110 { 111 // Pin section again (not allowed by DFAKE_SCOPED_LOCK) 112 DFAKE_SCOPED_LOCK(warner); 113 EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state()); 114 // Reset the status of warner for further tests. 115 local_reporter->reset(); 116 } // Unpin section. 117 } // Unpin section. 118 119 { 120 // Pin section. 121 DFAKE_SCOPED_LOCK(warner); 122 EXPECT_FALSE(local_reporter->fail_state()); 123 } // Unpin section. 124 } 125 126 TEST(ThreadCollisionTest, MTBookCriticalSectionTest) { 127 class NonThreadSafeQueue { 128 public: 129 explicit NonThreadSafeQueue(base::AsserterBase* asserter) 130 : push_pop_(asserter) { 131 } 132 133 void push(int value) { 134 DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); 135 } 136 137 int pop() { 138 DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); 139 return 0; 140 } 141 142 private: 143 DFAKE_MUTEX(push_pop_); 144 145 DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue); 146 }; 147 148 class QueueUser : public base::DelegateSimpleThread::Delegate { 149 public: 150 explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {} 151 152 void Run() override { 153 queue_->push(0); 154 queue_->pop(); 155 } 156 157 private: 158 NonThreadSafeQueue* queue_; 159 }; 160 161 AssertReporter* local_reporter = new AssertReporter(); 162 163 NonThreadSafeQueue queue(local_reporter); 164 165 QueueUser queue_user_a(&queue); 166 QueueUser queue_user_b(&queue); 167 168 base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); 169 base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); 170 171 thread_a.Start(); 172 thread_b.Start(); 173 174 thread_a.Join(); 175 thread_b.Join(); 176 177 EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state()); 178 } 179 180 TEST(ThreadCollisionTest, MTScopedBookCriticalSectionTest) { 181 // Queue with a 5 seconds push execution time, hopefuly the two used threads 182 // in the test will enter the push at same time. 183 class NonThreadSafeQueue { 184 public: 185 explicit NonThreadSafeQueue(base::AsserterBase* asserter) 186 : push_pop_(asserter) { 187 } 188 189 void push(int value) { 190 DFAKE_SCOPED_LOCK(push_pop_); 191 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5)); 192 } 193 194 int pop() { 195 DFAKE_SCOPED_LOCK(push_pop_); 196 return 0; 197 } 198 199 private: 200 DFAKE_MUTEX(push_pop_); 201 202 DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue); 203 }; 204 205 class QueueUser : public base::DelegateSimpleThread::Delegate { 206 public: 207 explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {} 208 209 void Run() override { 210 queue_->push(0); 211 queue_->pop(); 212 } 213 214 private: 215 NonThreadSafeQueue* queue_; 216 }; 217 218 AssertReporter* local_reporter = new AssertReporter(); 219 220 NonThreadSafeQueue queue(local_reporter); 221 222 QueueUser queue_user_a(&queue); 223 QueueUser queue_user_b(&queue); 224 225 base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); 226 base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); 227 228 thread_a.Start(); 229 thread_b.Start(); 230 231 thread_a.Join(); 232 thread_b.Join(); 233 234 EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state()); 235 } 236 237 TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) { 238 // Queue with a 2 seconds push execution time, hopefuly the two used threads 239 // in the test will enter the push at same time. 240 class NonThreadSafeQueue { 241 public: 242 explicit NonThreadSafeQueue(base::AsserterBase* asserter) 243 : push_pop_(asserter) { 244 } 245 246 void push(int value) { 247 DFAKE_SCOPED_LOCK(push_pop_); 248 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(2)); 249 } 250 251 int pop() { 252 DFAKE_SCOPED_LOCK(push_pop_); 253 return 0; 254 } 255 256 private: 257 DFAKE_MUTEX(push_pop_); 258 259 DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue); 260 }; 261 262 // This time the QueueUser class protects the non thread safe queue with 263 // a lock. 264 class QueueUser : public base::DelegateSimpleThread::Delegate { 265 public: 266 QueueUser(NonThreadSafeQueue* queue, base::Lock* lock) 267 : queue_(queue), lock_(lock) {} 268 269 void Run() override { 270 { 271 base::AutoLock auto_lock(*lock_); 272 queue_->push(0); 273 } 274 { 275 base::AutoLock auto_lock(*lock_); 276 queue_->pop(); 277 } 278 } 279 private: 280 NonThreadSafeQueue* queue_; 281 base::Lock* lock_; 282 }; 283 284 AssertReporter* local_reporter = new AssertReporter(); 285 286 NonThreadSafeQueue queue(local_reporter); 287 288 base::Lock lock; 289 290 QueueUser queue_user_a(&queue, &lock); 291 QueueUser queue_user_b(&queue, &lock); 292 293 base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); 294 base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); 295 296 thread_a.Start(); 297 thread_b.Start(); 298 299 thread_a.Join(); 300 thread_b.Join(); 301 302 EXPECT_FALSE(local_reporter->fail_state()); 303 } 304 305 TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) { 306 // Queue with a 2 seconds push execution time, hopefuly the two used threads 307 // in the test will enter the push at same time. 308 class NonThreadSafeQueue { 309 public: 310 explicit NonThreadSafeQueue(base::AsserterBase* asserter) 311 : push_pop_(asserter) { 312 } 313 314 void push(int) { 315 DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); 316 bar(); 317 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(2)); 318 } 319 320 int pop() { 321 DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); 322 return 0; 323 } 324 325 void bar() { 326 DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); 327 } 328 329 private: 330 DFAKE_MUTEX(push_pop_); 331 332 DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue); 333 }; 334 335 // This time the QueueUser class protects the non thread safe queue with 336 // a lock. 337 class QueueUser : public base::DelegateSimpleThread::Delegate { 338 public: 339 QueueUser(NonThreadSafeQueue* queue, base::Lock* lock) 340 : queue_(queue), lock_(lock) {} 341 342 void Run() override { 343 { 344 base::AutoLock auto_lock(*lock_); 345 queue_->push(0); 346 } 347 { 348 base::AutoLock auto_lock(*lock_); 349 queue_->bar(); 350 } 351 { 352 base::AutoLock auto_lock(*lock_); 353 queue_->pop(); 354 } 355 } 356 private: 357 NonThreadSafeQueue* queue_; 358 base::Lock* lock_; 359 }; 360 361 AssertReporter* local_reporter = new AssertReporter(); 362 363 NonThreadSafeQueue queue(local_reporter); 364 365 base::Lock lock; 366 367 QueueUser queue_user_a(&queue, &lock); 368 QueueUser queue_user_b(&queue, &lock); 369 370 base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); 371 base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); 372 373 thread_a.Start(); 374 thread_b.Start(); 375 376 thread_a.Join(); 377 thread_b.Join(); 378 379 EXPECT_FALSE(local_reporter->fail_state()); 380 } 381