// 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