Home | History | Annotate | Download | only in threading
      1 // Copyright (c) 2011 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/memory/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
     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, 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 
    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, 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