• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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