This source file includes following definitions.
- TEST
- shared_value_
- Fiddle
- SleepALittle
- Run
- TEST
- try_lock_succeeded_
- Run
- try_lock_succeeded
- TEST
- TEST
- TEST
#include "mojo/public/cpp/utility/mutex.h"
#include <stdlib.h>
#include <time.h>
#include <vector>
#include "mojo/public/cpp/system/macros.h"
#include "mojo/public/cpp/utility/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace {
TEST(MutexTest, TrivialSingleThreaded) {
Mutex mutex;
mutex.Lock();
mutex.AssertHeld();
mutex.Unlock();
EXPECT_TRUE(mutex.TryLock());
mutex.AssertHeld();
mutex.Unlock();
{
MutexLock lock(&mutex);
mutex.AssertHeld();
}
EXPECT_TRUE(mutex.TryLock());
mutex.Unlock();
}
class Fiddler {
public:
enum Type { kTypeLock, kTypeTry };
Fiddler(size_t times_to_lock,
Type type,
bool should_sleep,
Mutex* mutex,
int* shared_value)
: times_to_lock_(times_to_lock),
type_(type),
should_sleep_(should_sleep),
mutex_(mutex),
shared_value_(shared_value) {
}
~Fiddler() {
}
void Fiddle() {
for (size_t i = 0; i < times_to_lock_;) {
switch (type_) {
case kTypeLock: {
mutex_->Lock();
int old_shared_value = *shared_value_;
if (should_sleep_)
SleepALittle();
*shared_value_ = old_shared_value + 1;
mutex_->Unlock();
i++;
break;
}
case kTypeTry:
if (mutex_->TryLock()) {
int old_shared_value = *shared_value_;
if (should_sleep_)
SleepALittle();
*shared_value_ = old_shared_value + 1;
mutex_->Unlock();
i++;
} else {
SleepALittle();
}
break;
}
}
}
private:
static void SleepALittle() {
static const long kNanosPerMilli = 1000000;
struct timespec req = {
0,
(rand() % 10) * kNanosPerMilli
};
int rv MOJO_ALLOW_UNUSED = nanosleep(&req, NULL);
assert(rv == 0);
}
const size_t times_to_lock_;
const Type type_;
const bool should_sleep_;
Mutex* const mutex_;
int* const shared_value_;
MOJO_DISALLOW_COPY_AND_ASSIGN(Fiddler);
};
class FiddlerThread : public Thread {
public:
FiddlerThread(Fiddler* fiddler)
: fiddler_(fiddler) {
}
virtual ~FiddlerThread() {
delete fiddler_;
}
virtual void Run() MOJO_OVERRIDE {
fiddler_->Fiddle();
}
private:
Fiddler* const fiddler_;
MOJO_DISALLOW_COPY_AND_ASSIGN(FiddlerThread);
};
TEST(MutexTest, ThreadedStress) {
static const size_t kNumThreads = 20;
static const int kTimesToLockEach = 20;
assert(kNumThreads % 4 == 0);
Mutex mutex;
int shared_value = 0;
std::vector<FiddlerThread*> fiddler_threads;
for (size_t i = 0; i < kNumThreads; i += 4) {
fiddler_threads.push_back(new FiddlerThread(new Fiddler(
kTimesToLockEach, Fiddler::kTypeLock, false, &mutex, &shared_value)));
fiddler_threads.push_back(new FiddlerThread(new Fiddler(
kTimesToLockEach, Fiddler::kTypeTry, false, &mutex, &shared_value)));
fiddler_threads.push_back(new FiddlerThread(new Fiddler(
kTimesToLockEach, Fiddler::kTypeLock, true, &mutex, &shared_value)));
fiddler_threads.push_back(new FiddlerThread(new Fiddler(
kTimesToLockEach, Fiddler::kTypeTry, true, &mutex, &shared_value)));
}
for (size_t i = 0; i < kNumThreads; i++)
fiddler_threads[i]->Start();
Fiddler(kTimesToLockEach, Fiddler::kTypeLock, true, &mutex, &shared_value)
.Fiddle();
for (size_t i = 0; i < kNumThreads; i++)
fiddler_threads[i]->Join();
EXPECT_EQ(static_cast<int>(kNumThreads + 1) * kTimesToLockEach, shared_value);
for (size_t i = 0; i < kNumThreads; i++)
delete fiddler_threads[i];
fiddler_threads.clear();
}
class TryThread : public Thread {
public:
explicit TryThread(Mutex* mutex) : mutex_(mutex), try_lock_succeeded_() {}
virtual ~TryThread() {}
virtual void Run() MOJO_OVERRIDE {
try_lock_succeeded_ = mutex_->TryLock();
if (try_lock_succeeded_)
mutex_->Unlock();
}
bool try_lock_succeeded() const { return try_lock_succeeded_; }
private:
Mutex* const mutex_;
bool try_lock_succeeded_;
MOJO_DISALLOW_COPY_AND_ASSIGN(TryThread);
};
TEST(MutexTest, TryLock) {
Mutex mutex;
{
TryThread thread(&mutex);
thread.Start();
thread.Join();
EXPECT_TRUE(thread.try_lock_succeeded());
}
ASSERT_TRUE(mutex.TryLock());
{
TryThread thread(&mutex);
thread.Start();
thread.Join();
EXPECT_FALSE(thread.try_lock_succeeded());
}
mutex.Unlock();
{
TryThread thread(&mutex);
thread.Start();
thread.Join();
EXPECT_TRUE(thread.try_lock_succeeded());
}
}
#if !defined(NDEBUG)
TEST(MutexTest, DebugAssertHeldFailure) {
Mutex mutex;
EXPECT_DEATH(mutex.AssertHeld(), "");
}
TEST(MutexTest, DebugAssertionFailures) {
EXPECT_DEATH({
Mutex mutex;
mutex.Unlock();
}, "");
EXPECT_DEATH({
Mutex mutex;
mutex.Lock();
mutex.Lock();
}, "");
EXPECT_DEATH({
Mutex mutex;
mutex.Lock();
mutex.TryLock();
}, "");
EXPECT_DEATH({
Mutex mutex;
mutex.Lock();
}, "");
}
#endif
}
}