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
14namespace base {
15
16// Basic test to make sure that Acquire()/Release()/Try() don't crash ----------
17
18class BasicLockTestThread : public PlatformThread::Delegate {
19 public:
20  explicit BasicLockTestThread(Lock* lock) : lock_(lock), acquired_(0) {}
21
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
43  int acquired() const { return acquired_; }
44
45 private:
46  Lock* lock_;
47  int acquired_;
48
49  DISALLOW_COPY_AND_ASSIGN(BasicLockTestThread);
50};
51
52TEST(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
93class TryLockTestThread : public PlatformThread::Delegate {
94 public:
95  explicit TryLockTestThread(Lock* lock) : lock_(lock), got_lock_(false) {}
96
97  void ThreadMain() override {
98    got_lock_ = lock_->Try();
99    if (got_lock_)
100      lock_->Release();
101  }
102
103  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
112TEST(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
151class MutexLockTestThread : public PlatformThread::Delegate {
152 public:
153  MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {}
154
155  // Static helper which can also be called from the main thread.
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
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
175TEST(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
191TEST(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