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