Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
thread_pool_test.cc
Go to the documentation of this 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
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/thread_pool.h"
6#include "vm/lockers.h"
7#include "vm/os.h"
8#include "vm/unit_test.h"
9
10namespace dart {
11
12DECLARE_FLAG(int, worker_timeout_millis);
13
14// Some of these tests change VM flags, so they should run without a full VM
15// startup to prevent races on the flag changes. None of the tests require full
16// VM startup, so we do this for all of them.
17#define THREAD_POOL_UNIT_TEST_CASE(name) \
18 static void name##helper(); \
19 UNIT_TEST_CASE(name) { \
20 OSThread::Init(); \
21 name##helper(); \
22 /* Delete the current thread's TLS and set it's TLS to null. */ \
23 /* If it is the last thread then the destructor would call */ \
24 /* OSThread::Cleanup. */ \
25 OSThread* os_thread = OSThread::Current(); \
26 OSThread::SetCurrent(nullptr); \
27 delete os_thread; \
28 } \
29 void name##helper()
30
31THREAD_POOL_UNIT_TEST_CASE(ThreadPool_Create) {
32 ThreadPool thread_pool;
33}
34
35class TestTask : public ThreadPool::Task {
36 public:
37 TestTask(Monitor* sync, bool* done) : sync_(sync), done_(done) {}
38
39 // Before running the task, *done_ should be true. This lets the caller
40 // ASSERT things knowing that the thread is still around. To unblock the
41 // thread, the caller should take the lock, set *done_ to false, and Notify()
42 // the monitor.
43 virtual void Run() {
44 {
45 MonitorLocker ml(sync_);
46 while (*done_) {
47 ml.Wait();
48 }
49 }
50 MonitorLocker ml(sync_);
51 *done_ = true;
52 ml.Notify();
53 }
54
55 private:
56 Monitor* sync_;
57 bool* done_;
58};
59
60THREAD_POOL_UNIT_TEST_CASE(ThreadPool_RunOne) {
61 ThreadPool thread_pool;
62 Monitor sync;
63 bool done = true;
64 thread_pool.Run<TestTask>(&sync, &done);
65 {
66 MonitorLocker ml(&sync);
67 done = false;
68 ml.Notify();
69 while (!done) {
70 ml.Wait();
71 }
72 }
73 EXPECT(done);
74
75 // Do a sanity test on the worker stats.
76 EXPECT_EQ(1U, thread_pool.workers_started());
77 EXPECT_EQ(0U, thread_pool.workers_stopped());
78}
79
80THREAD_POOL_UNIT_TEST_CASE(ThreadPool_RunMany) {
81 const int kTaskCount = 100;
82 ThreadPool thread_pool;
83 Monitor sync[kTaskCount];
84 bool done[kTaskCount];
85
86 for (int i = 0; i < kTaskCount; i++) {
87 done[i] = true;
88 thread_pool.Run<TestTask>(&sync[i], &done[i]);
89 }
90 for (int i = 0; i < kTaskCount; i++) {
91 MonitorLocker ml(&sync[i]);
92 done[i] = false;
93 ml.Notify();
94 while (!done[i]) {
95 ml.Wait();
96 }
97 EXPECT(done[i]);
98 }
99}
100
102 public:
103 SleepTask(Monitor* sync, int* started_count, int* slept_count, int millis)
104 : sync_(sync),
105 started_count_(started_count),
106 slept_count_(slept_count),
107 millis_(millis) {}
108
109 virtual void Run() {
110 {
111 MonitorLocker ml(sync_);
112 *started_count_ = *started_count_ + 1;
113 ml.Notify();
114 }
115 // Sleep so we can be sure the ThreadPool destructor blocks until we're
116 // done.
117 OS::Sleep(millis_);
118 {
119 MonitorLocker ml(sync_);
120 *slept_count_ = *slept_count_ + 1;
121 // No notification here. The main thread is blocked in ThreadPool
122 // shutdown waiting for this thread to finish.
123 }
124 }
125
126 private:
127 Monitor* sync_;
128 int* started_count_;
129 int* slept_count_;
130 int millis_;
131};
132
133THREAD_POOL_UNIT_TEST_CASE(ThreadPool_WorkerShutdown) {
134 const int kTaskCount = 10;
135 Monitor sync;
136 int slept_count = 0;
137 int started_count = 0;
138
139 // Set up the ThreadPool so that workers notify before they exit.
140 ThreadPool* thread_pool = new ThreadPool();
141
142 // Run a single task.
143 for (int i = 0; i < kTaskCount; i++) {
144 thread_pool->Run<SleepTask>(&sync, &started_count, &slept_count, 2);
145 }
146
147 {
148 // Wait for everybody to start.
149 MonitorLocker ml(&sync);
150 while (started_count < kTaskCount) {
151 ml.Wait();
152 }
153 }
154
155 // Kill the thread pool while the workers are sleeping.
156 delete thread_pool;
157 thread_pool = nullptr;
158
159 int final_count = 0;
160 {
161 MonitorLocker ml(&sync);
162 final_count = slept_count;
163 }
164
165 // We should have waited for all the workers to finish, so they all should
166 // have had a chance to increment slept_count.
167 EXPECT_EQ(kTaskCount, final_count);
168}
169
170THREAD_POOL_UNIT_TEST_CASE(ThreadPool_WorkerTimeout) {
171 // Adjust the worker timeout so that we timeout quickly.
172 int saved_timeout = FLAG_worker_timeout_millis;
173 FLAG_worker_timeout_millis = 1;
174
175 {
176 ThreadPool thread_pool;
177 EXPECT_EQ(0U, thread_pool.workers_started());
178 EXPECT_EQ(0U, thread_pool.workers_stopped());
179
180 // Run a worker.
181 Monitor sync;
182 bool done = true;
183 thread_pool.Run<TestTask>(&sync, &done);
184 EXPECT_EQ(1U, thread_pool.workers_started());
185 EXPECT_EQ(0U, thread_pool.workers_stopped());
186 {
187 MonitorLocker ml(&sync);
188 done = false;
189 ml.Notify();
190 while (!done) {
191 ml.Wait();
192 }
193 }
194 EXPECT(done);
195
196 // Wait up to 5 seconds to see if a worker times out.
197 const int kMaxWait = 5000;
198 int waited = 0;
199 while (thread_pool.workers_stopped() == 0 && waited < kMaxWait) {
200 OS::Sleep(1);
201 waited += 1;
202 }
203 EXPECT_EQ(1U, thread_pool.workers_stopped());
204 }
205
206 FLAG_worker_timeout_millis = saved_timeout;
207}
208
210 public:
211 SpawnTask(ThreadPool* pool, Monitor* sync, int todo, int total, int* done)
212 : pool_(pool), sync_(sync), todo_(todo), total_(total), done_(done) {}
213
214 virtual void Run() {
215 todo_--; // Subtract one for current task.
216 int child_todo = todo_ / 2;
217
218 // Spawn 0-2 children.
219 if (todo_ > 0) {
220 pool_->Run<SpawnTask>(pool_, sync_, todo_ - child_todo, total_, done_);
221 }
222 if (todo_ > 1) {
223 pool_->Run<SpawnTask>(pool_, sync_, child_todo, total_, done_);
224 }
225
226 {
227 MonitorLocker ml(sync_);
228 (*done_)++;
229 if (*done_ >= total_) {
230 ml.Notify();
231 }
232 }
233 }
234
235 private:
236 ThreadPool* pool_;
237 Monitor* sync_;
238 int todo_;
239 int total_;
240 int* done_;
241};
242
243THREAD_POOL_UNIT_TEST_CASE(ThreadPool_RecursiveSpawn) {
244 ThreadPool thread_pool;
245 Monitor sync;
246 const int kTotalTasks = 500;
247 int done = 0;
248 thread_pool.Run<SpawnTask>(&thread_pool, &sync, kTotalTasks, kTotalTasks,
249 &done);
250 {
251 MonitorLocker ml(&sync);
252 while (done < kTotalTasks) {
253 ml.Wait();
254 }
255 }
256 EXPECT_EQ(kTotalTasks, done);
257}
258
259} // namespace dart
AutoreleasePool pool
static void done(const char *config, const char *src, const char *srcOptions, const char *name)
Definition DM.cpp:263
#define EXPECT(type, expectedAlignment, expectedSize)
Monitor::WaitResult Wait(int64_t millis=Monitor::kNoTimeout)
Definition lockers.h:172
static void Sleep(int64_t millis)
SleepTask(Monitor *sync, int *started_count, int *slept_count, int millis)
virtual void Run()
SpawnTask(ThreadPool *pool, Monitor *sync, int todo, int total, int *done)
virtual void Run()
TestTask(Monitor *sync, bool *done)
virtual void Run()
uint64_t workers_started() const
Definition thread_pool.h:65
uint64_t workers_stopped() const
Definition thread_pool.h:67
bool Run(Args &&... args)
Definition thread_pool.h:45
#define DECLARE_FLAG(type, name)
Definition flags.h:14
#define THREAD_POOL_UNIT_TEST_CASE(name)