Chromium Code Reviews| Index: base/task_scheduler/scheduler_lock_unittest.cc | 
| diff --git a/base/task_scheduler/scheduler_lock_unittest.cc b/base/task_scheduler/scheduler_lock_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..48964e93741aad2106f1215b5468bb687ccc3d93 | 
| --- /dev/null | 
| +++ b/base/task_scheduler/scheduler_lock_unittest.cc | 
| @@ -0,0 +1,257 @@ | 
| +// 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_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/threading/platform_thread.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| + | 
| +namespace base { | 
| +namespace internal { | 
| + | 
| +// Adapted from base::Lock's BasicLockTestThread to make sure | 
| +// Acquire()/Release() don't crash. | 
| +class BasicLockTestThread : public PlatformThread::Delegate { | 
| + public: | 
| + explicit BasicLockTestThread(SchedulerLock* lock) | 
| + : lock_(lock), | 
| + acquired_(0) {} | 
| + | 
| + void ThreadMain() 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(); | 
| + } | 
| + } | 
| + | 
| + int acquired() const { return acquired_; } | 
| + | 
| + private: | 
| + SchedulerLock* const lock_; | 
| + int acquired_; | 
| + | 
| + DISALLOW_COPY_AND_ASSIGN(BasicLockTestThread); | 
| +}; | 
| + | 
| +class BasicLockAcquireAndWaitThread : public PlatformThread::Delegate { | 
| + public: | 
| + explicit BasicLockAcquireAndWaitThread(SchedulerLock* lock) | 
| + : lock_(lock), | 
| + lock_acquire_event_(false, false), | 
| + main_thread_continue_event_(false, false) {} | 
| + | 
| + void ThreadMain() override { | 
| + lock_->Acquire(); | 
| + lock_acquire_event_.Signal(); | 
| + main_thread_continue_event_.Wait(); | 
| + lock_->Release(); | 
| + } | 
| + | 
| + void WaitForLockAcquisition() { | 
| + lock_acquire_event_.Wait(); | 
| + } | 
| + | 
| + void ContinueMain() { | 
| + main_thread_continue_event_.Signal(); | 
| + } | 
| + | 
| + private: | 
| + 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); | 
| + PlatformThreadHandle handle; | 
| + | 
| + ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); | 
| + | 
| + 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(rand() % 20)); | 
| + lock.Release(); | 
| + } | 
| + for (int i = 0; i < 5; i++) { | 
| + lock.Acquire(); | 
| + acquired++; | 
| + PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20)); | 
| + lock.Release(); | 
| + } | 
| + | 
| + PlatformThread::Join(handle); | 
| + | 
| + 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, AcquireNonPredecessor) { | 
| + SchedulerLock lock1; | 
| + SchedulerLock lock2; | 
| + EXPECT_DEBUG_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_DEBUG_DEATH({ | 
| + lock1.Acquire(); | 
| + lock3.Acquire(); | 
| + }, ""); | 
| +} | 
| + | 
| +TEST(TaskSchedulerLock, AcquireLocksDifferentThreadsSafely) { | 
| + SchedulerLock lock1; | 
| + SchedulerLock lock2; | 
| + BasicLockAcquireAndWaitThread thread(&lock1); | 
| + PlatformThreadHandle handle; | 
| + ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); | 
| + | 
| + lock2.Acquire(); | 
| + thread.WaitForLockAcquisition(); | 
| + thread.ContinueMain(); | 
| + PlatformThread::Join(handle); | 
| + lock2.Release(); | 
| +} | 
| 
 
gab
2016/02/19 22:05:24
What is this now testing that the next two tests a
 
robliao
2016/02/19 23:51:12
This tests the absence of any predecessor relation
 
 | 
| + | 
| +TEST(TaskSchedulerLock, | 
| + AcquireLocksWithPredecessorDifferentThreadsSafely1PredecessorFirst) { | 
| + // 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); | 
| + PlatformThreadHandle handle; | 
| + ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); | 
| + thread.WaitForLockAcquisition(); | 
| + predecessor.Release(); | 
| + thread.ContinueMain(); | 
| + PlatformThread::Join(handle); | 
| +} | 
| + | 
| +TEST(TaskSchedulerLock, | 
| + AcquireLocksWithPredecessorDifferentThreadsSafely1PredecessorLast) { | 
| + // 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); | 
| + PlatformThreadHandle handle; | 
| + ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); | 
| + thread.WaitForLockAcquisition(); | 
| + lock.Release(); | 
| + thread.ContinueMain(); | 
| + PlatformThread::Join(handle); | 
| +} | 
| + | 
| +TEST(TaskSchedulerLock, SelfReferentialLock) { | 
| + struct SelfReferentialLock { | 
| + SelfReferentialLock() : lock(&lock) {} | 
| + | 
| + SchedulerLock lock; | 
| + }; | 
| + | 
| + EXPECT_DEBUG_DEATH({ SelfReferentialLock lock; }, ""); | 
| +} | 
| + | 
| +TEST(TaskSchedulerLock, PredecessorCycle) { | 
| + struct LockCycle { | 
| + LockCycle() : lock1(&lock2), lock2(&lock1) {} | 
| + | 
| + SchedulerLock lock1; | 
| + SchedulerLock lock2; | 
| + }; | 
| + | 
| + EXPECT_DEBUG_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_DEBUG_DEATH({ LockCycle cycle; }, ""); | 
| +} | 
| + | 
| +} // namespace internal | 
| +} // base |