| Index: runtime/vm/thread_pool_test.cc
|
| ===================================================================
|
| --- runtime/vm/thread_pool_test.cc (revision 0)
|
| +++ runtime/vm/thread_pool_test.cc (revision 0)
|
| @@ -0,0 +1,220 @@
|
| +// 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(1, thread_pool.workers_started());
|
| + EXPECT_EQ(0, thread_pool.workers_stopped());
|
| + EXPECT_EQ(1, thread_pool.workers_idle());
|
| + EXPECT_EQ(0, thread_pool.workers_running());
|
| +}
|
| +
|
| +
|
| +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(0, thread_pool.workers_started());
|
| + EXPECT_EQ(0, thread_pool.workers_stopped());
|
| +
|
| + // Run a worker.
|
| + Monitor sync;
|
| + bool done = false;
|
| + thread_pool.Run(new TestTask(&sync, &done));
|
| + EXPECT_EQ(1, thread_pool.workers_started());
|
| + EXPECT_EQ(0, 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(1, 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
|
|
|