blob: 049cd1a86b89cad70590b6a7b84ebe25889e7670 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/os.h"
#include "vm/thread.h"
#include "vm/thread_pool.h"
#include "vm/unit_test.h"
namespace dart {
DECLARE_FLAG(int, worker_timeout_millis);
class ThreadPoolTestPeer {
public:
// When the pool has an exit monitor, workers notify a monitor just
// before they exit. This is only used in tests to make sure that
// Shutdown works.
static void SetExitMonitor(Monitor* exit_monitor, int* exit_count) {
ThreadPool::exit_monitor_ = exit_monitor;
ThreadPool::exit_count_ = exit_count;
}
};
UNIT_TEST_CASE(ThreadPool_Create) {
ThreadPool thread_pool;
}
class TestTask : public ThreadPool::Task {
public:
TestTask(Monitor* sync, bool* done)
: sync_(sync), done_(done) {
}
void Run() {
MonitorLocker ml(sync_);
*done_ = true;
ml.Notify();
}
private:
Monitor* sync_;
bool* done_;
};
UNIT_TEST_CASE(ThreadPool_RunOne) {
ThreadPool thread_pool;
Monitor sync;
bool done = false;
thread_pool.Run(new TestTask(&sync, &done));
{
MonitorLocker ml(&sync);
while (!done) {
ml.Wait();
}
}
EXPECT(done);
// Do a sanity test on the worker stats.
EXPECT_EQ(1U, thread_pool.workers_started());
EXPECT_EQ(0U, thread_pool.workers_stopped());
}
UNIT_TEST_CASE(ThreadPool_RunMany) {
const int kTaskCount = 100;
ThreadPool thread_pool;
Monitor sync[kTaskCount];
bool done[kTaskCount];
for (int i = 0; i < kTaskCount; i++) {
done[i] = false;
thread_pool.Run(new TestTask(&sync[i], &done[i]));
}
for (int i = 0; i < kTaskCount; i++) {
MonitorLocker ml(&sync[i]);
while (!done[i]) {
ml.Wait();
}
EXPECT(done[i]);
}
}
class SleepTask : public ThreadPool::Task {
public:
explicit SleepTask(int millis)
: millis_(millis) {
}
void Run() {
OS::Sleep(millis_);
}
private:
int millis_;
};
UNIT_TEST_CASE(ThreadPool_WorkerShutdown) {
Monitor exit_sync;
int exit_count = 0;
MonitorLocker ml(&exit_sync);
// Set up the ThreadPool so that workers notify before they exit.
ThreadPool* thread_pool = new ThreadPool();
ThreadPoolTestPeer::SetExitMonitor(&exit_sync, &exit_count);
// Run a single task.
thread_pool->Run(new SleepTask(2));
// Kill the thread pool.
delete thread_pool;
thread_pool = NULL;
// Wait for the workers to terminate.
while (exit_count == 0) {
ml.Wait();
}
EXPECT_EQ(1, exit_count);
}
UNIT_TEST_CASE(ThreadPool_WorkerTimeout) {
// Adjust the worker timeout so that we timeout quickly.
int saved_timeout = FLAG_worker_timeout_millis;
FLAG_worker_timeout_millis = 1;
ThreadPool thread_pool;
EXPECT_EQ(0U, thread_pool.workers_started());
EXPECT_EQ(0U, thread_pool.workers_stopped());
// Run a worker.
Monitor sync;
bool done = false;
thread_pool.Run(new TestTask(&sync, &done));
EXPECT_EQ(1U, thread_pool.workers_started());
EXPECT_EQ(0U, thread_pool.workers_stopped());
{
MonitorLocker ml(&sync);
while (!done) {
ml.Wait();
}
}
EXPECT(done);
// Wait up to 5 seconds to see if a worker times out.
const int kMaxWait = 5000;
int waited = 0;
while (thread_pool.workers_stopped() == 0 && waited < kMaxWait) {
OS::Sleep(1);
waited += 1;
}
EXPECT_EQ(1U, thread_pool.workers_stopped());
FLAG_worker_timeout_millis = saved_timeout;
}
class SpawnTask : public ThreadPool::Task {
public:
SpawnTask(ThreadPool* pool, Monitor* sync, int todo, int total, int* done)
: pool_(pool), sync_(sync), todo_(todo), total_(total), done_(done) {
}
void Run() {
todo_--; // Subtract one for current task.
int child_todo = todo_ / 2;
// Spawn 0-2 children.
if (todo_ > 0) {
pool_->Run(
new SpawnTask(pool_, sync_, todo_ - child_todo, total_, done_));
}
if (todo_ > 1) {
pool_->Run(
new SpawnTask(pool_, sync_, child_todo, total_, done_));
}
{
MonitorLocker ml(sync_);
(*done_)++;
if (*done_ >= total_) {
ml.Notify();
}
}
}
private:
ThreadPool* pool_;
Monitor* sync_;
int todo_;
int total_;
int* done_;
};
UNIT_TEST_CASE(ThreadPool_RecursiveSpawn) {
ThreadPool thread_pool;
Monitor sync;
const int kTotalTasks = 500;
int done = 0;
thread_pool.Run(
new SpawnTask(&thread_pool, &sync, kTotalTasks, kTotalTasks, &done));
{
MonitorLocker ml(&sync);
while (done < kTotalTasks) {
ml.Wait();
}
}
EXPECT_EQ(kTotalTasks, done);
}
} // namespace dart