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