| OLD | NEW |
| (Empty) |
| 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/lock.h" | |
| 7 #include "base/scoped_ptr.h" | |
| 8 #include "base/threading/platform_thread.h" | |
| 9 #include "base/threading/simple_thread.h" | |
| 10 #include "base/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, Lock& lock) | |
| 270 : queue_(queue), | |
| 271 lock_(lock) {} | |
| 272 | |
| 273 virtual void Run() { | |
| 274 { | |
| 275 AutoLock auto_lock(lock_); | |
| 276 queue_.push(0); | |
| 277 } | |
| 278 { | |
| 279 AutoLock auto_lock(lock_); | |
| 280 queue_.pop(); | |
| 281 } | |
| 282 } | |
| 283 private: | |
| 284 NonThreadSafeQueue& queue_; | |
| 285 Lock& lock_; | |
| 286 }; | |
| 287 | |
| 288 AssertReporter* local_reporter = new AssertReporter(); | |
| 289 | |
| 290 NonThreadSafeQueue queue(local_reporter); | |
| 291 | |
| 292 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, Lock& lock) | |
| 344 : queue_(queue), | |
| 345 lock_(lock) {} | |
| 346 | |
| 347 virtual void Run() { | |
| 348 { | |
| 349 AutoLock auto_lock(lock_); | |
| 350 queue_.push(0); | |
| 351 } | |
| 352 { | |
| 353 AutoLock auto_lock(lock_); | |
| 354 queue_.bar(); | |
| 355 } | |
| 356 { | |
| 357 AutoLock auto_lock(lock_); | |
| 358 queue_.pop(); | |
| 359 } | |
| 360 } | |
| 361 private: | |
| 362 NonThreadSafeQueue& queue_; | |
| 363 Lock& lock_; | |
| 364 }; | |
| 365 | |
| 366 AssertReporter* local_reporter = new AssertReporter(); | |
| 367 | |
| 368 NonThreadSafeQueue queue(local_reporter); | |
| 369 | |
| 370 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 } | |
| OLD | NEW |