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