1 /*
2  * Copyright 2011 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <stdlib.h>
18 
19 #include "gtest/gtest.h"
20 #include "sfntly/port/lock.h"
21 #include "test/platform_thread.h"
22 
23 namespace sfntly {
24 
25 // Basic test to make sure that Acquire()/Unlock()/Try() don't crash
26 
27 class BasicLockTestThread : public PlatformThread::Delegate {
28  public:
BasicLockTestThread(Lock * lock)29   BasicLockTestThread(Lock* lock) : lock_(lock), acquired_(0) {}
30 
ThreadMain()31   virtual void ThreadMain() {
32     for (int i = 0; i < 10; i++) {
33       lock_->Acquire();
34       acquired_++;
35       lock_->Unlock();
36     }
37     for (int i = 0; i < 10; i++) {
38       lock_->Acquire();
39       acquired_++;
40       PlatformThread::Sleep(rand() % 20);
41       lock_->Unlock();
42     }
43     for (int i = 0; i < 10; i++) {
44       if (lock_->Try()) {
45         acquired_++;
46         PlatformThread::Sleep(rand() % 20);
47         lock_->Unlock();
48       }
49     }
50   }
51 
acquired() const52   int acquired() const { return acquired_; }
53 
54  private:
55   Lock* lock_;
56   int acquired_;
57 
58   NO_COPY_AND_ASSIGN(BasicLockTestThread);
59 };
60 
BasicLockTest()61 bool BasicLockTest() {
62   Lock lock;
63   BasicLockTestThread thread(&lock);
64   PlatformThreadHandle handle = kNullThreadHandle;
65 
66   EXPECT_TRUE(PlatformThread::Create(&thread, &handle));
67 
68   int acquired = 0;
69   for (int i = 0; i < 5; i++) {
70     lock.Acquire();
71     acquired++;
72     lock.Unlock();
73   }
74   for (int i = 0; i < 10; i++) {
75     lock.Acquire();
76     acquired++;
77     PlatformThread::Sleep(rand() % 20);
78     lock.Unlock();
79   }
80   for (int i = 0; i < 10; i++) {
81     if (lock.Try()) {
82       acquired++;
83       PlatformThread::Sleep(rand() % 20);
84       lock.Unlock();
85     }
86   }
87   for (int i = 0; i < 5; i++) {
88     lock.Acquire();
89     acquired++;
90     PlatformThread::Sleep(rand() % 20);
91     lock.Unlock();
92   }
93 
94   PlatformThread::Join(handle);
95 
96   EXPECT_GE(acquired, 20);
97   EXPECT_GE(thread.acquired(), 20);
98 
99   return true;
100 }
101 
102 // Test that Try() works as expected -------------------------------------------
103 
104 class TryLockTestThread : public PlatformThread::Delegate {
105  public:
TryLockTestThread(Lock * lock)106   TryLockTestThread(Lock* lock) : lock_(lock), got_lock_(false) {}
107 
ThreadMain()108   virtual void ThreadMain() {
109     got_lock_ = lock_->Try();
110     if (got_lock_)
111       lock_->Unlock();
112   }
113 
got_lock() const114   bool got_lock() const { return got_lock_; }
115 
116  private:
117   Lock* lock_;
118   bool got_lock_;
119 
120   NO_COPY_AND_ASSIGN(TryLockTestThread);
121 };
122 
TryLockTest()123 bool TryLockTest() {
124   Lock lock;
125 
126   EXPECT_TRUE(lock.Try());
127   // We now have the lock....
128 
129   // This thread will not be able to get the lock.
130   {
131     TryLockTestThread thread(&lock);
132     PlatformThreadHandle handle = kNullThreadHandle;
133 
134     EXPECT_TRUE(PlatformThread::Create(&thread, &handle));
135 
136     PlatformThread::Join(handle);
137 
138     EXPECT_FALSE(thread.got_lock());
139   }
140 
141   lock.Unlock();
142 
143   // This thread will....
144   {
145     TryLockTestThread thread(&lock);
146     PlatformThreadHandle handle = kNullThreadHandle;
147 
148     EXPECT_TRUE(PlatformThread::Create(&thread, &handle));
149 
150     PlatformThread::Join(handle);
151 
152     EXPECT_TRUE(thread.got_lock());
153     // But it released it....
154     EXPECT_TRUE(lock.Try());
155   }
156 
157   lock.Unlock();
158   return true;
159 }
160 
161 // Tests that locks actually exclude -------------------------------------------
162 
163 class MutexLockTestThread : public PlatformThread::Delegate {
164  public:
MutexLockTestThread(Lock * lock,int * value)165   MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {}
166 
167   // Static helper which can also be called from the main thread.
DoStuff(Lock * lock,int * value)168   static void DoStuff(Lock* lock, int* value) {
169     for (int i = 0; i < 40; i++) {
170       lock->Acquire();
171       int v = *value;
172       PlatformThread::Sleep(rand() % 10);
173       *value = v + 1;
174       lock->Unlock();
175     }
176   }
177 
ThreadMain()178   virtual void ThreadMain() {
179     DoStuff(lock_, value_);
180   }
181 
182  private:
183   Lock* lock_;
184   int* value_;
185 
186   NO_COPY_AND_ASSIGN(MutexLockTestThread);
187 };
188 
MutexTwoThreads()189 bool MutexTwoThreads() {
190   Lock lock;
191   int value = 0;
192 
193   MutexLockTestThread thread(&lock, &value);
194   PlatformThreadHandle handle = kNullThreadHandle;
195 
196   EXPECT_TRUE(PlatformThread::Create(&thread, &handle));
197 
198   MutexLockTestThread::DoStuff(&lock, &value);
199 
200   PlatformThread::Join(handle);
201 
202   EXPECT_EQ(2 * 40, value);
203   return true;
204 }
205 
MutexFourThreads()206 bool MutexFourThreads() {
207   Lock lock;
208   int value = 0;
209 
210   MutexLockTestThread thread1(&lock, &value);
211   MutexLockTestThread thread2(&lock, &value);
212   MutexLockTestThread thread3(&lock, &value);
213   PlatformThreadHandle handle1 = kNullThreadHandle;
214   PlatformThreadHandle handle2 = kNullThreadHandle;
215   PlatformThreadHandle handle3 = kNullThreadHandle;
216 
217   EXPECT_TRUE(PlatformThread::Create(&thread1, &handle1));
218   EXPECT_TRUE(PlatformThread::Create(&thread2, &handle2));
219   EXPECT_TRUE(PlatformThread::Create(&thread3, &handle3));
220 
221   MutexLockTestThread::DoStuff(&lock, &value);
222 
223   PlatformThread::Join(handle1);
224   PlatformThread::Join(handle2);
225   PlatformThread::Join(handle3);
226 
227   EXPECT_EQ(4 * 40, value);
228   return true;
229 }
230 
231 }  // namespace sfntly
232 
TEST(LockTest,Basic)233 TEST(LockTest, Basic) {
234   ASSERT_TRUE(sfntly::BasicLockTest());
235 }
236 
TEST(LockTest,TryLock)237 TEST(LockTest, TryLock) {
238   ASSERT_TRUE(sfntly::TryLockTest());
239 }
240 
TEST(LockTest,Mutex)241 TEST(LockTest, Mutex) {
242   ASSERT_TRUE(sfntly::MutexTwoThreads());
243   ASSERT_TRUE(sfntly::MutexFourThreads());
244 }
245