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).
12 #include "mojo/public/cpp/system/macros.h"
13 #include "mojo/public/cpp/utility/thread.h"
14 #include "testing/gtest/include/gtest/gtest.h"
19 TEST(MutexTest
, TrivialSingleThreaded
) {
26 EXPECT_TRUE(mutex
.TryLock());
31 MutexLock
lock(&mutex
);
35 EXPECT_TRUE(mutex
.TryLock());
41 enum Type
{ kTypeLock
, kTypeTry
};
42 Fiddler(size_t times_to_lock
,
47 : times_to_lock_(times_to_lock
),
49 should_sleep_(should_sleep
),
51 shared_value_(shared_value
) {
58 for (size_t i
= 0; i
< times_to_lock_
;) {
62 int old_shared_value
= *shared_value_
;
65 *shared_value_
= old_shared_value
+ 1;
71 if (mutex_
->TryLock()) {
72 int old_shared_value
= *shared_value_
;
75 *shared_value_
= old_shared_value
+ 1;
79 SleepALittle(); // Don't spin.
87 static void SleepALittle() {
88 static const long kNanosPerMilli
= 1000000;
89 struct timespec req
= {
91 (rand() % 10) * kNanosPerMilli
// Nanoseconds.
93 int rv MOJO_ALLOW_UNUSED
= nanosleep(&req
, NULL
);
97 const size_t times_to_lock_
;
99 const bool should_sleep_
;
101 int* const shared_value_
;
103 MOJO_DISALLOW_COPY_AND_ASSIGN(Fiddler
);
106 class FiddlerThread
: public Thread
{
108 // Takes ownership of |fiddler|.
109 FiddlerThread(Fiddler
* fiddler
)
110 : fiddler_(fiddler
) {
113 virtual ~FiddlerThread() {
117 virtual void Run() MOJO_OVERRIDE
{
122 Fiddler
* const fiddler_
;
124 MOJO_DISALLOW_COPY_AND_ASSIGN(FiddlerThread
);
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);
134 int shared_value
= 0;
136 std::vector
<FiddlerThread
*> fiddler_threads
;
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
)));
149 for (size_t i
= 0; i
< kNumThreads
; i
++)
150 fiddler_threads
[i
]->Start();
152 // Do some fiddling ourselves.
153 Fiddler(kTimesToLockEach
, Fiddler::kTypeLock
, true, &mutex
, &shared_value
)
157 for (size_t i
= 0; i
< kNumThreads
; i
++)
158 fiddler_threads
[i
]->Join();
160 EXPECT_EQ(static_cast<int>(kNumThreads
+ 1) * kTimesToLockEach
, shared_value
);
163 for (size_t i
= 0; i
< kNumThreads
; i
++)
164 delete fiddler_threads
[i
];
165 fiddler_threads
.clear();
168 class TryThread
: public Thread
{
170 explicit TryThread(Mutex
* mutex
) : mutex_(mutex
), try_lock_succeeded_() {}
171 virtual ~TryThread() {}
173 virtual void Run() MOJO_OVERRIDE
{
174 try_lock_succeeded_
= mutex_
->TryLock();
175 if (try_lock_succeeded_
)
179 bool try_lock_succeeded() const { return try_lock_succeeded_
; }
183 bool try_lock_succeeded_
;
185 MOJO_DISALLOW_COPY_AND_ASSIGN(TryThread
);
188 TEST(MutexTest
, TryLock
) {
191 // |TryLock()| should succeed -- we don't have the lock.
193 TryThread
thread(&mutex
);
196 EXPECT_TRUE(thread
.try_lock_succeeded());
200 ASSERT_TRUE(mutex
.TryLock());
202 // Now it should fail.
204 TryThread
thread(&mutex
);
207 EXPECT_FALSE(thread
.try_lock_succeeded());
213 // It should succeed again.
215 TryThread
thread(&mutex
);
218 EXPECT_TRUE(thread
.try_lock_succeeded());
223 // Tests of assertions for Debug builds.
225 // Test |AssertHeld()| (which is an actual user API).
226 TEST(MutexTest
, DebugAssertHeldFailure
) {
228 EXPECT_DEATH_IF_SUPPORTED(mutex
.AssertHeld(), "");
231 // Test other consistency checks.
232 TEST(MutexTest
, DebugAssertionFailures
) {
233 // Unlock without lock held.
234 EXPECT_DEATH_IF_SUPPORTED({
239 // Lock with lock held (on same thread).
240 EXPECT_DEATH_IF_SUPPORTED({
246 // Try lock with lock held.
247 EXPECT_DEATH_IF_SUPPORTED({
253 // Destroy lock with lock held.
254 EXPECT_DEATH_IF_SUPPORTED({
259 #endif // !defined(NDEBUG)