| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/os.h" | 5 #include "vm/os.h" |
| 6 #include "vm/lockers.h" | 6 #include "vm/lockers.h" |
| 7 #include "vm/thread_pool.h" | 7 #include "vm/thread_pool.h" |
| 8 #include "vm/unit_test.h" | 8 #include "vm/unit_test.h" |
| 9 | 9 |
| 10 namespace dart { | 10 namespace dart { |
| 11 | 11 |
| 12 DECLARE_FLAG(int, worker_timeout_millis); | 12 DECLARE_FLAG(int, worker_timeout_millis); |
| 13 | 13 |
| 14 | |
| 15 class ThreadPoolTestPeer { | |
| 16 public: | |
| 17 // When the pool has an exit monitor, workers notify a monitor just | |
| 18 // before they exit. This is only used in tests to make sure that | |
| 19 // Shutdown works. | |
| 20 static void SetExitMonitor(Monitor* exit_monitor, int* exit_count) { | |
| 21 ThreadPool::exit_monitor_ = exit_monitor; | |
| 22 ThreadPool::exit_count_ = exit_count; | |
| 23 } | |
| 24 }; | |
| 25 | |
| 26 | |
| 27 UNIT_TEST_CASE(ThreadPool_Create) { | |
| 28 ThreadPool thread_pool; | |
| 29 } | |
| 30 | |
| 31 | |
| 32 class TestTask : public ThreadPool::Task { | 14 class TestTask : public ThreadPool::Task { |
| 33 public: | 15 public: |
| 34 TestTask(Monitor* sync, bool* done) | 16 TestTask(Monitor* sync, bool* done) |
| 35 : sync_(sync), done_(done) { | 17 : sync_(sync), done_(done) { |
| 36 } | 18 } |
| 37 | 19 |
| 38 virtual void Run() { | 20 virtual void Run() { |
| 39 MonitorLocker ml(sync_); | 21 MonitorLocker ml(sync_); |
| 40 *done_ = true; | 22 *done_ = true; |
| 41 ml.Notify(); | 23 ml.Notify(); |
| 42 } | 24 } |
| 43 | 25 |
| 44 private: | 26 private: |
| 45 Monitor* sync_; | 27 Monitor* sync_; |
| 46 bool* done_; | 28 bool* done_; |
| 47 }; | 29 }; |
| 48 | 30 |
| 49 | 31 |
| 50 UNIT_TEST_CASE(ThreadPool_RunOne) { | 32 UNIT_TEST_CASE(ThreadPool_RunOne) { |
| 51 ThreadPool thread_pool; | 33 ThreadPool::set_shutdown_timeout_fatal(false); |
| 52 Monitor sync; | 34 Monitor sync; |
| 53 bool done = false; | 35 bool done = false; |
| 54 thread_pool.Run(new TestTask(&sync, &done)); | 36 ThreadPool::Run(new TestTask(&sync, &done)); |
| 55 { | 37 { |
| 56 MonitorLocker ml(&sync); | 38 MonitorLocker ml(&sync); |
| 57 while (!done) { | 39 while (!done) { |
| 58 ml.Wait(); | 40 ml.Wait(); |
| 59 } | 41 } |
| 60 } | 42 } |
| 61 EXPECT(done); | 43 EXPECT(done); |
| 62 | 44 |
| 63 // Do a sanity test on the worker stats. | 45 // Do a sanity test on the worker stats. |
| 64 EXPECT_EQ(1U, thread_pool.workers_started()); | 46 EXPECT_LE(1U, ThreadPool::workers_started()); |
| 65 EXPECT_EQ(0U, thread_pool.workers_stopped()); | 47 EXPECT_EQ(0U, ThreadPool::workers_stopped()); |
| 48 EXPECT(ThreadPool::Shutdown()); |
| 66 } | 49 } |
| 67 | 50 |
| 68 | 51 |
| 69 UNIT_TEST_CASE(ThreadPool_RunMany) { | 52 UNIT_TEST_CASE(ThreadPool_RunMany) { |
| 53 ThreadPool::set_shutdown_timeout_fatal(false); |
| 70 const int kTaskCount = 100; | 54 const int kTaskCount = 100; |
| 71 ThreadPool thread_pool; | |
| 72 Monitor sync[kTaskCount]; | 55 Monitor sync[kTaskCount]; |
| 73 bool done[kTaskCount]; | 56 bool done[kTaskCount]; |
| 74 | 57 |
| 75 for (int i = 0; i < kTaskCount; i++) { | 58 for (int i = 0; i < kTaskCount; i++) { |
| 76 done[i] = false; | 59 done[i] = false; |
| 77 thread_pool.Run(new TestTask(&sync[i], &done[i])); | 60 ThreadPool::Run(new TestTask(&sync[i], &done[i])); |
| 78 } | 61 } |
| 79 for (int i = 0; i < kTaskCount; i++) { | 62 for (int i = 0; i < kTaskCount; i++) { |
| 80 MonitorLocker ml(&sync[i]); | 63 MonitorLocker ml(&sync[i]); |
| 81 while (!done[i]) { | 64 while (!done[i]) { |
| 82 ml.Wait(); | 65 ml.Wait(); |
| 83 } | 66 } |
| 84 EXPECT(done[i]); | 67 EXPECT(done[i]); |
| 85 } | 68 } |
| 69 EXPECT(ThreadPool::Shutdown()); |
| 86 } | 70 } |
| 87 | 71 |
| 88 | 72 |
| 89 class SleepTask : public ThreadPool::Task { | |
| 90 public: | |
| 91 explicit SleepTask(int millis) | |
| 92 : millis_(millis) { | |
| 93 } | |
| 94 | |
| 95 virtual void Run() { | |
| 96 OS::Sleep(millis_); | |
| 97 } | |
| 98 | |
| 99 private: | |
| 100 int millis_; | |
| 101 }; | |
| 102 | |
| 103 | |
| 104 UNIT_TEST_CASE(ThreadPool_WorkerShutdown) { | |
| 105 Monitor exit_sync; | |
| 106 int exit_count = 0; | |
| 107 MonitorLocker ml(&exit_sync); | |
| 108 | |
| 109 // Set up the ThreadPool so that workers notify before they exit. | |
| 110 ThreadPool* thread_pool = new ThreadPool(); | |
| 111 ThreadPoolTestPeer::SetExitMonitor(&exit_sync, &exit_count); | |
| 112 | |
| 113 // Run a single task. | |
| 114 thread_pool->Run(new SleepTask(2)); | |
| 115 | |
| 116 // Kill the thread pool. | |
| 117 delete thread_pool; | |
| 118 thread_pool = NULL; | |
| 119 | |
| 120 // Wait for the workers to terminate. | |
| 121 while (exit_count == 0) { | |
| 122 ml.Wait(); | |
| 123 } | |
| 124 EXPECT_EQ(1, exit_count); | |
| 125 } | |
| 126 | |
| 127 | |
| 128 UNIT_TEST_CASE(ThreadPool_WorkerTimeout) { | 73 UNIT_TEST_CASE(ThreadPool_WorkerTimeout) { |
| 129 // Adjust the worker timeout so that we timeout quickly. | 74 // Adjust the worker timeout so that we timeout quickly. |
| 130 int saved_timeout = FLAG_worker_timeout_millis; | 75 int saved_timeout = FLAG_worker_timeout_millis; |
| 131 FLAG_worker_timeout_millis = 1; | 76 FLAG_worker_timeout_millis = 1; |
| 132 | 77 |
| 133 ThreadPool thread_pool; | 78 ThreadPool::set_shutdown_timeout_fatal(false); |
| 134 EXPECT_EQ(0U, thread_pool.workers_started()); | 79 EXPECT_LE(0U, ThreadPool::workers_started()); |
| 135 EXPECT_EQ(0U, thread_pool.workers_stopped()); | 80 EXPECT_EQ(0U, ThreadPool::workers_stopped()); |
| 136 | 81 |
| 137 // Run a worker. | 82 // Run a worker. |
| 138 Monitor sync; | 83 Monitor sync; |
| 139 bool done = false; | 84 bool done = false; |
| 140 thread_pool.Run(new TestTask(&sync, &done)); | 85 ThreadPool::Run(new TestTask(&sync, &done)); |
| 141 EXPECT_EQ(1U, thread_pool.workers_started()); | 86 EXPECT_LE(1U, ThreadPool::workers_started()); |
| 142 EXPECT_EQ(0U, thread_pool.workers_stopped()); | 87 EXPECT_EQ(0U, ThreadPool::workers_stopped()); |
| 143 { | 88 { |
| 144 MonitorLocker ml(&sync); | 89 MonitorLocker ml(&sync); |
| 145 while (!done) { | 90 while (!done) { |
| 146 ml.Wait(); | 91 ml.Wait(); |
| 147 } | 92 } |
| 148 } | 93 } |
| 149 EXPECT(done); | 94 EXPECT(done); |
| 150 | 95 |
| 151 // Wait up to 5 seconds to see if a worker times out. | 96 // Wait up to 5 seconds to see if a worker times out. |
| 152 const int kMaxWait = 5000; | 97 const int kMaxWait = 5000; |
| 153 int waited = 0; | 98 int waited = 0; |
| 154 while (thread_pool.workers_stopped() == 0 && waited < kMaxWait) { | 99 while (ThreadPool::workers_stopped() == 0 && waited < kMaxWait) { |
| 155 OS::Sleep(1); | 100 OS::Sleep(1); |
| 156 waited += 1; | 101 waited += 1; |
| 157 } | 102 } |
| 158 EXPECT_EQ(1U, thread_pool.workers_stopped()); | 103 EXPECT_LE(1U, ThreadPool::workers_stopped()); |
| 159 FLAG_worker_timeout_millis = saved_timeout; | 104 FLAG_worker_timeout_millis = saved_timeout; |
| 105 |
| 106 EXPECT(ThreadPool::Shutdown()); |
| 160 } | 107 } |
| 161 | 108 |
| 162 | 109 |
| 163 class SpawnTask : public ThreadPool::Task { | 110 class SpawnTask : public ThreadPool::Task { |
| 164 public: | 111 public: |
| 165 SpawnTask(ThreadPool* pool, Monitor* sync, int todo, int total, int* done) | 112 SpawnTask(Monitor* sync, int todo, int total, int* done) |
| 166 : pool_(pool), sync_(sync), todo_(todo), total_(total), done_(done) { | 113 : sync_(sync), todo_(todo), total_(total), done_(done) { |
| 167 } | 114 } |
| 168 | 115 |
| 169 virtual void Run() { | 116 virtual void Run() { |
| 170 todo_--; // Subtract one for current task. | 117 todo_--; // Subtract one for current task. |
| 171 int child_todo = todo_ / 2; | 118 int child_todo = todo_ / 2; |
| 172 | 119 |
| 173 // Spawn 0-2 children. | 120 // Spawn 0-2 children. |
| 174 if (todo_ > 0) { | 121 if (todo_ > 0) { |
| 175 pool_->Run( | 122 ThreadPool::Run( |
| 176 new SpawnTask(pool_, sync_, todo_ - child_todo, total_, done_)); | 123 new SpawnTask(sync_, todo_ - child_todo, total_, done_)); |
| 177 } | 124 } |
| 178 if (todo_ > 1) { | 125 if (todo_ > 1) { |
| 179 pool_->Run( | 126 ThreadPool::Run( |
| 180 new SpawnTask(pool_, sync_, child_todo, total_, done_)); | 127 new SpawnTask(sync_, child_todo, total_, done_)); |
| 181 } | 128 } |
| 182 | 129 |
| 183 { | 130 { |
| 184 MonitorLocker ml(sync_); | 131 MonitorLocker ml(sync_); |
| 185 (*done_)++; | 132 (*done_)++; |
| 186 if (*done_ >= total_) { | 133 if (*done_ >= total_) { |
| 187 ml.Notify(); | 134 ml.Notify(); |
| 188 } | 135 } |
| 189 } | 136 } |
| 190 } | 137 } |
| 191 | 138 |
| 192 private: | 139 private: |
| 193 ThreadPool* pool_; | |
| 194 Monitor* sync_; | 140 Monitor* sync_; |
| 195 int todo_; | 141 int todo_; |
| 196 int total_; | 142 int total_; |
| 197 int* done_; | 143 int* done_; |
| 198 }; | 144 }; |
| 199 | 145 |
| 200 | 146 |
| 201 UNIT_TEST_CASE(ThreadPool_RecursiveSpawn) { | 147 UNIT_TEST_CASE(ThreadPool_RecursiveSpawn) { |
| 202 ThreadPool thread_pool; | 148 ThreadPool::set_shutdown_timeout_fatal(false); |
| 203 Monitor sync; | 149 Monitor sync; |
| 204 const int kTotalTasks = 500; | 150 const int kTotalTasks = 500; |
| 205 int done = 0; | 151 int done = 0; |
| 206 thread_pool.Run( | 152 ThreadPool::Run( |
| 207 new SpawnTask(&thread_pool, &sync, kTotalTasks, kTotalTasks, &done)); | 153 new SpawnTask(&sync, kTotalTasks, kTotalTasks, &done)); |
| 208 { | 154 { |
| 209 MonitorLocker ml(&sync); | 155 MonitorLocker ml(&sync); |
| 210 while (done < kTotalTasks) { | 156 while (done < kTotalTasks) { |
| 211 ml.Wait(); | 157 ml.Wait(); |
| 212 } | 158 } |
| 213 } | 159 } |
| 214 EXPECT_EQ(kTotalTasks, done); | 160 EXPECT_EQ(kTotalTasks, done); |
| 161 EXPECT(ThreadPool::Shutdown()); |
| 215 } | 162 } |
| 216 | 163 |
| 217 | 164 |
| 165 class SleepTask : public ThreadPool::Task { |
| 166 public: |
| 167 SleepTask(Monitor* sync, int millis) |
| 168 : sync_(sync), millis_(millis) { |
| 169 } |
| 170 |
| 171 virtual void Run() { |
| 172 { |
| 173 MonitorLocker ml(sync_); |
| 174 ml.Notify(); |
| 175 } |
| 176 OS::Sleep(millis_); |
| 177 } |
| 178 |
| 179 private: |
| 180 Monitor* sync_; |
| 181 int millis_; |
| 182 }; |
| 183 |
| 184 |
| 185 UNIT_TEST_CASE(ThreadPool_ShutdownTimeout) { |
| 186 ThreadPool::set_shutdown_timeout_fatal(false); |
| 187 Monitor sync; |
| 188 ThreadPool::Run(new SleepTask(&sync, 15000)); |
| 189 { |
| 190 MonitorLocker ml(&sync); |
| 191 ml.Wait(); |
| 192 } |
| 193 EXPECT(!ThreadPool::Shutdown()); |
| 194 } |
| 195 |
| 196 |
| 218 } // namespace dart | 197 } // namespace dart |
| OLD | NEW |