| // Copyright (c) 2012 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/synchronization/lock.h" |
| |
| #include <stdlib.h> |
| |
| #include "base/compiler_specific.h" |
| #include "base/threading/platform_thread.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace base { |
| |
| // Basic test to make sure that Acquire()/Release()/Try() don't crash ---------- |
| |
| class BasicLockTestThread : public PlatformThread::Delegate { |
| public: |
| BasicLockTestThread(Lock* lock) : lock_(lock), acquired_(0) {} |
| |
| virtual 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(rand() % 20)); |
| lock_->Release(); |
| } |
| for (int i = 0; i < 10; i++) { |
| if (lock_->Try()) { |
| acquired_++; |
| PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20)); |
| lock_->Release(); |
| } |
| } |
| } |
| |
| int acquired() const { return acquired_; } |
| |
| private: |
| Lock* lock_; |
| int acquired_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BasicLockTestThread); |
| }; |
| |
| TEST(LockTest, Basic) { |
| Lock lock; |
| BasicLockTestThread thread(&lock); |
| PlatformThreadHandle handle = kNullThreadHandle; |
| |
| 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 < 10; i++) { |
| if (lock.Try()) { |
| 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_GE(acquired, 20); |
| EXPECT_GE(thread.acquired(), 20); |
| } |
| |
| // Test that Try() works as expected ------------------------------------------- |
| |
| class TryLockTestThread : public PlatformThread::Delegate { |
| public: |
| TryLockTestThread(Lock* lock) : lock_(lock), got_lock_(false) {} |
| |
| virtual void ThreadMain() override { |
| got_lock_ = lock_->Try(); |
| if (got_lock_) |
| lock_->Release(); |
| } |
| |
| bool got_lock() const { return got_lock_; } |
| |
| private: |
| Lock* lock_; |
| bool got_lock_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TryLockTestThread); |
| }; |
| |
| TEST(LockTest, TryLock) { |
| Lock lock; |
| |
| ASSERT_TRUE(lock.Try()); |
| // We now have the lock.... |
| |
| // This thread will not be able to get the lock. |
| { |
| TryLockTestThread thread(&lock); |
| PlatformThreadHandle handle = kNullThreadHandle; |
| |
| ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); |
| |
| PlatformThread::Join(handle); |
| |
| ASSERT_FALSE(thread.got_lock()); |
| } |
| |
| lock.Release(); |
| |
| // This thread will.... |
| { |
| TryLockTestThread thread(&lock); |
| PlatformThreadHandle handle = kNullThreadHandle; |
| |
| ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); |
| |
| PlatformThread::Join(handle); |
| |
| ASSERT_TRUE(thread.got_lock()); |
| // But it released it.... |
| ASSERT_TRUE(lock.Try()); |
| } |
| |
| lock.Release(); |
| } |
| |
| // Tests that locks actually exclude ------------------------------------------- |
| |
| class MutexLockTestThread : public PlatformThread::Delegate { |
| public: |
| MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {} |
| |
| // Static helper which can also be called from the main thread. |
| static void DoStuff(Lock* lock, int* value) { |
| for (int i = 0; i < 40; i++) { |
| lock->Acquire(); |
| int v = *value; |
| PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 10)); |
| *value = v + 1; |
| lock->Release(); |
| } |
| } |
| |
| virtual void ThreadMain() override { |
| DoStuff(lock_, value_); |
| } |
| |
| private: |
| Lock* lock_; |
| int* value_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MutexLockTestThread); |
| }; |
| |
| TEST(LockTest, MutexTwoThreads) { |
| Lock lock; |
| int value = 0; |
| |
| MutexLockTestThread thread(&lock, &value); |
| PlatformThreadHandle handle = kNullThreadHandle; |
| |
| ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); |
| |
| MutexLockTestThread::DoStuff(&lock, &value); |
| |
| PlatformThread::Join(handle); |
| |
| EXPECT_EQ(2 * 40, value); |
| } |
| |
| TEST(LockTest, MutexFourThreads) { |
| Lock lock; |
| int value = 0; |
| |
| MutexLockTestThread thread1(&lock, &value); |
| MutexLockTestThread thread2(&lock, &value); |
| MutexLockTestThread thread3(&lock, &value); |
| PlatformThreadHandle handle1 = kNullThreadHandle; |
| PlatformThreadHandle handle2 = kNullThreadHandle; |
| PlatformThreadHandle handle3 = kNullThreadHandle; |
| |
| ASSERT_TRUE(PlatformThread::Create(0, &thread1, &handle1)); |
| ASSERT_TRUE(PlatformThread::Create(0, &thread2, &handle2)); |
| ASSERT_TRUE(PlatformThread::Create(0, &thread3, &handle3)); |
| |
| MutexLockTestThread::DoStuff(&lock, &value); |
| |
| PlatformThread::Join(handle1); |
| PlatformThread::Join(handle2); |
| PlatformThread::Join(handle3); |
| |
| EXPECT_EQ(4 * 40, value); |
| } |
| |
| } // namespace base |