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