1 | // |
2 | // ThreadTest.cpp |
3 | // |
4 | // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. |
5 | // and Contributors. |
6 | // |
7 | // SPDX-License-Identifier: BSL-1.0 |
8 | // |
9 | |
10 | |
11 | #include "ThreadTest.h" |
12 | #include "Poco/CppUnit/TestCaller.h" |
13 | #include "Poco/CppUnit/TestSuite.h" |
14 | #include "Poco/Thread.h" |
15 | #include "Poco/Runnable.h" |
16 | #include "Poco/ThreadTarget.h" |
17 | #include "Poco/Event.h" |
18 | #include "Poco/Timestamp.h" |
19 | #include "Poco/Timespan.h" |
20 | #include "Poco/Environment.h" |
21 | #if defined(__sun) && defined(__SVR4) && !defined(__EXTENSIONS__) |
22 | #define __EXTENSIONS__ |
23 | #endif |
24 | #include <climits> |
25 | #include <vector> |
26 | #include <sstream> |
27 | |
28 | using Poco::Thread; |
29 | using Poco::Runnable; |
30 | using Poco::ThreadTarget; |
31 | using Poco::Event; |
32 | |
33 | |
34 | class MyRunnable: public Runnable |
35 | { |
36 | public: |
37 | MyRunnable(): _ran(false) |
38 | { |
39 | } |
40 | |
41 | void run() |
42 | { |
43 | Thread* pThread = Thread::current(); |
44 | if (pThread) |
45 | _threadName = pThread->name(); |
46 | _ran = true; |
47 | _event.wait(); |
48 | } |
49 | |
50 | bool ran() const |
51 | { |
52 | return _ran; |
53 | } |
54 | |
55 | const std::string& threadName() const |
56 | { |
57 | return _threadName; |
58 | } |
59 | |
60 | void notify() |
61 | { |
62 | _event.set(); |
63 | } |
64 | |
65 | static void staticFunc() |
66 | { |
67 | ++_staticVar; |
68 | } |
69 | |
70 | static int _staticVar; |
71 | |
72 | private: |
73 | bool _ran; |
74 | std::string _threadName; |
75 | Event _event; |
76 | }; |
77 | |
78 | |
79 | int MyRunnable::_staticVar = 0; |
80 | |
81 | |
82 | void freeFunc() |
83 | { |
84 | ++MyRunnable::_staticVar; |
85 | } |
86 | |
87 | |
88 | void freeFunc(void* pData) |
89 | { |
90 | MyRunnable::_staticVar += *reinterpret_cast<int*>(pData); |
91 | } |
92 | |
93 | |
94 | class NonJoinRunnable : public Runnable |
95 | { |
96 | public: |
97 | NonJoinRunnable() : _finished(false) |
98 | { |
99 | } |
100 | |
101 | void run() |
102 | { |
103 | _finished = true; |
104 | } |
105 | |
106 | bool finished() const |
107 | { |
108 | return _finished; |
109 | } |
110 | |
111 | private: |
112 | bool _finished; |
113 | }; |
114 | |
115 | |
116 | class TrySleepRunnable : public Runnable |
117 | { |
118 | public: |
119 | TrySleepRunnable() : _counter(0), _sleepy(true) |
120 | { |
121 | } |
122 | |
123 | void run() |
124 | { |
125 | _sleepy = !Thread::trySleep(300000); |
126 | ++_counter; |
127 | _sleepy = !Thread::trySleep(300000); |
128 | ++_counter; |
129 | _sleepy = !Thread::trySleep(100); |
130 | ++_counter; |
131 | } |
132 | |
133 | int counter() const |
134 | { |
135 | return _counter; |
136 | } |
137 | |
138 | bool isSleepy() const |
139 | { |
140 | return _sleepy; |
141 | } |
142 | |
143 | private: |
144 | int _counter; |
145 | bool _sleepy; |
146 | }; |
147 | |
148 | |
149 | ThreadTest::ThreadTest(const std::string& rName): CppUnit::TestCase(rName) |
150 | { |
151 | } |
152 | |
153 | |
154 | ThreadTest::~ThreadTest() |
155 | { |
156 | } |
157 | |
158 | |
159 | void ThreadTest::testThread() |
160 | { |
161 | Thread thread; |
162 | MyRunnable r; |
163 | assertTrue (!thread.isRunning()); |
164 | thread.start(r); |
165 | Thread::sleep(200); |
166 | assertTrue (thread.isRunning()); |
167 | r.notify(); |
168 | thread.join(); |
169 | assertTrue (!thread.isRunning()); |
170 | assertTrue (r.ran()); |
171 | assertTrue (!r.threadName().empty()); |
172 | } |
173 | |
174 | |
175 | void ThreadTest::testNamedThread() |
176 | { |
177 | Thread thread("MyThread" ); |
178 | MyRunnable r; |
179 | thread.start(r); |
180 | r.notify(); |
181 | thread.join(); |
182 | assertTrue (r.ran()); |
183 | assertTrue (r.threadName() == "MyThread" ); |
184 | } |
185 | |
186 | |
187 | void ThreadTest::testCurrent() |
188 | { |
189 | assertNullPtr (Thread::current()); |
190 | } |
191 | |
192 | |
193 | void ThreadTest::testThreads() |
194 | { |
195 | Thread thread1("Thread1" ); |
196 | Thread thread2("Thread2" ); |
197 | Thread thread3("Thread3" ); |
198 | Thread thread4("Thread4" ); |
199 | |
200 | MyRunnable r1; |
201 | MyRunnable r2; |
202 | MyRunnable r3; |
203 | MyRunnable r4; |
204 | assertTrue (!thread1.isRunning()); |
205 | assertTrue (!thread2.isRunning()); |
206 | assertTrue (!thread3.isRunning()); |
207 | assertTrue (!thread4.isRunning()); |
208 | thread1.start(r1); |
209 | Thread::sleep(200); |
210 | assertTrue (thread1.isRunning()); |
211 | assertTrue (!thread2.isRunning()); |
212 | assertTrue (!thread3.isRunning()); |
213 | assertTrue (!thread4.isRunning()); |
214 | thread2.start(r2); |
215 | thread3.start(r3); |
216 | thread4.start(r4); |
217 | Thread::sleep(200); |
218 | assertTrue (thread1.isRunning()); |
219 | assertTrue (thread2.isRunning()); |
220 | assertTrue (thread3.isRunning()); |
221 | assertTrue (thread4.isRunning()); |
222 | r4.notify(); |
223 | thread4.join(); |
224 | assertTrue (!thread4.isRunning()); |
225 | assertTrue (thread1.isRunning()); |
226 | assertTrue (thread2.isRunning()); |
227 | assertTrue (thread3.isRunning()); |
228 | r3.notify(); |
229 | thread3.join(); |
230 | assertTrue (!thread3.isRunning()); |
231 | r2.notify(); |
232 | thread2.join(); |
233 | assertTrue (!thread2.isRunning()); |
234 | r1.notify(); |
235 | thread1.join(); |
236 | assertTrue (!thread1.isRunning()); |
237 | assertTrue (r1.ran()); |
238 | assertTrue (r1.threadName() == "Thread1" ); |
239 | assertTrue (r2.ran()); |
240 | assertTrue (r2.threadName() == "Thread2" ); |
241 | assertTrue (r3.ran()); |
242 | assertTrue (r3.threadName() == "Thread3" ); |
243 | assertTrue (r4.ran()); |
244 | assertTrue (r4.threadName() == "Thread4" ); |
245 | } |
246 | |
247 | |
248 | void ThreadTest::testJoin() |
249 | { |
250 | Thread thread; |
251 | MyRunnable r; |
252 | assertTrue (!thread.isRunning()); |
253 | thread.start(r); |
254 | Thread::sleep(200); |
255 | assertTrue (thread.isRunning()); |
256 | assertTrue (!thread.tryJoin(100)); |
257 | r.notify(); |
258 | assertTrue (thread.tryJoin(500)); |
259 | assertTrue (!thread.isRunning()); |
260 | } |
261 | |
262 | |
263 | void ThreadTest::testNotJoin() |
264 | { |
265 | Thread thread; |
266 | NonJoinRunnable r; |
267 | thread.start(r); |
268 | |
269 | while (!r.finished()) |
270 | { |
271 | Thread::sleep(10); |
272 | } |
273 | |
274 | Thread::sleep(100); |
275 | assertTrue (!thread.isRunning()); |
276 | } |
277 | |
278 | |
279 | void ThreadTest::testTrySleep() |
280 | { |
281 | Thread thread; |
282 | TrySleepRunnable r; |
283 | assertTrue (r.isSleepy()); |
284 | assertTrue (!thread.isRunning()); |
285 | assertTrue (r.counter() == 0); |
286 | thread.start(r); |
287 | assertTrue (thread.isRunning()); |
288 | assertTrue (r.counter() == 0); |
289 | assertTrue (r.isSleepy()); |
290 | Thread::sleep(100); |
291 | assertTrue (r.counter() == 0); |
292 | assertTrue (r.isSleepy()); |
293 | thread.wakeUp(); |
294 | Thread::sleep(10); |
295 | assertTrue (r.counter() == 1); |
296 | assertTrue (r.isSleepy()); |
297 | Thread::sleep(100); |
298 | assertTrue (r.counter() == 1); |
299 | thread.wakeUp(); |
300 | Thread::sleep(10); |
301 | assertTrue (r.counter() == 2); |
302 | assertTrue (r.isSleepy()); |
303 | Thread::sleep(200); |
304 | assertTrue (r.counter() == 3); |
305 | assertTrue (!r.isSleepy()); |
306 | assertTrue (!thread.isRunning()); |
307 | thread.wakeUp(); |
308 | assertTrue (!thread.isRunning()); |
309 | } |
310 | |
311 | |
312 | void ThreadTest::testNotRun() |
313 | { |
314 | Thread thread; |
315 | } |
316 | |
317 | |
318 | void ThreadTest::testNotRunJoin() |
319 | { |
320 | Thread thread; |
321 | thread.join(); |
322 | } |
323 | |
324 | |
325 | void ThreadTest::testThreadTarget() |
326 | { |
327 | ThreadTarget te(&MyRunnable::staticFunc); |
328 | Thread thread; |
329 | |
330 | assertTrue (!thread.isRunning()); |
331 | |
332 | int tmp = MyRunnable::_staticVar; |
333 | thread.start(te); |
334 | thread.join(); |
335 | assertTrue (tmp + 1 == MyRunnable::_staticVar); |
336 | |
337 | ThreadTarget te1(freeFunc); |
338 | assertTrue (!thread.isRunning()); |
339 | |
340 | tmp = MyRunnable::_staticVar; |
341 | thread.start(te1); |
342 | thread.join(); |
343 | assertTrue (tmp + 1 == MyRunnable::_staticVar); |
344 | } |
345 | |
346 | |
347 | void ThreadTest::testThreadFunction() |
348 | { |
349 | Thread thread; |
350 | |
351 | assertTrue (!thread.isRunning()); |
352 | |
353 | int tmp = MyRunnable::_staticVar; |
354 | thread.start(freeFunc, &tmp); |
355 | thread.join(); |
356 | assertTrue (tmp * 2 == MyRunnable::_staticVar); |
357 | |
358 | assertTrue (!thread.isRunning()); |
359 | |
360 | tmp = MyRunnable::_staticVar = 0; |
361 | thread.start(freeFunc, &tmp); |
362 | thread.join(); |
363 | assertTrue (0 == MyRunnable::_staticVar); |
364 | } |
365 | |
366 | |
367 | struct Functor |
368 | { |
369 | void operator () () |
370 | { |
371 | ++MyRunnable::_staticVar; |
372 | } |
373 | }; |
374 | |
375 | |
376 | void ThreadTest::testThreadFunctor() |
377 | { |
378 | Thread thread; |
379 | |
380 | assertTrue (!thread.isRunning()); |
381 | |
382 | MyRunnable::_staticVar = 0; |
383 | thread.startFunc(Functor()); |
384 | thread.join(); |
385 | assertTrue (1 == MyRunnable::_staticVar); |
386 | |
387 | assertTrue (!thread.isRunning()); |
388 | |
389 | |
390 | Thread thread2; |
391 | |
392 | assertTrue (!thread2.isRunning()); |
393 | |
394 | MyRunnable::_staticVar = 0; |
395 | thread.startFunc([] () |
396 | { |
397 | MyRunnable::_staticVar++; |
398 | }); |
399 | thread.join(); |
400 | assertTrue (1 == MyRunnable::_staticVar); |
401 | |
402 | assertTrue (!thread2.isRunning()); |
403 | |
404 | } |
405 | |
406 | |
407 | void ThreadTest::testThreadStackSize() |
408 | { |
409 | int stackSize = 50000000; |
410 | |
411 | Thread thread; |
412 | |
413 | assertTrue (0 == thread.getStackSize()); |
414 | thread.setStackSize(stackSize); |
415 | assertTrue (stackSize <= thread.getStackSize()); |
416 | int tmp = MyRunnable::_staticVar; |
417 | thread.start(freeFunc, &tmp); |
418 | thread.join(); |
419 | assertTrue (tmp * 2 == MyRunnable::_staticVar); |
420 | |
421 | stackSize = 1; |
422 | thread.setStackSize(stackSize); |
423 | |
424 | #if !defined(POCO_OS_FAMILY_BSD) // on BSD family, stack size is rounded |
425 | assertTrue (stackSize >= thread.getStackSize()); |
426 | #endif |
427 | |
428 | tmp = MyRunnable::_staticVar; |
429 | thread.start(freeFunc, &tmp); |
430 | thread.join(); |
431 | assertTrue (tmp * 2 == MyRunnable::_staticVar); |
432 | |
433 | thread.setStackSize(0); |
434 | assertTrue (0 == thread.getStackSize()); |
435 | tmp = MyRunnable::_staticVar; |
436 | thread.start(freeFunc, &tmp); |
437 | thread.join(); |
438 | assertTrue (tmp * 2 == MyRunnable::_staticVar); |
439 | } |
440 | |
441 | |
442 | void ThreadTest::testSleep() |
443 | { |
444 | Poco::Timestamp start; |
445 | Thread::sleep(200); |
446 | Poco::Timespan elapsed = start.elapsed(); |
447 | assertTrue (elapsed.totalMilliseconds() >= 190 && elapsed.totalMilliseconds() < 250); |
448 | } |
449 | |
450 | void ThreadTest::testAffinity() |
451 | { |
452 | std::stringstream ss; |
453 | unsigned cpuCount = Poco::Environment::processorCount(); |
454 | unsigned usedCpu = 0; |
455 | std::vector<Thread*> threadList; |
456 | Thread* thread = NULL; |
457 | std::vector<MyRunnable*> runnableList; |
458 | MyRunnable* runbl = NULL; |
459 | |
460 | for (unsigned i = 0; i < cpuCount; i++) |
461 | { |
462 | ss.str("" ); |
463 | ss << "Thread" << i; |
464 | thread = new Thread(ss.str()); |
465 | threadList.push_back(thread); |
466 | runbl = new MyRunnable(); |
467 | runnableList.push_back(runbl); |
468 | } |
469 | |
470 | for (int i = 0; i < cpuCount; i++) |
471 | { |
472 | assertTrue (!threadList[i]->isRunning()); |
473 | } |
474 | |
475 | for (int i = 0; i < cpuCount; i++) |
476 | { |
477 | threadList[i]->start(*runnableList[i]); |
478 | threadList[i]->setAffinity(i); |
479 | Thread::sleep(100); |
480 | usedCpu = threadList[i]->getAffinity(); |
481 | assertTrue (usedCpu == i || usedCpu == -1); |
482 | } |
483 | |
484 | for (int i = 0; i < cpuCount; i++) |
485 | { |
486 | runnableList[i]->notify(); |
487 | threadList[i]->join(); |
488 | delete runnableList[i]; |
489 | delete threadList[i]; |
490 | } |
491 | } |
492 | |
493 | |
494 | void ThreadTest::testJoinNotStarted() |
495 | { |
496 | Thread thread; |
497 | thread.join(); |
498 | } |
499 | |
500 | |
501 | void ThreadTest::setUp() |
502 | { |
503 | } |
504 | |
505 | |
506 | void ThreadTest::tearDown() |
507 | { |
508 | } |
509 | |
510 | |
511 | CppUnit::Test* ThreadTest::suite() |
512 | { |
513 | CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ThreadTest" ); |
514 | |
515 | CppUnit_addTest(pSuite, ThreadTest, testThread); |
516 | CppUnit_addTest(pSuite, ThreadTest, testNamedThread); |
517 | CppUnit_addTest(pSuite, ThreadTest, testCurrent); |
518 | CppUnit_addTest(pSuite, ThreadTest, testThreads); |
519 | CppUnit_addTest(pSuite, ThreadTest, testJoin); |
520 | CppUnit_addTest(pSuite, ThreadTest, testNotJoin); |
521 | CppUnit_addTest(pSuite, ThreadTest, testNotRun); |
522 | CppUnit_addTest(pSuite, ThreadTest, testNotRunJoin); |
523 | CppUnit_addTest(pSuite, ThreadTest, testTrySleep); |
524 | CppUnit_addTest(pSuite, ThreadTest, testThreadTarget); |
525 | CppUnit_addTest(pSuite, ThreadTest, testThreadFunction); |
526 | CppUnit_addTest(pSuite, ThreadTest, testThreadFunctor); |
527 | CppUnit_addTest(pSuite, ThreadTest, testThreadStackSize); |
528 | CppUnit_addTest(pSuite, ThreadTest, testSleep); |
529 | CppUnit_addTest(pSuite, ThreadTest, testAffinity); |
530 | CppUnit_addTest(pSuite, ThreadTest, testJoinNotStarted); |
531 | |
532 | return pSuite; |
533 | } |
534 | |