1 // Copyright 2012 The Chromium Authors
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 <cstdint>
10
11 #include "base/compiler_specific.h"
12 #include "base/dcheck_is_on.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/synchronization/lock_subtle.h"
15 #include "base/test/gtest_util.h"
16 #include "base/threading/platform_thread.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 using testing::UnorderedElementsAre;
21 using testing::UnorderedElementsAreArray;
22
23 namespace base {
24
25 // Basic test to make sure that Acquire()/Release()/Try() don't crash ----------
26
27 class BasicLockTestThread : public PlatformThread::Delegate {
28 public:
BasicLockTestThread(Lock * lock)29 explicit BasicLockTestThread(Lock* lock) : lock_(lock), acquired_(0) {}
30
31 BasicLockTestThread(const BasicLockTestThread&) = delete;
32 BasicLockTestThread& operator=(const BasicLockTestThread&) = delete;
33
ThreadMain()34 void ThreadMain() override {
35 for (int i = 0; i < 10; i++) {
36 lock_->Acquire();
37 acquired_++;
38 lock_->Release();
39 }
40 for (int i = 0; i < 10; i++) {
41 lock_->Acquire();
42 acquired_++;
43 PlatformThread::Sleep(Milliseconds(rand() % 20));
44 lock_->Release();
45 }
46 for (int i = 0; i < 10; i++) {
47 if (lock_->Try()) {
48 acquired_++;
49 PlatformThread::Sleep(Milliseconds(rand() % 20));
50 lock_->Release();
51 }
52 }
53 }
54
acquired() const55 int acquired() const { return acquired_; }
56
57 private:
58 raw_ptr<Lock> lock_;
59 int acquired_;
60 };
61
TEST(LockTest,Basic)62 TEST(LockTest, Basic) {
63 Lock lock;
64 BasicLockTestThread thread(&lock);
65 PlatformThreadHandle handle;
66
67 ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
68
69 int acquired = 0;
70 for (int i = 0; i < 5; i++) {
71 lock.Acquire();
72 acquired++;
73 lock.Release();
74 }
75 for (int i = 0; i < 10; i++) {
76 lock.Acquire();
77 acquired++;
78 PlatformThread::Sleep(Milliseconds(rand() % 20));
79 lock.Release();
80 }
81 for (int i = 0; i < 10; i++) {
82 if (lock.Try()) {
83 acquired++;
84 PlatformThread::Sleep(Milliseconds(rand() % 20));
85 lock.Release();
86 }
87 }
88 for (int i = 0; i < 5; i++) {
89 lock.Acquire();
90 acquired++;
91 PlatformThread::Sleep(Milliseconds(rand() % 20));
92 lock.Release();
93 }
94
95 PlatformThread::Join(handle);
96
97 EXPECT_GE(acquired, 20);
98 EXPECT_GE(thread.acquired(), 20);
99 }
100
101 // Test that Try() works as expected -------------------------------------------
102
103 class TryLockTestThread : public PlatformThread::Delegate {
104 public:
TryLockTestThread(Lock * lock)105 explicit TryLockTestThread(Lock* lock) : lock_(lock), got_lock_(false) {}
106
107 TryLockTestThread(const TryLockTestThread&) = delete;
108 TryLockTestThread& operator=(const TryLockTestThread&) = delete;
109
ThreadMain()110 void ThreadMain() override {
111 // The local variable is required for the static analyzer to see that the
112 // lock is properly released.
113 bool got_lock = lock_->Try();
114 got_lock_ = got_lock;
115 if (got_lock)
116 lock_->Release();
117 }
118
got_lock() const119 bool got_lock() const { return got_lock_; }
120
121 private:
122 raw_ptr<Lock> lock_;
123 bool got_lock_;
124 };
125
TEST(LockTest,TryLock)126 TEST(LockTest, TryLock) {
127 Lock lock;
128
129 ASSERT_TRUE(lock.Try());
130 lock.AssertAcquired();
131
132 // This thread will not be able to get the lock.
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_FALSE(thread.got_lock());
142 }
143
144 lock.Release();
145
146 // This thread will....
147 {
148 TryLockTestThread thread(&lock);
149 PlatformThreadHandle handle;
150
151 ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
152
153 PlatformThread::Join(handle);
154
155 ASSERT_TRUE(thread.got_lock());
156 // But it released it....
157 ASSERT_TRUE(lock.Try());
158 lock.AssertAcquired();
159 }
160
161 lock.Release();
162 }
163
164 // Tests that locks actually exclude -------------------------------------------
165
166 class MutexLockTestThread : public PlatformThread::Delegate {
167 public:
MutexLockTestThread(Lock * lock,int * value)168 MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {}
169
170 MutexLockTestThread(const MutexLockTestThread&) = delete;
171 MutexLockTestThread& operator=(const MutexLockTestThread&) = delete;
172
173 // Static helper which can also be called from the main thread.
DoStuff(Lock * lock,int * value)174 static void DoStuff(Lock* lock, int* value) {
175 for (int i = 0; i < 40; i++) {
176 lock->Acquire();
177 int v = *value;
178 PlatformThread::Sleep(Milliseconds(rand() % 10));
179 *value = v + 1;
180 lock->Release();
181 }
182 }
183
ThreadMain()184 void ThreadMain() override { DoStuff(lock_, value_); }
185
186 private:
187 raw_ptr<Lock> lock_;
188 raw_ptr<int> value_;
189 };
190
TEST(LockTest,MutexTwoThreads)191 TEST(LockTest, MutexTwoThreads) {
192 Lock lock;
193 int value = 0;
194
195 MutexLockTestThread thread(&lock, &value);
196 PlatformThreadHandle handle;
197
198 ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
199
200 MutexLockTestThread::DoStuff(&lock, &value);
201
202 PlatformThread::Join(handle);
203
204 EXPECT_EQ(2 * 40, value);
205 }
206
TEST(LockTest,MutexFourThreads)207 TEST(LockTest, MutexFourThreads) {
208 Lock lock;
209 int value = 0;
210
211 MutexLockTestThread thread1(&lock, &value);
212 MutexLockTestThread thread2(&lock, &value);
213 MutexLockTestThread thread3(&lock, &value);
214 PlatformThreadHandle handle1;
215 PlatformThreadHandle handle2;
216 PlatformThreadHandle handle3;
217
218 ASSERT_TRUE(PlatformThread::Create(0, &thread1, &handle1));
219 ASSERT_TRUE(PlatformThread::Create(0, &thread2, &handle2));
220 ASSERT_TRUE(PlatformThread::Create(0, &thread3, &handle3));
221
222 MutexLockTestThread::DoStuff(&lock, &value);
223
224 PlatformThread::Join(handle1);
225 PlatformThread::Join(handle2);
226 PlatformThread::Join(handle3);
227
228 EXPECT_EQ(4 * 40, value);
229 }
230
TEST(LockTest,AutoLockMaybe)231 TEST(LockTest, AutoLockMaybe) {
232 Lock lock;
233 {
234 AutoLockMaybe auto_lock(&lock);
235 lock.AssertAcquired();
236 }
237 EXPECT_DCHECK_DEATH(lock.AssertAcquired());
238 }
239
TEST(LockTest,AutoLockMaybeNull)240 TEST(LockTest, AutoLockMaybeNull) {
241 AutoLockMaybe auto_lock(nullptr);
242 }
243
TEST(LockTest,ReleasableAutoLockExplicitRelease)244 TEST(LockTest, ReleasableAutoLockExplicitRelease) {
245 Lock lock;
246 ReleasableAutoLock auto_lock(&lock);
247 lock.AssertAcquired();
248 auto_lock.Release();
249 EXPECT_DCHECK_DEATH(lock.AssertAcquired());
250 }
251
TEST(LockTest,ReleasableAutoLockImplicitRelease)252 TEST(LockTest, ReleasableAutoLockImplicitRelease) {
253 Lock lock;
254 {
255 ReleasableAutoLock auto_lock(&lock);
256 lock.AssertAcquired();
257 }
258 EXPECT_DCHECK_DEATH(lock.AssertAcquired());
259 }
260
261 class TryLockTest : public testing::Test {
262 protected:
263 Lock lock_;
264 int x_ GUARDED_BY(lock_) = 0;
265 };
266
267 // Verifies thread safety annotations do not prevent correct `AutoTryLock` usage
268 // from compiling. A dual of this test exists in lock_nocompile.nc. For more
269 // context, see <https://crbug.com/340196356>.
TEST_F(TryLockTest,CorrectlyCheckIsAcquired)270 TEST_F(TryLockTest, CorrectlyCheckIsAcquired) {
271 AutoTryLock maybe(lock_);
272 // Should compile because we correctly check whether the lock is acquired
273 // before writing to `x_`.
274 if (maybe.is_acquired()) {
275 x_ = 5;
276 }
277 }
278
279 #if DCHECK_IS_ON()
280
TEST(LockTest,GetTrackedLocksHeldByCurrentThread)281 TEST(LockTest, GetTrackedLocksHeldByCurrentThread) {
282 Lock lock_a;
283 Lock lock_b;
284 Lock lock_c;
285 const uintptr_t lock_a_ptr = reinterpret_cast<uintptr_t>(&lock_a);
286 const uintptr_t lock_b_ptr = reinterpret_cast<uintptr_t>(&lock_b);
287 const uintptr_t lock_c_ptr = reinterpret_cast<uintptr_t>(&lock_c);
288
289 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
290 UnorderedElementsAre());
291 ReleasableAutoLock auto_lock_a(&lock_a, subtle::LockTracking::kEnabled);
292 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
293 UnorderedElementsAre(lock_a_ptr));
294 ReleasableAutoLock auto_lock_b(&lock_b, subtle::LockTracking::kEnabled);
295 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
296 UnorderedElementsAre(lock_a_ptr, lock_b_ptr));
297 auto_lock_a.Release();
298 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
299 UnorderedElementsAre(lock_b_ptr));
300 ReleasableAutoLock auto_lock_c(&lock_c, subtle::LockTracking::kEnabled);
301 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
302 UnorderedElementsAre(lock_b_ptr, lock_c_ptr));
303 auto_lock_c.Release();
304 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
305 UnorderedElementsAre(lock_b_ptr));
306 auto_lock_b.Release();
307 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
308 UnorderedElementsAre());
309 }
310
TEST(LockTest,GetTrackedLocksHeldByCurrentThread_AutoLock)311 TEST(LockTest, GetTrackedLocksHeldByCurrentThread_AutoLock) {
312 Lock lock;
313 const uintptr_t lock_ptr = reinterpret_cast<uintptr_t>(&lock);
314 AutoLock auto_lock(lock, subtle::LockTracking::kEnabled);
315 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
316 UnorderedElementsAre(lock_ptr));
317 }
318
TEST(LockTest,GetTrackedLocksHeldByCurrentThread_MovableAutoLock)319 TEST(LockTest, GetTrackedLocksHeldByCurrentThread_MovableAutoLock) {
320 Lock lock;
321 const uintptr_t lock_ptr = reinterpret_cast<uintptr_t>(&lock);
322 MovableAutoLock auto_lock(lock, subtle::LockTracking::kEnabled);
323 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
324 UnorderedElementsAre(lock_ptr));
325 }
326
TEST(LockTest,GetTrackedLocksHeldByCurrentThread_AutoTryLock)327 TEST(LockTest, GetTrackedLocksHeldByCurrentThread_AutoTryLock) {
328 Lock lock;
329 const uintptr_t lock_ptr = reinterpret_cast<uintptr_t>(&lock);
330 AutoTryLock auto_lock(lock, subtle::LockTracking::kEnabled);
331 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
332 UnorderedElementsAre(lock_ptr));
333 }
334
TEST(LockTest,GetTrackedLocksHeldByCurrentThread_AutoLockMaybe)335 TEST(LockTest, GetTrackedLocksHeldByCurrentThread_AutoLockMaybe) {
336 Lock lock;
337 const uintptr_t lock_ptr = reinterpret_cast<uintptr_t>(&lock);
338 AutoLockMaybe auto_lock(&lock, subtle::LockTracking::kEnabled);
339 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
340 UnorderedElementsAre(lock_ptr));
341 }
342
TEST(LockTest,GetTrackedLocksHeldByCurrentThreadOverCapacity)343 TEST(LockTest, GetTrackedLocksHeldByCurrentThreadOverCapacity)
344 // Thread-safety analysis doesn't handle the array of locks properly.
345 NO_THREAD_SAFETY_ANALYSIS {
346 constexpr size_t kHeldLocksCapacity = 10;
347 std::array<Lock, kHeldLocksCapacity + 1> locks;
348
349 for (size_t i = 0; i < kHeldLocksCapacity; ++i) {
350 locks[i].Acquire(subtle::LockTracking::kEnabled);
351 }
352
353 EXPECT_DCHECK_DEATH({
354 locks[kHeldLocksCapacity].Acquire(subtle::LockTracking::kEnabled);
355 locks[kHeldLocksCapacity].Release();
356 });
357
358 for (size_t i = 0; i < kHeldLocksCapacity; ++i) {
359 locks[i].Release();
360
361 std::vector<uintptr_t> expected_locks;
362 for (size_t j = i + 1; j < kHeldLocksCapacity; ++j) {
363 expected_locks.push_back(reinterpret_cast<uintptr_t>(&locks[j]));
364 }
365
366 EXPECT_THAT(subtle::GetTrackedLocksHeldByCurrentThread(),
367 UnorderedElementsAreArray(expected_locks));
368 }
369 }
370
TEST(LockTest,TrackingDisabled)371 TEST(LockTest, TrackingDisabled) {
372 Lock lock;
373 AutoLock auto_lock(lock, subtle::LockTracking::kDisabled);
374 EXPECT_TRUE(subtle::GetTrackedLocksHeldByCurrentThread().empty());
375 }
376
377 #endif // DCHECK_IS_ON()
378
379 } // namespace base
380