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 |