1 // Copyright (c) 2012 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 "base/synchronization/lock.h"
6
7 #include <stdlib.h>
8
9 #include "base/compiler_specific.h"
10 #include "base/macros.h"
11 #include "base/threading/platform_thread.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13
14 namespace base {
15
16 // Basic test to make sure that Acquire()/Release()/Try() don't crash ----------
17
18 class BasicLockTestThread : public PlatformThread::Delegate {
19 public:
BasicLockTestThread(Lock * lock)20 explicit BasicLockTestThread(Lock* lock) : lock_(lock), acquired_(0) {}
21
ThreadMain()22 void ThreadMain() override {
23 for (int i = 0; i < 10; i++) {
24 lock_->Acquire();
25 acquired_++;
26 lock_->Release();
27 }
28 for (int i = 0; i < 10; i++) {
29 lock_->Acquire();
30 acquired_++;
31 PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20));
32 lock_->Release();
33 }
34 for (int i = 0; i < 10; i++) {
35 if (lock_->Try()) {
36 acquired_++;
37 PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20));
38 lock_->Release();
39 }
40 }
41 }
42
acquired() const43 int acquired() const { return acquired_; }
44
45 private:
46 Lock* lock_;
47 int acquired_;
48
49 DISALLOW_COPY_AND_ASSIGN(BasicLockTestThread);
50 };
51
TEST(LockTest,Basic)52 TEST(LockTest, Basic) {
53 Lock lock;
54 BasicLockTestThread thread(&lock);
55 PlatformThreadHandle handle;
56
57 ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
58
59 int acquired = 0;
60 for (int i = 0; i < 5; i++) {
61 lock.Acquire();
62 acquired++;
63 lock.Release();
64 }
65 for (int i = 0; i < 10; i++) {
66 lock.Acquire();
67 acquired++;
68 PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20));
69 lock.Release();
70 }
71 for (int i = 0; i < 10; i++) {
72 if (lock.Try()) {
73 acquired++;
74 PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20));
75 lock.Release();
76 }
77 }
78 for (int i = 0; i < 5; i++) {
79 lock.Acquire();
80 acquired++;
81 PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20));
82 lock.Release();
83 }
84
85 PlatformThread::Join(handle);
86
87 EXPECT_GE(acquired, 20);
88 EXPECT_GE(thread.acquired(), 20);
89 }
90
91 // Test that Try() works as expected -------------------------------------------
92
93 class TryLockTestThread : public PlatformThread::Delegate {
94 public:
TryLockTestThread(Lock * lock)95 explicit TryLockTestThread(Lock* lock) : lock_(lock), got_lock_(false) {}
96
ThreadMain()97 void ThreadMain() override {
98 got_lock_ = lock_->Try();
99 if (got_lock_)
100 lock_->Release();
101 }
102
got_lock() const103 bool got_lock() const { return got_lock_; }
104
105 private:
106 Lock* lock_;
107 bool got_lock_;
108
109 DISALLOW_COPY_AND_ASSIGN(TryLockTestThread);
110 };
111
TEST(LockTest,TryLock)112 TEST(LockTest, TryLock) {
113 Lock lock;
114
115 ASSERT_TRUE(lock.Try());
116 // We now have the lock....
117
118 // This thread will not be able to get the lock.
119 {
120 TryLockTestThread thread(&lock);
121 PlatformThreadHandle handle;
122
123 ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
124
125 PlatformThread::Join(handle);
126
127 ASSERT_FALSE(thread.got_lock());
128 }
129
130 lock.Release();
131
132 // This thread will....
133 {
134 TryLockTestThread thread(&lock);
135 PlatformThreadHandle handle;
136
137 ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
138
139 PlatformThread::Join(handle);
140
141 ASSERT_TRUE(thread.got_lock());
142 // But it released it....
143 ASSERT_TRUE(lock.Try());
144 }
145
146 lock.Release();
147 }
148
149 // Tests that locks actually exclude -------------------------------------------
150
151 class MutexLockTestThread : public PlatformThread::Delegate {
152 public:
MutexLockTestThread(Lock * lock,int * value)153 MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {}
154
155 // Static helper which can also be called from the main thread.
DoStuff(Lock * lock,int * value)156 static void DoStuff(Lock* lock, int* value) {
157 for (int i = 0; i < 40; i++) {
158 lock->Acquire();
159 int v = *value;
160 PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 10));
161 *value = v + 1;
162 lock->Release();
163 }
164 }
165
ThreadMain()166 void ThreadMain() override { DoStuff(lock_, value_); }
167
168 private:
169 Lock* lock_;
170 int* value_;
171
172 DISALLOW_COPY_AND_ASSIGN(MutexLockTestThread);
173 };
174
TEST(LockTest,MutexTwoThreads)175 TEST(LockTest, MutexTwoThreads) {
176 Lock lock;
177 int value = 0;
178
179 MutexLockTestThread thread(&lock, &value);
180 PlatformThreadHandle handle;
181
182 ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
183
184 MutexLockTestThread::DoStuff(&lock, &value);
185
186 PlatformThread::Join(handle);
187
188 EXPECT_EQ(2 * 40, value);
189 }
190
TEST(LockTest,MutexFourThreads)191 TEST(LockTest, MutexFourThreads) {
192 Lock lock;
193 int value = 0;
194
195 MutexLockTestThread thread1(&lock, &value);
196 MutexLockTestThread thread2(&lock, &value);
197 MutexLockTestThread thread3(&lock, &value);
198 PlatformThreadHandle handle1;
199 PlatformThreadHandle handle2;
200 PlatformThreadHandle handle3;
201
202 ASSERT_TRUE(PlatformThread::Create(0, &thread1, &handle1));
203 ASSERT_TRUE(PlatformThread::Create(0, &thread2, &handle2));
204 ASSERT_TRUE(PlatformThread::Create(0, &thread3, &handle3));
205
206 MutexLockTestThread::DoStuff(&lock, &value);
207
208 PlatformThread::Join(handle1);
209 PlatformThread::Join(handle2);
210 PlatformThread::Join(handle3);
211
212 EXPECT_EQ(4 * 40, value);
213 }
214
215 } // namespace base
216