| 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 |