2014-08-06 09:35:21 +00:00
|
|
|
// Copyright 2014 the V8 project 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 "src/base/platform/condition-variable.h"
|
|
|
|
|
|
|
|
#include "src/base/platform/platform.h"
|
|
|
|
#include "src/base/platform/time.h"
|
|
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace base {
|
|
|
|
|
|
|
|
TEST(ConditionVariable, WaitForAfterNofityOnSameThread) {
|
|
|
|
for (int n = 0; n < 10; ++n) {
|
|
|
|
Mutex mutex;
|
|
|
|
ConditionVariable cv;
|
|
|
|
|
|
|
|
LockGuard<Mutex> lock_guard(&mutex);
|
|
|
|
|
|
|
|
cv.NotifyOne();
|
|
|
|
EXPECT_FALSE(cv.WaitFor(&mutex, TimeDelta::FromMicroseconds(n)));
|
|
|
|
|
|
|
|
cv.NotifyAll();
|
|
|
|
EXPECT_FALSE(cv.WaitFor(&mutex, TimeDelta::FromMicroseconds(n)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2015-04-20 13:08:11 +00:00
|
|
|
class ThreadWithMutexAndConditionVariable final : public Thread {
|
2014-08-06 09:35:21 +00:00
|
|
|
public:
|
|
|
|
ThreadWithMutexAndConditionVariable()
|
2014-08-12 13:33:35 +00:00
|
|
|
: Thread(Options("ThreadWithMutexAndConditionVariable")),
|
|
|
|
running_(false),
|
|
|
|
finished_(false) {}
|
2014-08-06 09:35:21 +00:00
|
|
|
virtual ~ThreadWithMutexAndConditionVariable() {}
|
|
|
|
|
2015-04-20 13:08:11 +00:00
|
|
|
virtual void Run() override {
|
2014-08-06 09:35:21 +00:00
|
|
|
LockGuard<Mutex> lock_guard(&mutex_);
|
|
|
|
running_ = true;
|
|
|
|
cv_.NotifyOne();
|
|
|
|
while (running_) {
|
|
|
|
cv_.Wait(&mutex_);
|
|
|
|
}
|
|
|
|
finished_ = true;
|
|
|
|
cv_.NotifyAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool running_;
|
|
|
|
bool finished_;
|
|
|
|
ConditionVariable cv_;
|
|
|
|
Mutex mutex_;
|
|
|
|
};
|
|
|
|
|
2014-08-29 10:53:08 +00:00
|
|
|
} // namespace
|
2014-08-06 09:35:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
TEST(ConditionVariable, MultipleThreadsWithSeparateConditionVariables) {
|
|
|
|
static const int kThreadCount = 128;
|
|
|
|
ThreadWithMutexAndConditionVariable threads[kThreadCount];
|
|
|
|
|
|
|
|
for (int n = 0; n < kThreadCount; ++n) {
|
|
|
|
LockGuard<Mutex> lock_guard(&threads[n].mutex_);
|
|
|
|
EXPECT_FALSE(threads[n].running_);
|
|
|
|
EXPECT_FALSE(threads[n].finished_);
|
|
|
|
threads[n].Start();
|
|
|
|
// Wait for nth thread to start.
|
|
|
|
while (!threads[n].running_) {
|
|
|
|
threads[n].cv_.Wait(&threads[n].mutex_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int n = kThreadCount - 1; n >= 0; --n) {
|
|
|
|
LockGuard<Mutex> lock_guard(&threads[n].mutex_);
|
|
|
|
EXPECT_TRUE(threads[n].running_);
|
|
|
|
EXPECT_FALSE(threads[n].finished_);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int n = 0; n < kThreadCount; ++n) {
|
|
|
|
LockGuard<Mutex> lock_guard(&threads[n].mutex_);
|
|
|
|
EXPECT_TRUE(threads[n].running_);
|
|
|
|
EXPECT_FALSE(threads[n].finished_);
|
|
|
|
// Tell the nth thread to quit.
|
|
|
|
threads[n].running_ = false;
|
|
|
|
threads[n].cv_.NotifyOne();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int n = kThreadCount - 1; n >= 0; --n) {
|
|
|
|
// Wait for nth thread to quit.
|
|
|
|
LockGuard<Mutex> lock_guard(&threads[n].mutex_);
|
|
|
|
while (!threads[n].finished_) {
|
|
|
|
threads[n].cv_.Wait(&threads[n].mutex_);
|
|
|
|
}
|
|
|
|
EXPECT_FALSE(threads[n].running_);
|
|
|
|
EXPECT_TRUE(threads[n].finished_);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int n = 0; n < kThreadCount; ++n) {
|
|
|
|
threads[n].Join();
|
|
|
|
LockGuard<Mutex> lock_guard(&threads[n].mutex_);
|
|
|
|
EXPECT_FALSE(threads[n].running_);
|
|
|
|
EXPECT_TRUE(threads[n].finished_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2015-04-20 13:08:11 +00:00
|
|
|
class ThreadWithSharedMutexAndConditionVariable final : public Thread {
|
2014-08-06 09:35:21 +00:00
|
|
|
public:
|
|
|
|
ThreadWithSharedMutexAndConditionVariable()
|
2014-08-12 13:33:35 +00:00
|
|
|
: Thread(Options("ThreadWithSharedMutexAndConditionVariable")),
|
|
|
|
running_(false),
|
|
|
|
finished_(false),
|
|
|
|
cv_(NULL),
|
|
|
|
mutex_(NULL) {}
|
2014-08-06 09:35:21 +00:00
|
|
|
virtual ~ThreadWithSharedMutexAndConditionVariable() {}
|
|
|
|
|
2015-04-20 13:08:11 +00:00
|
|
|
virtual void Run() override {
|
2014-08-06 09:35:21 +00:00
|
|
|
LockGuard<Mutex> lock_guard(mutex_);
|
|
|
|
running_ = true;
|
|
|
|
cv_->NotifyAll();
|
|
|
|
while (running_) {
|
|
|
|
cv_->Wait(mutex_);
|
|
|
|
}
|
|
|
|
finished_ = true;
|
|
|
|
cv_->NotifyAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool running_;
|
|
|
|
bool finished_;
|
|
|
|
ConditionVariable* cv_;
|
|
|
|
Mutex* mutex_;
|
|
|
|
};
|
|
|
|
|
2014-08-29 10:53:08 +00:00
|
|
|
} // namespace
|
2014-08-06 09:35:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
TEST(ConditionVariable, MultipleThreadsWithSharedSeparateConditionVariables) {
|
|
|
|
static const int kThreadCount = 128;
|
|
|
|
ThreadWithSharedMutexAndConditionVariable threads[kThreadCount];
|
|
|
|
ConditionVariable cv;
|
|
|
|
Mutex mutex;
|
|
|
|
|
|
|
|
for (int n = 0; n < kThreadCount; ++n) {
|
|
|
|
threads[n].mutex_ = &mutex;
|
|
|
|
threads[n].cv_ = &cv;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start all threads.
|
|
|
|
{
|
|
|
|
LockGuard<Mutex> lock_guard(&mutex);
|
|
|
|
for (int n = 0; n < kThreadCount; ++n) {
|
|
|
|
EXPECT_FALSE(threads[n].running_);
|
|
|
|
EXPECT_FALSE(threads[n].finished_);
|
|
|
|
threads[n].Start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for all threads to start.
|
|
|
|
{
|
|
|
|
LockGuard<Mutex> lock_guard(&mutex);
|
|
|
|
for (int n = kThreadCount - 1; n >= 0; --n) {
|
|
|
|
while (!threads[n].running_) {
|
|
|
|
cv.Wait(&mutex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure that all threads are running.
|
|
|
|
{
|
|
|
|
LockGuard<Mutex> lock_guard(&mutex);
|
|
|
|
for (int n = 0; n < kThreadCount; ++n) {
|
|
|
|
EXPECT_TRUE(threads[n].running_);
|
|
|
|
EXPECT_FALSE(threads[n].finished_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tell all threads to quit.
|
|
|
|
{
|
|
|
|
LockGuard<Mutex> lock_guard(&mutex);
|
|
|
|
for (int n = kThreadCount - 1; n >= 0; --n) {
|
|
|
|
EXPECT_TRUE(threads[n].running_);
|
|
|
|
EXPECT_FALSE(threads[n].finished_);
|
|
|
|
// Tell the nth thread to quit.
|
|
|
|
threads[n].running_ = false;
|
|
|
|
}
|
|
|
|
cv.NotifyAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for all threads to quit.
|
|
|
|
{
|
|
|
|
LockGuard<Mutex> lock_guard(&mutex);
|
|
|
|
for (int n = 0; n < kThreadCount; ++n) {
|
|
|
|
while (!threads[n].finished_) {
|
|
|
|
cv.Wait(&mutex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure all threads are finished.
|
|
|
|
{
|
|
|
|
LockGuard<Mutex> lock_guard(&mutex);
|
|
|
|
for (int n = kThreadCount - 1; n >= 0; --n) {
|
|
|
|
EXPECT_FALSE(threads[n].running_);
|
|
|
|
EXPECT_TRUE(threads[n].finished_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Join all threads.
|
|
|
|
for (int n = 0; n < kThreadCount; ++n) {
|
|
|
|
threads[n].Join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2015-04-20 13:08:11 +00:00
|
|
|
class LoopIncrementThread final : public Thread {
|
2014-08-06 09:35:21 +00:00
|
|
|
public:
|
2014-08-12 13:33:35 +00:00
|
|
|
LoopIncrementThread(int rem, int* counter, int limit, int thread_count,
|
|
|
|
ConditionVariable* cv, Mutex* mutex)
|
|
|
|
: Thread(Options("LoopIncrementThread")),
|
|
|
|
rem_(rem),
|
|
|
|
counter_(counter),
|
|
|
|
limit_(limit),
|
|
|
|
thread_count_(thread_count),
|
|
|
|
cv_(cv),
|
|
|
|
mutex_(mutex) {
|
2014-08-06 09:35:21 +00:00
|
|
|
EXPECT_LT(rem, thread_count);
|
|
|
|
EXPECT_EQ(0, limit % thread_count);
|
|
|
|
}
|
|
|
|
|
2015-04-20 13:08:11 +00:00
|
|
|
virtual void Run() override {
|
2014-08-06 09:35:21 +00:00
|
|
|
int last_count = -1;
|
|
|
|
while (true) {
|
|
|
|
LockGuard<Mutex> lock_guard(mutex_);
|
|
|
|
int count = *counter_;
|
|
|
|
while (count % thread_count_ != rem_ && count < limit_) {
|
|
|
|
cv_->Wait(mutex_);
|
|
|
|
count = *counter_;
|
|
|
|
}
|
|
|
|
if (count >= limit_) break;
|
|
|
|
EXPECT_EQ(*counter_, count);
|
|
|
|
if (last_count != -1) {
|
|
|
|
EXPECT_EQ(last_count + (thread_count_ - 1), count);
|
|
|
|
}
|
|
|
|
count++;
|
|
|
|
*counter_ = count;
|
|
|
|
last_count = count;
|
|
|
|
cv_->NotifyAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
const int rem_;
|
|
|
|
int* counter_;
|
|
|
|
const int limit_;
|
|
|
|
const int thread_count_;
|
|
|
|
ConditionVariable* cv_;
|
|
|
|
Mutex* mutex_;
|
|
|
|
};
|
|
|
|
|
2014-08-29 10:53:08 +00:00
|
|
|
} // namespace
|
2014-08-06 09:35:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
TEST(ConditionVariable, LoopIncrement) {
|
|
|
|
static const int kMaxThreadCount = 16;
|
|
|
|
Mutex mutex;
|
|
|
|
ConditionVariable cv;
|
|
|
|
for (int thread_count = 1; thread_count < kMaxThreadCount; ++thread_count) {
|
|
|
|
int limit = thread_count * 10;
|
|
|
|
int counter = 0;
|
|
|
|
|
|
|
|
// Setup the threads.
|
2014-08-29 10:53:08 +00:00
|
|
|
Thread** threads = new Thread* [thread_count];
|
2014-08-06 09:35:21 +00:00
|
|
|
for (int n = 0; n < thread_count; ++n) {
|
2014-08-29 10:53:08 +00:00
|
|
|
threads[n] = new LoopIncrementThread(n, &counter, limit, thread_count,
|
|
|
|
&cv, &mutex);
|
2014-08-06 09:35:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Start all threads.
|
|
|
|
for (int n = thread_count - 1; n >= 0; --n) {
|
|
|
|
threads[n]->Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Join and cleanup all threads.
|
|
|
|
for (int n = 0; n < thread_count; ++n) {
|
|
|
|
threads[n]->Join();
|
|
|
|
delete threads[n];
|
|
|
|
}
|
|
|
|
delete[] threads;
|
|
|
|
|
|
|
|
EXPECT_EQ(limit, counter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace base
|
|
|
|
} // namespace v8
|