Standardize usage of virtual/override/final in mojo/
[chromium-blink-merge.git] / mojo / public / cpp / utility / tests / mutex_unittest.cc
blobd6f75da574a0dbfdb25c62cc8d6e9ad86581d8c5
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.
5 #include "mojo/public/cpp/utility/mutex.h"
7 #include <stdlib.h> // For |rand()|.
8 #include <time.h> // For |nanosleep()| (defined by POSIX).
10 #include <vector>
12 #include "mojo/public/cpp/system/macros.h"
13 #include "mojo/public/cpp/utility/thread.h"
14 #include "testing/gtest/include/gtest/gtest.h"
16 namespace mojo {
17 namespace {
19 TEST(MutexTest, TrivialSingleThreaded) {
20 Mutex mutex;
22 mutex.Lock();
23 mutex.AssertHeld();
24 mutex.Unlock();
26 EXPECT_TRUE(mutex.TryLock());
27 mutex.AssertHeld();
28 mutex.Unlock();
31 MutexLock lock(&mutex);
32 mutex.AssertHeld();
35 EXPECT_TRUE(mutex.TryLock());
36 mutex.Unlock();
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) {
54 ~Fiddler() {
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;
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.
81 break;
86 private:
87 static void SleepALittle() {
88 static const long kNanosPerMilli = 1000000;
89 struct timespec req = {
90 0, // Seconds.
91 (rand() % 10) * kNanosPerMilli // Nanoseconds.
93 int rv = nanosleep(&req, NULL);
94 MOJO_ALLOW_UNUSED_LOCAL(rv);
95 assert(rv == 0);
98 const size_t times_to_lock_;
99 const Type type_;
100 const bool should_sleep_;
101 Mutex* const mutex_;
102 int* const shared_value_;
104 MOJO_DISALLOW_COPY_AND_ASSIGN(Fiddler);
107 class FiddlerThread : public Thread {
108 public:
109 // Takes ownership of |fiddler|.
110 FiddlerThread(Fiddler* fiddler)
111 : fiddler_(fiddler) {
114 ~FiddlerThread() override { delete fiddler_; }
116 void Run() override { fiddler_->Fiddle(); }
118 private:
119 Fiddler* const fiddler_;
121 MOJO_DISALLOW_COPY_AND_ASSIGN(FiddlerThread);
124 // This does a stress test (that also checks exclusion).
125 TEST(MutexTest, ThreadedStress) {
126 static const size_t kNumThreads = 20;
127 static const int kTimesToLockEach = 20;
128 assert(kNumThreads % 4 == 0);
130 Mutex mutex;
131 int shared_value = 0;
133 std::vector<FiddlerThread*> fiddler_threads;
135 for (size_t i = 0; i < kNumThreads; i += 4) {
136 fiddler_threads.push_back(new FiddlerThread(new Fiddler(
137 kTimesToLockEach, Fiddler::kTypeLock, false, &mutex, &shared_value)));
138 fiddler_threads.push_back(new FiddlerThread(new Fiddler(
139 kTimesToLockEach, Fiddler::kTypeTry, false, &mutex, &shared_value)));
140 fiddler_threads.push_back(new FiddlerThread(new Fiddler(
141 kTimesToLockEach, Fiddler::kTypeLock, true, &mutex, &shared_value)));
142 fiddler_threads.push_back(new FiddlerThread(new Fiddler(
143 kTimesToLockEach, Fiddler::kTypeTry, true, &mutex, &shared_value)));
146 for (size_t i = 0; i < kNumThreads; i++)
147 fiddler_threads[i]->Start();
149 // Do some fiddling ourselves.
150 Fiddler(kTimesToLockEach, Fiddler::kTypeLock, true, &mutex, &shared_value)
151 .Fiddle();
153 // Join.
154 for (size_t i = 0; i < kNumThreads; i++)
155 fiddler_threads[i]->Join();
157 EXPECT_EQ(static_cast<int>(kNumThreads + 1) * kTimesToLockEach, shared_value);
159 // Delete.
160 for (size_t i = 0; i < kNumThreads; i++)
161 delete fiddler_threads[i];
162 fiddler_threads.clear();
165 class TryThread : public Thread {
166 public:
167 explicit TryThread(Mutex* mutex) : mutex_(mutex), try_lock_succeeded_() {}
168 ~TryThread() override {}
170 void Run() override {
171 try_lock_succeeded_ = mutex_->TryLock();
172 if (try_lock_succeeded_)
173 mutex_->Unlock();
176 bool try_lock_succeeded() const { return try_lock_succeeded_; }
178 private:
179 Mutex* const mutex_;
180 bool try_lock_succeeded_;
182 MOJO_DISALLOW_COPY_AND_ASSIGN(TryThread);
185 TEST(MutexTest, TryLock) {
186 Mutex mutex;
188 // |TryLock()| should succeed -- we don't have the lock.
190 TryThread thread(&mutex);
191 thread.Start();
192 thread.Join();
193 EXPECT_TRUE(thread.try_lock_succeeded());
196 // Take the lock.
197 ASSERT_TRUE(mutex.TryLock());
199 // Now it should fail.
201 TryThread thread(&mutex);
202 thread.Start();
203 thread.Join();
204 EXPECT_FALSE(thread.try_lock_succeeded());
207 // Release the lock.
208 mutex.Unlock();
210 // It should succeed again.
212 TryThread thread(&mutex);
213 thread.Start();
214 thread.Join();
215 EXPECT_TRUE(thread.try_lock_succeeded());
220 // Tests of assertions for Debug builds.
221 #if !defined(NDEBUG)
222 // Test |AssertHeld()| (which is an actual user API).
223 TEST(MutexTest, DebugAssertHeldFailure) {
224 Mutex mutex;
225 EXPECT_DEATH_IF_SUPPORTED(mutex.AssertHeld(), "");
228 // Test other consistency checks.
229 TEST(MutexTest, DebugAssertionFailures) {
230 // Unlock without lock held.
231 EXPECT_DEATH_IF_SUPPORTED({
232 Mutex mutex;
233 mutex.Unlock();
234 }, "");
236 // Lock with lock held (on same thread).
237 EXPECT_DEATH_IF_SUPPORTED({
238 Mutex mutex;
239 mutex.Lock();
240 mutex.Lock();
241 }, "");
243 // Try lock with lock held.
244 EXPECT_DEATH_IF_SUPPORTED({
245 Mutex mutex;
246 mutex.Lock();
247 mutex.TryLock();
248 }, "");
250 // Destroy lock with lock held.
251 EXPECT_DEATH_IF_SUPPORTED({
252 Mutex mutex;
253 mutex.Lock();
254 }, "");
256 #endif // !defined(NDEBUG)
258 } // namespace
259 } // namespace mojo