Home | History | Annotate | Download | only in tests
      1 // Copyright 2014 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 "mojo/public/cpp/utility/mutex.h"
      6 
      7 #include <stdlib.h>  // For |rand()|.
      8 #include <time.h>  // For |nanosleep()| (defined by POSIX).
      9 
     10 #include <vector>
     11 
     12 #include "mojo/public/cpp/system/macros.h"
     13 #include "mojo/public/cpp/utility/thread.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 
     16 namespace mojo {
     17 namespace {
     18 
     19 TEST(MutexTest, TrivialSingleThreaded) {
     20   Mutex mutex;
     21 
     22   mutex.Lock();
     23   mutex.AssertHeld();
     24   mutex.Unlock();
     25 
     26   EXPECT_TRUE(mutex.TryLock());
     27   mutex.AssertHeld();
     28   mutex.Unlock();
     29 
     30   {
     31     MutexLock lock(&mutex);
     32     mutex.AssertHeld();
     33   }
     34 
     35   EXPECT_TRUE(mutex.TryLock());
     36   mutex.Unlock();
     37 }
     38 
     39 class Fiddler {
     40  public:
     41   enum Type { kTypeLock, kTypeTry };
     42   Fiddler(size_t times_to_lock,
     43           Type type,
     44           bool should_sleep,
     45           Mutex* mutex,
     46           int* shared_value)
     47       : times_to_lock_(times_to_lock),
     48         type_(type),
     49         should_sleep_(should_sleep),
     50         mutex_(mutex),
     51         shared_value_(shared_value) {
     52   }
     53 
     54   ~Fiddler() {
     55   }
     56 
     57   void Fiddle() {
     58     for (size_t i = 0; i < times_to_lock_;) {
     59       switch (type_) {
     60         case kTypeLock: {
     61           mutex_->Lock();
     62           int old_shared_value = *shared_value_;
     63           if (should_sleep_)
     64             SleepALittle();
     65           *shared_value_ = old_shared_value + 1;
     66           mutex_->Unlock();
     67           i++;
     68           break;
     69         }
     70         case kTypeTry:
     71           if (mutex_->TryLock()) {
     72             int old_shared_value = *shared_value_;
     73             if (should_sleep_)
     74               SleepALittle();
     75             *shared_value_ = old_shared_value + 1;
     76             mutex_->Unlock();
     77             i++;
     78           } else {
     79             SleepALittle();  // Don't spin.
     80           }
     81           break;
     82       }
     83     }
     84   }
     85 
     86  private:
     87   static void SleepALittle() {
     88     static const long kNanosPerMilli = 1000000;
     89     struct timespec req = {
     90       0,  // Seconds.
     91       (rand() % 10) * kNanosPerMilli // Nanoseconds.
     92     };
     93     int rv MOJO_ALLOW_UNUSED = nanosleep(&req, NULL);
     94     assert(rv == 0);
     95   }
     96 
     97   const size_t times_to_lock_;
     98   const Type type_;
     99   const bool should_sleep_;
    100   Mutex* const mutex_;
    101   int* const shared_value_;
    102 
    103   MOJO_DISALLOW_COPY_AND_ASSIGN(Fiddler);
    104 };
    105 
    106 class FiddlerThread : public Thread {
    107  public:
    108   // Takes ownership of |fiddler|.
    109   FiddlerThread(Fiddler* fiddler)
    110       : fiddler_(fiddler) {
    111   }
    112 
    113   virtual ~FiddlerThread() {
    114     delete fiddler_;
    115   }
    116 
    117   virtual void Run() MOJO_OVERRIDE {
    118     fiddler_->Fiddle();
    119   }
    120 
    121  private:
    122   Fiddler* const fiddler_;
    123 
    124   MOJO_DISALLOW_COPY_AND_ASSIGN(FiddlerThread);
    125 };
    126 
    127 // This does a stress test (that also checks exclusion).
    128 TEST(MutexTest, ThreadedStress) {
    129   static const size_t kNumThreads = 20;
    130   static const int kTimesToLockEach = 20;
    131   assert(kNumThreads % 4 == 0);
    132 
    133   Mutex mutex;
    134   int shared_value = 0;
    135 
    136   std::vector<FiddlerThread*> fiddler_threads;
    137 
    138   for (size_t i = 0; i < kNumThreads; i += 4) {
    139     fiddler_threads.push_back(new FiddlerThread(new Fiddler(
    140         kTimesToLockEach, Fiddler::kTypeLock, false, &mutex, &shared_value)));
    141     fiddler_threads.push_back(new FiddlerThread(new Fiddler(
    142         kTimesToLockEach, Fiddler::kTypeTry, false, &mutex, &shared_value)));
    143     fiddler_threads.push_back(new FiddlerThread(new Fiddler(
    144         kTimesToLockEach, Fiddler::kTypeLock, true, &mutex, &shared_value)));
    145     fiddler_threads.push_back(new FiddlerThread(new Fiddler(
    146         kTimesToLockEach, Fiddler::kTypeTry, true, &mutex, &shared_value)));
    147   }
    148 
    149   for (size_t i = 0; i < kNumThreads; i++)
    150     fiddler_threads[i]->Start();
    151 
    152   // Do some fiddling ourselves.
    153   Fiddler(kTimesToLockEach, Fiddler::kTypeLock, true, &mutex, &shared_value)
    154       .Fiddle();
    155 
    156   // Join.
    157   for (size_t i = 0; i < kNumThreads; i++)
    158     fiddler_threads[i]->Join();
    159 
    160   EXPECT_EQ(static_cast<int>(kNumThreads + 1) * kTimesToLockEach, shared_value);
    161 
    162   // Delete.
    163   for (size_t i = 0; i < kNumThreads; i++)
    164     delete fiddler_threads[i];
    165   fiddler_threads.clear();
    166 }
    167 
    168 class TryThread : public Thread {
    169  public:
    170   explicit TryThread(Mutex* mutex) : mutex_(mutex), try_lock_succeeded_() {}
    171   virtual ~TryThread() {}
    172 
    173   virtual void Run() MOJO_OVERRIDE {
    174     try_lock_succeeded_ = mutex_->TryLock();
    175     if (try_lock_succeeded_)
    176       mutex_->Unlock();
    177   }
    178 
    179   bool try_lock_succeeded() const { return try_lock_succeeded_; }
    180 
    181  private:
    182   Mutex* const mutex_;
    183   bool try_lock_succeeded_;
    184 
    185   MOJO_DISALLOW_COPY_AND_ASSIGN(TryThread);
    186 };
    187 
    188 TEST(MutexTest, TryLock) {
    189   Mutex mutex;
    190 
    191   // |TryLock()| should succeed -- we don't have the lock.
    192   {
    193     TryThread thread(&mutex);
    194     thread.Start();
    195     thread.Join();
    196     EXPECT_TRUE(thread.try_lock_succeeded());
    197   }
    198 
    199   // Take the lock.
    200   ASSERT_TRUE(mutex.TryLock());
    201 
    202   // Now it should fail.
    203   {
    204     TryThread thread(&mutex);
    205     thread.Start();
    206     thread.Join();
    207     EXPECT_FALSE(thread.try_lock_succeeded());
    208   }
    209 
    210   // Release the lock.
    211   mutex.Unlock();
    212 
    213   // It should succeed again.
    214   {
    215     TryThread thread(&mutex);
    216     thread.Start();
    217     thread.Join();
    218     EXPECT_TRUE(thread.try_lock_succeeded());
    219   }
    220 }
    221 
    222 
    223 // Tests of assertions for Debug builds.
    224 #if !defined(NDEBUG)
    225 // Test |AssertHeld()| (which is an actual user API).
    226 TEST(MutexTest, DebugAssertHeldFailure) {
    227   Mutex mutex;
    228   EXPECT_DEATH_IF_SUPPORTED(mutex.AssertHeld(), "");
    229 }
    230 
    231 // Test other consistency checks.
    232 TEST(MutexTest, DebugAssertionFailures) {
    233   // Unlock without lock held.
    234   EXPECT_DEATH_IF_SUPPORTED({
    235     Mutex mutex;
    236     mutex.Unlock();
    237   }, "");
    238 
    239   // Lock with lock held (on same thread).
    240   EXPECT_DEATH_IF_SUPPORTED({
    241     Mutex mutex;
    242     mutex.Lock();
    243     mutex.Lock();
    244   }, "");
    245 
    246   // Try lock with lock held.
    247   EXPECT_DEATH_IF_SUPPORTED({
    248     Mutex mutex;
    249     mutex.Lock();
    250     mutex.TryLock();
    251   }, "");
    252 
    253   // Destroy lock with lock held.
    254   EXPECT_DEATH_IF_SUPPORTED({
    255     Mutex mutex;
    256     mutex.Lock();
    257   }, "");
    258 }
    259 #endif  // !defined(NDEBUG)
    260 
    261 }  // namespace
    262 }  // namespace mojo
    263