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