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/test/gtest_util.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