217 lines
4.9 KiB
C++
217 lines
4.9 KiB
C++
// 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:
|
|
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 base
|