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