// 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 "butil/synchronization/lock.h" #include #include "butil/compiler_specific.h" #include "butil/threading/platform_thread.h" #include namespace butil { // Basic test to make sure that Acquire()/Release()/Try() don't crash ---------- class BasicLockTestThread : public PlatformThread::Delegate { public: explicit 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; 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: explicit 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; 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; 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; 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; PlatformThreadHandle handle2; PlatformThreadHandle handle3; 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 butil