blob: 655f68283ccc4a73446c250cc35435850f52e842 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// 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/memory/raw_ptr.h"
#include "base/test/gtest_util.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:
explicit BasicLockTestThread(Lock* lock) : lock_(lock), acquired_(0) {}
BasicLockTestThread(const BasicLockTestThread&) = delete;
BasicLockTestThread& operator=(const BasicLockTestThread&) = delete;
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(Milliseconds(rand() % 20));
lock_->Release();
}
for (int i = 0; i < 10; i++) {
if (lock_->Try()) {
acquired_++;
PlatformThread::Sleep(Milliseconds(rand() % 20));
lock_->Release();
}
}
}
int acquired() const { return acquired_; }
private:
raw_ptr<Lock> lock_;
int acquired_;
};
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(Milliseconds(rand() % 20));
lock.Release();
}
for (int i = 0; i < 10; i++) {
if (lock.Try()) {
acquired++;
PlatformThread::Sleep(Milliseconds(rand() % 20));
lock.Release();
}
}
for (int i = 0; i < 5; i++) {
lock.Acquire();
acquired++;
PlatformThread::Sleep(Milliseconds(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) {}
TryLockTestThread(const TryLockTestThread&) = delete;
TryLockTestThread& operator=(const TryLockTestThread&) = delete;
void ThreadMain() override {
// The local variable is required for the static analyzer to see that the
// lock is properly released.
bool got_lock = lock_->Try();
got_lock_ = got_lock;
if (got_lock)
lock_->Release();
}
bool got_lock() const { return got_lock_; }
private:
raw_ptr<Lock> lock_;
bool got_lock_;
};
TEST(LockTest, TryLock) {
Lock lock;
ASSERT_TRUE(lock.Try());
lock.AssertAcquired();
// 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.AssertAcquired();
}
lock.Release();
}
// Tests that locks actually exclude -------------------------------------------
class MutexLockTestThread : public PlatformThread::Delegate {
public:
MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {}
MutexLockTestThread(const MutexLockTestThread&) = delete;
MutexLockTestThread& operator=(const MutexLockTestThread&) = delete;
// 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(Milliseconds(rand() % 10));
*value = v + 1;
lock->Release();
}
}
void ThreadMain() override { DoStuff(lock_, value_); }
private:
raw_ptr<Lock> lock_;
raw_ptr<int> value_;
};
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);
}
TEST(LockTest, AutoLockMaybe) {
Lock lock;
{
AutoLockMaybe auto_lock(&lock);
lock.AssertAcquired();
}
EXPECT_DCHECK_DEATH(lock.AssertAcquired());
}
TEST(LockTest, AutoLockMaybeNull) {
AutoLockMaybe auto_lock(nullptr);
}
TEST(LockTest, ReleasableAutoLockExplicitRelease) {
Lock lock;
ReleasableAutoLock auto_lock(&lock);
lock.AssertAcquired();
auto_lock.Release();
EXPECT_DCHECK_DEATH(lock.AssertAcquired());
}
TEST(LockTest, ReleasableAutoLockImplicitRelease) {
Lock lock;
{
ReleasableAutoLock auto_lock(&lock);
lock.AssertAcquired();
}
EXPECT_DCHECK_DEATH(lock.AssertAcquired());
}
} // namespace base