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