1 // Copyright 2016 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/task_scheduler/scheduler_lock.h"
6
7 #include <stdlib.h>
8
9 #include "base/compiler_specific.h"
10 #include "base/macros.h"
11 #include "base/rand_util.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/task_scheduler/test_utils.h"
14 #include "base/threading/platform_thread.h"
15 #include "base/threading/simple_thread.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace base {
19 namespace internal {
20 namespace {
21
22 // Adapted from base::Lock's BasicLockTestThread to make sure
23 // Acquire()/Release() don't crash.
24 class BasicLockTestThread : public SimpleThread {
25 public:
BasicLockTestThread(SchedulerLock * lock)26 explicit BasicLockTestThread(SchedulerLock* lock)
27 : SimpleThread("BasicLockTestThread"),
28 lock_(lock),
29 acquired_(0) {}
30
acquired() const31 int acquired() const { return acquired_; }
32
33 private:
Run()34 void Run() override {
35 for (int i = 0; i < 10; i++) {
36 lock_->Acquire();
37 acquired_++;
38 lock_->Release();
39 }
40 for (int i = 0; i < 10; i++) {
41 lock_->Acquire();
42 acquired_++;
43 PlatformThread::Sleep(TimeDelta::FromMilliseconds(base::RandInt(0, 19)));
44 lock_->Release();
45 }
46 }
47
48 SchedulerLock* const lock_;
49 int acquired_;
50
51 DISALLOW_COPY_AND_ASSIGN(BasicLockTestThread);
52 };
53
54 class BasicLockAcquireAndWaitThread : public SimpleThread {
55 public:
BasicLockAcquireAndWaitThread(SchedulerLock * lock)56 explicit BasicLockAcquireAndWaitThread(SchedulerLock* lock)
57 : SimpleThread("BasicLockAcquireAndWaitThread"),
58 lock_(lock),
59 lock_acquire_event_(WaitableEvent::ResetPolicy::AUTOMATIC,
60 WaitableEvent::InitialState::NOT_SIGNALED),
61 main_thread_continue_event_(WaitableEvent::ResetPolicy::AUTOMATIC,
62 WaitableEvent::InitialState::NOT_SIGNALED) {
63 }
64
WaitForLockAcquisition()65 void WaitForLockAcquisition() {
66 lock_acquire_event_.Wait();
67 }
68
ContinueMain()69 void ContinueMain() {
70 main_thread_continue_event_.Signal();
71 }
72
73 private:
Run()74 void Run() override {
75 lock_->Acquire();
76 lock_acquire_event_.Signal();
77 main_thread_continue_event_.Wait();
78 lock_->Release();
79 }
80
81 SchedulerLock* const lock_;
82 WaitableEvent lock_acquire_event_;
83 WaitableEvent main_thread_continue_event_;
84
85 DISALLOW_COPY_AND_ASSIGN(BasicLockAcquireAndWaitThread);
86 };
87
TEST(TaskSchedulerLock,Basic)88 TEST(TaskSchedulerLock, Basic) {
89 SchedulerLock lock;
90 BasicLockTestThread thread(&lock);
91
92 thread.Start();
93
94 int acquired = 0;
95 for (int i = 0; i < 5; i++) {
96 lock.Acquire();
97 acquired++;
98 lock.Release();
99 }
100 for (int i = 0; i < 10; i++) {
101 lock.Acquire();
102 acquired++;
103 PlatformThread::Sleep(TimeDelta::FromMilliseconds(base::RandInt(0, 19)));
104 lock.Release();
105 }
106 for (int i = 0; i < 5; i++) {
107 lock.Acquire();
108 acquired++;
109 PlatformThread::Sleep(TimeDelta::FromMilliseconds(base::RandInt(0, 19)));
110 lock.Release();
111 }
112
113 thread.Join();
114
115 EXPECT_EQ(acquired, 20);
116 EXPECT_EQ(thread.acquired(), 20);
117 }
118
TEST(TaskSchedulerLock,AcquirePredecessor)119 TEST(TaskSchedulerLock, AcquirePredecessor) {
120 SchedulerLock predecessor;
121 SchedulerLock lock(&predecessor);
122 predecessor.Acquire();
123 lock.Acquire();
124 lock.Release();
125 predecessor.Release();
126 }
127
TEST(TaskSchedulerLock,AcquirePredecessorWrongOrder)128 TEST(TaskSchedulerLock, AcquirePredecessorWrongOrder) {
129 SchedulerLock predecessor;
130 SchedulerLock lock(&predecessor);
131 EXPECT_DCHECK_DEATH({
132 lock.Acquire();
133 predecessor.Acquire();
134 }, "");
135 }
136
TEST(TaskSchedulerLock,AcquireNonPredecessor)137 TEST(TaskSchedulerLock, AcquireNonPredecessor) {
138 SchedulerLock lock1;
139 SchedulerLock lock2;
140 EXPECT_DCHECK_DEATH({
141 lock1.Acquire();
142 lock2.Acquire();
143 }, "");
144 }
145
TEST(TaskSchedulerLock,AcquireMultipleLocksInOrder)146 TEST(TaskSchedulerLock, AcquireMultipleLocksInOrder) {
147 SchedulerLock lock1;
148 SchedulerLock lock2(&lock1);
149 SchedulerLock lock3(&lock2);
150 lock1.Acquire();
151 lock2.Acquire();
152 lock3.Acquire();
153 lock3.Release();
154 lock2.Release();
155 lock1.Release();
156 }
157
TEST(TaskSchedulerLock,AcquireMultipleLocksInTheMiddleOfAChain)158 TEST(TaskSchedulerLock, AcquireMultipleLocksInTheMiddleOfAChain) {
159 SchedulerLock lock1;
160 SchedulerLock lock2(&lock1);
161 SchedulerLock lock3(&lock2);
162 lock2.Acquire();
163 lock3.Acquire();
164 lock3.Release();
165 lock2.Release();
166 }
167
TEST(TaskSchedulerLock,AcquireMultipleLocksNoTransitivity)168 TEST(TaskSchedulerLock, AcquireMultipleLocksNoTransitivity) {
169 SchedulerLock lock1;
170 SchedulerLock lock2(&lock1);
171 SchedulerLock lock3(&lock2);
172 EXPECT_DCHECK_DEATH({
173 lock1.Acquire();
174 lock3.Acquire();
175 }, "");
176 }
177
TEST(TaskSchedulerLock,AcquireLocksDifferentThreadsSafely)178 TEST(TaskSchedulerLock, AcquireLocksDifferentThreadsSafely) {
179 SchedulerLock lock1;
180 SchedulerLock lock2;
181 BasicLockAcquireAndWaitThread thread(&lock1);
182 thread.Start();
183
184 lock2.Acquire();
185 thread.WaitForLockAcquisition();
186 thread.ContinueMain();
187 thread.Join();
188 lock2.Release();
189 }
190
TEST(TaskSchedulerLock,AcquireLocksWithPredecessorDifferentThreadsSafelyPredecessorFirst)191 TEST(TaskSchedulerLock,
192 AcquireLocksWithPredecessorDifferentThreadsSafelyPredecessorFirst) {
193 // A lock and its predecessor may be safely acquired on different threads.
194 // This Thread Other Thread
195 // predecessor.Acquire()
196 // lock.Acquire()
197 // predecessor.Release()
198 // lock.Release()
199 SchedulerLock predecessor;
200 SchedulerLock lock(&predecessor);
201 predecessor.Acquire();
202 BasicLockAcquireAndWaitThread thread(&lock);
203 thread.Start();
204 thread.WaitForLockAcquisition();
205 predecessor.Release();
206 thread.ContinueMain();
207 thread.Join();
208 }
209
TEST(TaskSchedulerLock,AcquireLocksWithPredecessorDifferentThreadsSafelyPredecessorLast)210 TEST(TaskSchedulerLock,
211 AcquireLocksWithPredecessorDifferentThreadsSafelyPredecessorLast) {
212 // A lock and its predecessor may be safely acquired on different threads.
213 // This Thread Other Thread
214 // lock.Acquire()
215 // predecessor.Acquire()
216 // lock.Release()
217 // predecessor.Release()
218 SchedulerLock predecessor;
219 SchedulerLock lock(&predecessor);
220 lock.Acquire();
221 BasicLockAcquireAndWaitThread thread(&predecessor);
222 thread.Start();
223 thread.WaitForLockAcquisition();
224 lock.Release();
225 thread.ContinueMain();
226 thread.Join();
227 }
228
TEST(TaskSchedulerLock,AcquireLocksWithPredecessorDifferentThreadsSafelyNoInterference)229 TEST(TaskSchedulerLock,
230 AcquireLocksWithPredecessorDifferentThreadsSafelyNoInterference) {
231 // Acquisition of an unrelated lock on another thread should not affect a
232 // legal lock acquisition with a predecessor on this thread.
233 // This Thread Other Thread
234 // predecessor.Acquire()
235 // unrelated.Acquire()
236 // lock.Acquire()
237 // unrelated.Release()
238 // lock.Release()
239 // predecessor.Release();
240 SchedulerLock predecessor;
241 SchedulerLock lock(&predecessor);
242 predecessor.Acquire();
243 SchedulerLock unrelated;
244 BasicLockAcquireAndWaitThread thread(&unrelated);
245 thread.Start();
246 thread.WaitForLockAcquisition();
247 lock.Acquire();
248 thread.ContinueMain();
249 thread.Join();
250 lock.Release();
251 predecessor.Release();
252 }
253
TEST(TaskSchedulerLock,SelfReferentialLock)254 TEST(TaskSchedulerLock, SelfReferentialLock) {
255 struct SelfReferentialLock {
256 SelfReferentialLock() : lock(&lock) {}
257
258 SchedulerLock lock;
259 };
260
261 EXPECT_DCHECK_DEATH({ SelfReferentialLock lock; }, "");
262 }
263
TEST(TaskSchedulerLock,PredecessorCycle)264 TEST(TaskSchedulerLock, PredecessorCycle) {
265 struct LockCycle {
266 LockCycle() : lock1(&lock2), lock2(&lock1) {}
267
268 SchedulerLock lock1;
269 SchedulerLock lock2;
270 };
271
272 EXPECT_DCHECK_DEATH({ LockCycle cycle; }, "");
273 }
274
TEST(TaskSchedulerLock,PredecessorLongerCycle)275 TEST(TaskSchedulerLock, PredecessorLongerCycle) {
276 struct LockCycle {
277 LockCycle()
278 : lock1(&lock5),
279 lock2(&lock1),
280 lock3(&lock2),
281 lock4(&lock3),
282 lock5(&lock4) {}
283
284 SchedulerLock lock1;
285 SchedulerLock lock2;
286 SchedulerLock lock3;
287 SchedulerLock lock4;
288 SchedulerLock lock5;
289 };
290
291 EXPECT_DCHECK_DEATH({ LockCycle cycle; }, "");
292 }
293
294 } // namespace
295 } // namespace internal
296 } // namespace base
297