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
23namespace sfntly {
24
25// Basic test to make sure that Acquire()/Unlock()/Try() don't crash
26
27class BasicLockTestThread : public PlatformThread::Delegate {
28 public:
29  BasicLockTestThread(Lock* lock) : lock_(lock), acquired_(0) {}
30
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
52  int acquired() const { return acquired_; }
53
54 private:
55  Lock* lock_;
56  int acquired_;
57
58  NO_COPY_AND_ASSIGN(BasicLockTestThread);
59};
60
61bool 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
104class TryLockTestThread : public PlatformThread::Delegate {
105 public:
106  TryLockTestThread(Lock* lock) : lock_(lock), got_lock_(false) {}
107
108  virtual void ThreadMain() {
109    got_lock_ = lock_->Try();
110    if (got_lock_)
111      lock_->Unlock();
112  }
113
114  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
123bool 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
163class MutexLockTestThread : public PlatformThread::Delegate {
164 public:
165  MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {}
166
167  // Static helper which can also be called from the main thread.
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
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
189bool 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
206bool 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
233TEST(LockTest, Basic) {
234  ASSERT_TRUE(sfntly::BasicLockTest());
235}
236
237TEST(LockTest, TryLock) {
238  ASSERT_TRUE(sfntly::TryLockTest());
239}
240
241TEST(LockTest, Mutex) {
242  ASSERT_TRUE(sfntly::MutexTwoThreads());
243  ASSERT_TRUE(sfntly::MutexFourThreads());
244}
245