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