|  | // Copyright 2016 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/task/task_scheduler/scheduler_lock.h" | 
|  |  | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/rand_util.h" | 
|  | #include "base/synchronization/waitable_event.h" | 
|  | #include "base/test/gtest_util.h" | 
|  | #include "base/threading/platform_thread.h" | 
|  | #include "base/threading/simple_thread.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace internal { | 
|  | namespace { | 
|  |  | 
|  | // Adapted from base::Lock's BasicLockTestThread to make sure | 
|  | // Acquire()/Release() don't crash. | 
|  | class BasicLockTestThread : public SimpleThread { | 
|  | public: | 
|  | explicit BasicLockTestThread(SchedulerLock* lock) | 
|  | : SimpleThread("BasicLockTestThread"), lock_(lock), acquired_(0) {} | 
|  |  | 
|  | int acquired() const { return acquired_; } | 
|  |  | 
|  | private: | 
|  | void Run() override { | 
|  | for (int i = 0; i < 10; i++) { | 
|  | lock_->Acquire(); | 
|  | acquired_++; | 
|  | lock_->Release(); | 
|  | } | 
|  | for (int i = 0; i < 10; i++) { | 
|  | lock_->Acquire(); | 
|  | acquired_++; | 
|  | PlatformThread::Sleep(TimeDelta::FromMilliseconds(base::RandInt(0, 19))); | 
|  | lock_->Release(); | 
|  | } | 
|  | } | 
|  |  | 
|  | SchedulerLock* const lock_; | 
|  | int acquired_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(BasicLockTestThread); | 
|  | }; | 
|  |  | 
|  | class BasicLockAcquireAndWaitThread : public SimpleThread { | 
|  | public: | 
|  | explicit BasicLockAcquireAndWaitThread(SchedulerLock* lock) | 
|  | : SimpleThread("BasicLockAcquireAndWaitThread"), | 
|  | lock_(lock), | 
|  | lock_acquire_event_(WaitableEvent::ResetPolicy::AUTOMATIC, | 
|  | WaitableEvent::InitialState::NOT_SIGNALED), | 
|  | main_thread_continue_event_(WaitableEvent::ResetPolicy::AUTOMATIC, | 
|  | WaitableEvent::InitialState::NOT_SIGNALED) { | 
|  | } | 
|  |  | 
|  | void WaitForLockAcquisition() { lock_acquire_event_.Wait(); } | 
|  |  | 
|  | void ContinueMain() { main_thread_continue_event_.Signal(); } | 
|  |  | 
|  | private: | 
|  | void Run() override { | 
|  | lock_->Acquire(); | 
|  | lock_acquire_event_.Signal(); | 
|  | main_thread_continue_event_.Wait(); | 
|  | lock_->Release(); | 
|  | } | 
|  |  | 
|  | SchedulerLock* const lock_; | 
|  | WaitableEvent lock_acquire_event_; | 
|  | WaitableEvent main_thread_continue_event_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(BasicLockAcquireAndWaitThread); | 
|  | }; | 
|  |  | 
|  | TEST(TaskSchedulerLock, Basic) { | 
|  | SchedulerLock lock; | 
|  | BasicLockTestThread thread(&lock); | 
|  |  | 
|  | thread.Start(); | 
|  |  | 
|  | int acquired = 0; | 
|  | for (int i = 0; i < 5; i++) { | 
|  | lock.Acquire(); | 
|  | acquired++; | 
|  | lock.Release(); | 
|  | } | 
|  | for (int i = 0; i < 10; i++) { | 
|  | lock.Acquire(); | 
|  | acquired++; | 
|  | PlatformThread::Sleep(TimeDelta::FromMilliseconds(base::RandInt(0, 19))); | 
|  | lock.Release(); | 
|  | } | 
|  | for (int i = 0; i < 5; i++) { | 
|  | lock.Acquire(); | 
|  | acquired++; | 
|  | PlatformThread::Sleep(TimeDelta::FromMilliseconds(base::RandInt(0, 19))); | 
|  | lock.Release(); | 
|  | } | 
|  |  | 
|  | thread.Join(); | 
|  |  | 
|  | EXPECT_EQ(acquired, 20); | 
|  | EXPECT_EQ(thread.acquired(), 20); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, AcquirePredecessor) { | 
|  | SchedulerLock predecessor; | 
|  | SchedulerLock lock(&predecessor); | 
|  | predecessor.Acquire(); | 
|  | lock.Acquire(); | 
|  | lock.Release(); | 
|  | predecessor.Release(); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, AcquirePredecessorWrongOrder) { | 
|  | SchedulerLock predecessor; | 
|  | SchedulerLock lock(&predecessor); | 
|  | EXPECT_DCHECK_DEATH({ | 
|  | lock.Acquire(); | 
|  | predecessor.Acquire(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, AcquireNonPredecessor) { | 
|  | SchedulerLock lock1; | 
|  | SchedulerLock lock2; | 
|  | EXPECT_DCHECK_DEATH({ | 
|  | lock1.Acquire(); | 
|  | lock2.Acquire(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, AcquireMultipleLocksInOrder) { | 
|  | SchedulerLock lock1; | 
|  | SchedulerLock lock2(&lock1); | 
|  | SchedulerLock lock3(&lock2); | 
|  | lock1.Acquire(); | 
|  | lock2.Acquire(); | 
|  | lock3.Acquire(); | 
|  | lock3.Release(); | 
|  | lock2.Release(); | 
|  | lock1.Release(); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, AcquireMultipleLocksInTheMiddleOfAChain) { | 
|  | SchedulerLock lock1; | 
|  | SchedulerLock lock2(&lock1); | 
|  | SchedulerLock lock3(&lock2); | 
|  | lock2.Acquire(); | 
|  | lock3.Acquire(); | 
|  | lock3.Release(); | 
|  | lock2.Release(); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, AcquireMultipleLocksNoTransitivity) { | 
|  | SchedulerLock lock1; | 
|  | SchedulerLock lock2(&lock1); | 
|  | SchedulerLock lock3(&lock2); | 
|  | EXPECT_DCHECK_DEATH({ | 
|  | lock1.Acquire(); | 
|  | lock3.Acquire(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, AcquireLocksDifferentThreadsSafely) { | 
|  | SchedulerLock lock1; | 
|  | SchedulerLock lock2; | 
|  | BasicLockAcquireAndWaitThread thread(&lock1); | 
|  | thread.Start(); | 
|  |  | 
|  | lock2.Acquire(); | 
|  | thread.WaitForLockAcquisition(); | 
|  | thread.ContinueMain(); | 
|  | thread.Join(); | 
|  | lock2.Release(); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, | 
|  | AcquireLocksWithPredecessorDifferentThreadsSafelyPredecessorFirst) { | 
|  | // A lock and its predecessor may be safely acquired on different threads. | 
|  | // This Thread                Other Thread | 
|  | // predecessor.Acquire() | 
|  | //                            lock.Acquire() | 
|  | // predecessor.Release() | 
|  | //                            lock.Release() | 
|  | SchedulerLock predecessor; | 
|  | SchedulerLock lock(&predecessor); | 
|  | predecessor.Acquire(); | 
|  | BasicLockAcquireAndWaitThread thread(&lock); | 
|  | thread.Start(); | 
|  | thread.WaitForLockAcquisition(); | 
|  | predecessor.Release(); | 
|  | thread.ContinueMain(); | 
|  | thread.Join(); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, | 
|  | AcquireLocksWithPredecessorDifferentThreadsSafelyPredecessorLast) { | 
|  | // A lock and its predecessor may be safely acquired on different threads. | 
|  | // This Thread                Other Thread | 
|  | // lock.Acquire() | 
|  | //                            predecessor.Acquire() | 
|  | // lock.Release() | 
|  | //                            predecessor.Release() | 
|  | SchedulerLock predecessor; | 
|  | SchedulerLock lock(&predecessor); | 
|  | lock.Acquire(); | 
|  | BasicLockAcquireAndWaitThread thread(&predecessor); | 
|  | thread.Start(); | 
|  | thread.WaitForLockAcquisition(); | 
|  | lock.Release(); | 
|  | thread.ContinueMain(); | 
|  | thread.Join(); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, | 
|  | AcquireLocksWithPredecessorDifferentThreadsSafelyNoInterference) { | 
|  | // Acquisition of an unrelated lock on another thread should not affect a | 
|  | // legal lock acquisition with a predecessor on this thread. | 
|  | // This Thread                Other Thread | 
|  | // predecessor.Acquire() | 
|  | //                            unrelated.Acquire() | 
|  | // lock.Acquire() | 
|  | //                            unrelated.Release() | 
|  | // lock.Release() | 
|  | // predecessor.Release(); | 
|  | SchedulerLock predecessor; | 
|  | SchedulerLock lock(&predecessor); | 
|  | predecessor.Acquire(); | 
|  | SchedulerLock unrelated; | 
|  | BasicLockAcquireAndWaitThread thread(&unrelated); | 
|  | thread.Start(); | 
|  | thread.WaitForLockAcquisition(); | 
|  | lock.Acquire(); | 
|  | thread.ContinueMain(); | 
|  | thread.Join(); | 
|  | lock.Release(); | 
|  | predecessor.Release(); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, SelfReferentialLock) { | 
|  | struct SelfReferentialLock { | 
|  | SelfReferentialLock() : lock(&lock) {} | 
|  |  | 
|  | SchedulerLock lock; | 
|  | }; | 
|  |  | 
|  | EXPECT_DCHECK_DEATH({ SelfReferentialLock lock; }); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, PredecessorCycle) { | 
|  | struct LockCycle { | 
|  | LockCycle() : lock1(&lock2), lock2(&lock1) {} | 
|  |  | 
|  | SchedulerLock lock1; | 
|  | SchedulerLock lock2; | 
|  | }; | 
|  |  | 
|  | EXPECT_DCHECK_DEATH({ LockCycle cycle; }); | 
|  | } | 
|  |  | 
|  | TEST(TaskSchedulerLock, PredecessorLongerCycle) { | 
|  | struct LockCycle { | 
|  | LockCycle() | 
|  | : lock1(&lock5), | 
|  | lock2(&lock1), | 
|  | lock3(&lock2), | 
|  | lock4(&lock3), | 
|  | lock5(&lock4) {} | 
|  |  | 
|  | SchedulerLock lock1; | 
|  | SchedulerLock lock2; | 
|  | SchedulerLock lock3; | 
|  | SchedulerLock lock4; | 
|  | SchedulerLock lock5; | 
|  | }; | 
|  |  | 
|  | EXPECT_DCHECK_DEATH({ LockCycle cycle; }); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace internal | 
|  | }  // namespace base |