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/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/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(mutex.AssertHeld(), ""); | |
229 } | |
230 | |
231 // Test other consistency checks. | |
232 TEST(MutexTest, DebugAssertionFailures) { | |
233 // Unlock without lock held. | |
234 EXPECT_DEATH({ | |
235 Mutex mutex; | |
236 mutex.Unlock(); | |
237 }, ""); | |
238 | |
239 // Lock with lock held (on same thread). | |
240 EXPECT_DEATH({ | |
241 Mutex mutex; | |
242 mutex.Lock(); | |
243 mutex.Lock(); | |
244 }, ""); | |
245 | |
246 // Try lock with lock held. | |
247 EXPECT_DEATH({ | |
248 Mutex mutex; | |
249 mutex.Lock(); | |
250 mutex.TryLock(); | |
251 }, ""); | |
252 | |
253 // Destroy lock with lock held. | |
254 EXPECT_DEATH({ | |
255 Mutex mutex; | |
256 mutex.Lock(); | |
257 }, ""); | |
258 } | |
259 #endif // !defined(NDEBUG) | |
260 | |
261 } // namespace | |
262 } // namespace mojo | |
OLD | NEW |