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