thread_collision_warner_unittest.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2010 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/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 14MSVC_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 34namespace { 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. 39class 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 59TEST(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 75TEST(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 97TEST(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 127TEST(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 182TEST(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 240TEST(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 309TEST(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