Home | History | Annotate | Download | only in threading
      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
     17 MSVC_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 
     37 namespace {
     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.
     42 class 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 
     60 TEST(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 
     76 TEST(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 
     98 TEST(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 
    128 TEST(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 
    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(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 
    239 TEST(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 
    307 TEST(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