|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the test suite of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 #include <QtTest/QtTest> |
|
42 #include <qdatetime.h> |
|
43 #include <qthreadpool.h> |
|
44 #include <qstring.h> |
|
45 #include <qmutex.h> |
|
46 |
|
47 typedef void (*FunctionPointer)(); |
|
48 |
|
49 class FunctionPointerTask : public QRunnable |
|
50 { |
|
51 public: |
|
52 FunctionPointerTask(FunctionPointer function) |
|
53 :function(function) {} |
|
54 void run() { function(); } |
|
55 private: |
|
56 FunctionPointer function; |
|
57 }; |
|
58 |
|
59 QRunnable *createTask(FunctionPointer pointer) |
|
60 { |
|
61 return new FunctionPointerTask(pointer); |
|
62 } |
|
63 |
|
64 class tst_QThreadPool : public QObject |
|
65 { |
|
66 Q_OBJECT |
|
67 private slots: |
|
68 void runFunction(); |
|
69 void createThreadRunFunction(); |
|
70 void runMultiple(); |
|
71 void waitcomplete(); |
|
72 void runTask(); |
|
73 void singleton(); |
|
74 void destruction(); |
|
75 void threadRecycling(); |
|
76 void expiryTimeout(); |
|
77 void exceptions(); |
|
78 void maxThreadCount(); |
|
79 void setMaxThreadCount_data(); |
|
80 void setMaxThreadCount(); |
|
81 void setMaxThreadCountStartsAndStopsThreads(); |
|
82 void activeThreadCount(); |
|
83 void reserveThread_data(); |
|
84 void reserveThread(); |
|
85 void releaseThread_data(); |
|
86 void releaseThread(); |
|
87 void start(); |
|
88 void tryStart(); |
|
89 void tryStartPeakThreadCount(); |
|
90 void tryStartCount(); |
|
91 void waitForDone(); |
|
92 void destroyingWaitsForTasksToFinish(); |
|
93 void stressTest(); |
|
94 }; |
|
95 |
|
96 int testFunctionCount; |
|
97 |
|
98 void sleepTestFunction() |
|
99 { |
|
100 QTest::qSleep(1000); |
|
101 ++testFunctionCount; |
|
102 } |
|
103 |
|
104 void emptyFunct() |
|
105 { |
|
106 |
|
107 } |
|
108 |
|
109 void noSleepTestFunction() |
|
110 { |
|
111 ++testFunctionCount; |
|
112 } |
|
113 |
|
114 void sleepTestFunctionMutex() |
|
115 { |
|
116 static QMutex testMutex; |
|
117 QTest::qSleep(1000); |
|
118 testMutex.lock(); |
|
119 ++testFunctionCount; |
|
120 testMutex.unlock(); |
|
121 } |
|
122 |
|
123 void noSleepTestFunctionMutex() |
|
124 { |
|
125 static QMutex testMutex; |
|
126 testMutex.lock(); |
|
127 ++testFunctionCount; |
|
128 testMutex.unlock(); |
|
129 } |
|
130 |
|
131 void tst_QThreadPool::runFunction() |
|
132 { |
|
133 { |
|
134 QThreadPool manager; |
|
135 testFunctionCount = 0; |
|
136 manager.start(createTask(noSleepTestFunction)); |
|
137 } |
|
138 QCOMPARE(testFunctionCount, 1); |
|
139 } |
|
140 |
|
141 void tst_QThreadPool::createThreadRunFunction() |
|
142 { |
|
143 { |
|
144 QThreadPool manager; |
|
145 testFunctionCount = 0; |
|
146 manager.start(createTask(noSleepTestFunction)); |
|
147 } |
|
148 |
|
149 QCOMPARE(testFunctionCount, 1); |
|
150 } |
|
151 |
|
152 void tst_QThreadPool::runMultiple() |
|
153 { |
|
154 const int runs = 10; |
|
155 |
|
156 { |
|
157 QThreadPool manager; |
|
158 testFunctionCount = 0; |
|
159 for (int i = 0; i < runs; ++i) { |
|
160 manager.start(createTask(sleepTestFunctionMutex)); |
|
161 } |
|
162 } |
|
163 QCOMPARE(testFunctionCount, runs); |
|
164 |
|
165 { |
|
166 QThreadPool manager; |
|
167 testFunctionCount = 0; |
|
168 for (int i = 0; i < runs; ++i) { |
|
169 manager.start(createTask(noSleepTestFunctionMutex)); |
|
170 } |
|
171 } |
|
172 QCOMPARE(testFunctionCount, runs); |
|
173 |
|
174 { |
|
175 QThreadPool manager; |
|
176 for (int i = 0; i < 500; ++i) |
|
177 manager.start(createTask(emptyFunct)); |
|
178 } |
|
179 } |
|
180 |
|
181 void tst_QThreadPool::waitcomplete() |
|
182 { |
|
183 testFunctionCount = 0; |
|
184 const int runs = 500; |
|
185 for (int i = 0; i < 500; ++i) { |
|
186 QThreadPool pool; |
|
187 pool.start(createTask(noSleepTestFunction)); |
|
188 } |
|
189 QCOMPARE(testFunctionCount, runs); |
|
190 } |
|
191 |
|
192 volatile bool ran; |
|
193 class TestTask : public QRunnable |
|
194 { |
|
195 public: |
|
196 void run() |
|
197 { |
|
198 ran = true; |
|
199 } |
|
200 }; |
|
201 |
|
202 void tst_QThreadPool::runTask() |
|
203 { |
|
204 QThreadPool manager; |
|
205 ran = false; |
|
206 manager.start(new TestTask()); |
|
207 // Hang if task is not runned. |
|
208 while (ran == false) |
|
209 QTest::qSleep(100); // no busy loop - this doesn't work with FIFO schedulers |
|
210 } |
|
211 |
|
212 /* |
|
213 Test running via QThreadPool::globalInstance() |
|
214 */ |
|
215 void tst_QThreadPool::singleton() |
|
216 { |
|
217 ran = false; |
|
218 QThreadPool::globalInstance()->start(new TestTask()); |
|
219 while (ran == false) |
|
220 QTest::qSleep(100); // no busy loop - this doesn't work with FIFO schedulers |
|
221 } |
|
222 |
|
223 int *value = 0; |
|
224 class IntAccessor : public QRunnable |
|
225 { |
|
226 public: |
|
227 void run() |
|
228 { |
|
229 for (int i = 0; i < 100; ++i) { |
|
230 ++(*value); |
|
231 QTest::qSleep(1); |
|
232 } |
|
233 } |
|
234 }; |
|
235 |
|
236 /* |
|
237 Test that the ThreadManager destructor waits until |
|
238 all threads have completed. |
|
239 */ |
|
240 void tst_QThreadPool::destruction() |
|
241 { |
|
242 value = new int; |
|
243 QThreadPool *threadManager = new QThreadPool(); |
|
244 threadManager->start(new IntAccessor()); |
|
245 threadManager->start(new IntAccessor()); |
|
246 delete threadManager; |
|
247 delete value; |
|
248 value = 0; |
|
249 } |
|
250 |
|
251 QSemaphore threadRecyclingSemaphore; |
|
252 QThread *recycledThread = 0; |
|
253 |
|
254 class ThreadRecorderTask : public QRunnable |
|
255 { |
|
256 public: |
|
257 void run() |
|
258 { |
|
259 recycledThread = QThread::currentThread(); |
|
260 threadRecyclingSemaphore.release(); |
|
261 } |
|
262 }; |
|
263 |
|
264 /* |
|
265 Test that the thread pool really reuses threads. |
|
266 */ |
|
267 void tst_QThreadPool::threadRecycling() |
|
268 { |
|
269 QThreadPool threadPool; |
|
270 |
|
271 threadPool.start(new ThreadRecorderTask()); |
|
272 threadRecyclingSemaphore.acquire(); |
|
273 QThread *thread1 = recycledThread; |
|
274 |
|
275 QTest::qSleep(100); |
|
276 |
|
277 threadPool.start(new ThreadRecorderTask()); |
|
278 threadRecyclingSemaphore.acquire(); |
|
279 QThread *thread2 = recycledThread; |
|
280 QCOMPARE(thread1, thread2); |
|
281 |
|
282 QTest::qSleep(100); |
|
283 |
|
284 threadPool.start(new ThreadRecorderTask()); |
|
285 threadRecyclingSemaphore.acquire(); |
|
286 QThread *thread3 = recycledThread; |
|
287 QCOMPARE(thread2, thread3); |
|
288 } |
|
289 |
|
290 class ExpiryTimeoutTask : public QRunnable |
|
291 { |
|
292 public: |
|
293 QThread *thread; |
|
294 int runCount; |
|
295 QSemaphore semaphore; |
|
296 |
|
297 ExpiryTimeoutTask() |
|
298 : thread(0), runCount(0) |
|
299 { |
|
300 setAutoDelete(false); |
|
301 } |
|
302 |
|
303 void run() |
|
304 { |
|
305 thread = QThread::currentThread(); |
|
306 ++runCount; |
|
307 semaphore.release(); |
|
308 } |
|
309 }; |
|
310 |
|
311 void tst_QThreadPool::expiryTimeout() |
|
312 { |
|
313 ExpiryTimeoutTask task; |
|
314 |
|
315 QThreadPool threadPool; |
|
316 threadPool.setMaxThreadCount(1); |
|
317 |
|
318 int expiryTimeout = threadPool.expiryTimeout(); |
|
319 threadPool.setExpiryTimeout(1000); |
|
320 QCOMPARE(threadPool.expiryTimeout(), 1000); |
|
321 |
|
322 // run the task |
|
323 threadPool.start(&task); |
|
324 QVERIFY(task.semaphore.tryAcquire(1, 10000)); |
|
325 QCOMPARE(task.runCount, 1); |
|
326 QVERIFY(!task.thread->wait(100)); |
|
327 // thread should expire |
|
328 QThread *firstThread = task.thread; |
|
329 QVERIFY(task.thread->wait(10000)); |
|
330 |
|
331 // run task again, thread should be restarted |
|
332 threadPool.start(&task); |
|
333 QVERIFY(task.semaphore.tryAcquire(1, 10000)); |
|
334 QCOMPARE(task.runCount, 2); |
|
335 QVERIFY(!task.thread->wait(100)); |
|
336 // thread should expire again |
|
337 QVERIFY(task.thread->wait(10000)); |
|
338 |
|
339 // thread pool should have reused the expired thread (instead of |
|
340 // starting a new one) |
|
341 QCOMPARE(firstThread, task.thread); |
|
342 |
|
343 threadPool.setExpiryTimeout(expiryTimeout); |
|
344 QCOMPARE(threadPool.expiryTimeout(), expiryTimeout); |
|
345 } |
|
346 |
|
347 #ifndef QT_NO_EXCEPTIONS |
|
348 class ExceptionTask : public QRunnable |
|
349 { |
|
350 public: |
|
351 void run() |
|
352 { |
|
353 throw new int; |
|
354 } |
|
355 }; |
|
356 #endif |
|
357 |
|
358 void tst_QThreadPool::exceptions() |
|
359 { |
|
360 #ifndef QT_NO_EXCEPTIONS |
|
361 ExceptionTask task; |
|
362 { |
|
363 QThreadPool threadPool; |
|
364 // Uncomment this for a nice crash. |
|
365 // threadPool.start(&task); |
|
366 } |
|
367 #else |
|
368 QSKIP("No exception support", SkipAll); |
|
369 #endif |
|
370 } |
|
371 |
|
372 void tst_QThreadPool::maxThreadCount() |
|
373 { |
|
374 DEPENDS_ON("setMaxThreadCount()"); |
|
375 } |
|
376 |
|
377 void tst_QThreadPool::setMaxThreadCount_data() |
|
378 { |
|
379 QTest::addColumn<int>("limit"); |
|
380 |
|
381 QTest::newRow("") << 1; |
|
382 QTest::newRow("") << -1; |
|
383 QTest::newRow("") << 2; |
|
384 QTest::newRow("") << -2; |
|
385 QTest::newRow("") << 4; |
|
386 QTest::newRow("") << -4; |
|
387 QTest::newRow("") << 0; |
|
388 QTest::newRow("") << 12345; |
|
389 QTest::newRow("") << -6789; |
|
390 QTest::newRow("") << 42; |
|
391 QTest::newRow("") << -666; |
|
392 } |
|
393 |
|
394 void tst_QThreadPool::setMaxThreadCount() |
|
395 { |
|
396 QFETCH(int, limit); |
|
397 QThreadPool *threadPool = QThreadPool::globalInstance(); |
|
398 int savedLimit = threadPool->maxThreadCount(); |
|
399 |
|
400 // maxThreadCount() should always return the previous argument to |
|
401 // setMaxThreadCount(), regardless of input |
|
402 threadPool->setMaxThreadCount(limit); |
|
403 QCOMPARE(threadPool->maxThreadCount(), limit); |
|
404 |
|
405 // the value returned from maxThreadCount() should always be valid input for setMaxThreadCount() |
|
406 threadPool->setMaxThreadCount(savedLimit); |
|
407 QCOMPARE(threadPool->maxThreadCount(), savedLimit); |
|
408 |
|
409 // setting the limit on children should have no effect on the parent |
|
410 { |
|
411 QThreadPool threadPool2(threadPool); |
|
412 savedLimit = threadPool2.maxThreadCount(); |
|
413 |
|
414 // maxThreadCount() should always return the previous argument to |
|
415 // setMaxThreadCount(), regardless of input |
|
416 threadPool2.setMaxThreadCount(limit); |
|
417 QCOMPARE(threadPool2.maxThreadCount(), limit); |
|
418 |
|
419 // the value returned from maxThreadCount() should always be valid input for setMaxThreadCount() |
|
420 threadPool2.setMaxThreadCount(savedLimit); |
|
421 QCOMPARE(threadPool2.maxThreadCount(), savedLimit); |
|
422 } |
|
423 } |
|
424 |
|
425 void tst_QThreadPool::setMaxThreadCountStartsAndStopsThreads() |
|
426 { |
|
427 class WaitingTask : public QRunnable |
|
428 { |
|
429 public: |
|
430 QSemaphore waitForStarted, waitToFinish; |
|
431 |
|
432 WaitingTask() { setAutoDelete(false); } |
|
433 |
|
434 void run() |
|
435 { |
|
436 waitForStarted.release(); |
|
437 waitToFinish.acquire(); |
|
438 } |
|
439 }; |
|
440 |
|
441 QThreadPool threadPool; |
|
442 threadPool.setMaxThreadCount(1); |
|
443 |
|
444 WaitingTask *task = new WaitingTask; |
|
445 threadPool.start(task); |
|
446 QVERIFY(task->waitForStarted.tryAcquire(1, 1000)); |
|
447 |
|
448 // thread limit is 1, cannot start more tasks |
|
449 threadPool.start(task); |
|
450 QVERIFY(!task->waitForStarted.tryAcquire(1, 1000)); |
|
451 |
|
452 // increasing the limit by 1 should start the task immediately |
|
453 threadPool.setMaxThreadCount(2); |
|
454 QVERIFY(task->waitForStarted.tryAcquire(1, 1000)); |
|
455 |
|
456 // ... but we still cannot start more tasks |
|
457 threadPool.start(task); |
|
458 QVERIFY(!task->waitForStarted.tryAcquire(1, 1000)); |
|
459 |
|
460 // increasing the limit should be able to start more than one at a time |
|
461 threadPool.start(task); |
|
462 threadPool.setMaxThreadCount(4); |
|
463 QVERIFY(task->waitForStarted.tryAcquire(2, 1000)); |
|
464 |
|
465 // ... but we still cannot start more tasks |
|
466 threadPool.start(task); |
|
467 threadPool.start(task); |
|
468 QVERIFY(!task->waitForStarted.tryAcquire(2, 1000)); |
|
469 |
|
470 // decreasing the thread limit should cause the active thread count to go down |
|
471 threadPool.setMaxThreadCount(2); |
|
472 QCOMPARE(threadPool.activeThreadCount(), 4); |
|
473 task->waitToFinish.release(2); |
|
474 QTest::qWait(1000); |
|
475 QCOMPARE(threadPool.activeThreadCount(), 2); |
|
476 |
|
477 // ... and we still cannot start more tasks |
|
478 threadPool.start(task); |
|
479 threadPool.start(task); |
|
480 QVERIFY(!task->waitForStarted.tryAcquire(2, 1000)); |
|
481 |
|
482 // start all remaining tasks |
|
483 threadPool.start(task); |
|
484 threadPool.start(task); |
|
485 threadPool.start(task); |
|
486 threadPool.start(task); |
|
487 threadPool.setMaxThreadCount(8); |
|
488 QVERIFY(task->waitForStarted.tryAcquire(6, 1000)); |
|
489 |
|
490 task->waitToFinish.release(10); |
|
491 // delete task; |
|
492 } |
|
493 |
|
494 |
|
495 void tst_QThreadPool::activeThreadCount() |
|
496 { |
|
497 DEPENDS_ON("tryReserveThread()"); |
|
498 DEPENDS_ON("reserveThread()"); |
|
499 DEPENDS_ON("releaseThread()"); |
|
500 } |
|
501 |
|
502 void tst_QThreadPool::reserveThread_data() |
|
503 { |
|
504 setMaxThreadCount_data(); |
|
505 } |
|
506 |
|
507 void tst_QThreadPool::reserveThread() |
|
508 { |
|
509 QFETCH(int, limit); |
|
510 QThreadPool *threadpool = QThreadPool::globalInstance(); |
|
511 int savedLimit = threadpool->maxThreadCount(); |
|
512 threadpool->setMaxThreadCount(limit); |
|
513 |
|
514 // reserve up to the limit |
|
515 for (int i = 0; i < limit; ++i) |
|
516 threadpool->reserveThread(); |
|
517 |
|
518 // reserveThread() should always reserve a thread, regardless of |
|
519 // how many have been previously reserved |
|
520 threadpool->reserveThread(); |
|
521 QCOMPARE(threadpool->activeThreadCount(), (limit > 0 ? limit : 0) + 1); |
|
522 threadpool->reserveThread(); |
|
523 QCOMPARE(threadpool->activeThreadCount(), (limit > 0 ? limit : 0) + 2); |
|
524 |
|
525 // cleanup |
|
526 threadpool->releaseThread(); |
|
527 threadpool->releaseThread(); |
|
528 for (int i = 0; i < limit; ++i) |
|
529 threadpool->releaseThread(); |
|
530 |
|
531 // reserving threads in children should not effect the parent |
|
532 { |
|
533 QThreadPool threadpool2(threadpool); |
|
534 threadpool2.setMaxThreadCount(limit); |
|
535 |
|
536 // reserve up to the limit |
|
537 for (int i = 0; i < limit; ++i) |
|
538 threadpool2.reserveThread(); |
|
539 |
|
540 // reserveThread() should always reserve a thread, regardless |
|
541 // of how many have been previously reserved |
|
542 threadpool2.reserveThread(); |
|
543 QCOMPARE(threadpool2.activeThreadCount(), (limit > 0 ? limit : 0) + 1); |
|
544 threadpool2.reserveThread(); |
|
545 QCOMPARE(threadpool2.activeThreadCount(), (limit > 0 ? limit : 0) + 2); |
|
546 |
|
547 threadpool->reserveThread(); |
|
548 QCOMPARE(threadpool->activeThreadCount(), 1); |
|
549 threadpool->reserveThread(); |
|
550 QCOMPARE(threadpool->activeThreadCount(), 2); |
|
551 |
|
552 // cleanup |
|
553 threadpool2.releaseThread(); |
|
554 threadpool2.releaseThread(); |
|
555 threadpool->releaseThread(); |
|
556 threadpool->releaseThread(); |
|
557 while (threadpool2.activeThreadCount() > 0) |
|
558 threadpool2.releaseThread(); |
|
559 } |
|
560 |
|
561 // reset limit on global QThreadPool |
|
562 threadpool->setMaxThreadCount(savedLimit); |
|
563 } |
|
564 |
|
565 void tst_QThreadPool::releaseThread_data() |
|
566 { |
|
567 setMaxThreadCount_data(); |
|
568 } |
|
569 |
|
570 void tst_QThreadPool::releaseThread() |
|
571 { |
|
572 QFETCH(int, limit); |
|
573 QThreadPool *threadpool = QThreadPool::globalInstance(); |
|
574 int savedLimit = threadpool->maxThreadCount(); |
|
575 threadpool->setMaxThreadCount(limit); |
|
576 |
|
577 // reserve up to the limit |
|
578 for (int i = 0; i < limit; ++i) |
|
579 threadpool->reserveThread(); |
|
580 |
|
581 // release should decrease the number of reserved threads |
|
582 int reserved = threadpool->activeThreadCount(); |
|
583 while (reserved-- > 0) { |
|
584 threadpool->releaseThread(); |
|
585 QCOMPARE(threadpool->activeThreadCount(), reserved); |
|
586 } |
|
587 QCOMPARE(threadpool->activeThreadCount(), 0); |
|
588 |
|
589 // releaseThread() can release more than have been reserved |
|
590 threadpool->releaseThread(); |
|
591 QCOMPARE(threadpool->activeThreadCount(), -1); |
|
592 threadpool->reserveThread(); |
|
593 QCOMPARE(threadpool->activeThreadCount(), 0); |
|
594 |
|
595 // releasing threads in children should not effect the parent |
|
596 { |
|
597 QThreadPool threadpool2(threadpool); |
|
598 threadpool2.setMaxThreadCount(limit); |
|
599 |
|
600 // reserve up to the limit |
|
601 for (int i = 0; i < limit; ++i) |
|
602 threadpool2.reserveThread(); |
|
603 |
|
604 // release should decrease the number of reserved threads |
|
605 int reserved = threadpool2.activeThreadCount(); |
|
606 while (reserved-- > 0) { |
|
607 threadpool2.releaseThread(); |
|
608 QCOMPARE(threadpool2.activeThreadCount(), reserved); |
|
609 QCOMPARE(threadpool->activeThreadCount(), 0); |
|
610 } |
|
611 QCOMPARE(threadpool2.activeThreadCount(), 0); |
|
612 QCOMPARE(threadpool->activeThreadCount(), 0); |
|
613 |
|
614 // releaseThread() can release more than have been reserved |
|
615 threadpool2.releaseThread(); |
|
616 QCOMPARE(threadpool2.activeThreadCount(), -1); |
|
617 QCOMPARE(threadpool->activeThreadCount(), 0); |
|
618 threadpool2.reserveThread(); |
|
619 QCOMPARE(threadpool2.activeThreadCount(), 0); |
|
620 QCOMPARE(threadpool->activeThreadCount(), 0); |
|
621 } |
|
622 |
|
623 // reset limit on global QThreadPool |
|
624 threadpool->setMaxThreadCount(savedLimit); |
|
625 } |
|
626 |
|
627 QAtomicInt count; |
|
628 class CountingRunnable : public QRunnable |
|
629 { |
|
630 public: void run() |
|
631 { |
|
632 count.ref(); |
|
633 } |
|
634 }; |
|
635 |
|
636 void tst_QThreadPool::start() |
|
637 { |
|
638 const int runs = 1000; |
|
639 count = 0; |
|
640 { |
|
641 QThreadPool threadPool; |
|
642 for (int i = 0; i< runs; ++i) { |
|
643 threadPool.start(new CountingRunnable()); |
|
644 } |
|
645 } |
|
646 QCOMPARE(int(count), runs); |
|
647 } |
|
648 |
|
649 void tst_QThreadPool::tryStart() |
|
650 { |
|
651 class WaitingTask : public QRunnable |
|
652 { |
|
653 public: |
|
654 QSemaphore semaphore; |
|
655 |
|
656 WaitingTask() { setAutoDelete(false); } |
|
657 |
|
658 void run() |
|
659 { |
|
660 semaphore.acquire(); |
|
661 count.ref(); |
|
662 } |
|
663 }; |
|
664 |
|
665 count = 0; |
|
666 |
|
667 WaitingTask task; |
|
668 QThreadPool threadPool; |
|
669 for (int i = 0; i < threadPool.maxThreadCount(); ++i) { |
|
670 threadPool.start(&task); |
|
671 } |
|
672 QVERIFY(!threadPool.tryStart(&task)); |
|
673 task.semaphore.release(threadPool.maxThreadCount()); |
|
674 threadPool.waitForDone(); |
|
675 QCOMPARE(int(count), threadPool.maxThreadCount()); |
|
676 } |
|
677 |
|
678 QMutex mutex; |
|
679 int activeThreads = 0; |
|
680 int peakActiveThreads = 0; |
|
681 void tst_QThreadPool::tryStartPeakThreadCount() |
|
682 { |
|
683 class CounterTask : public QRunnable |
|
684 { |
|
685 public: |
|
686 CounterTask() { setAutoDelete(false); } |
|
687 |
|
688 void run() |
|
689 { |
|
690 { |
|
691 QMutexLocker lock(&mutex); |
|
692 ++activeThreads; |
|
693 peakActiveThreads = qMax(peakActiveThreads, activeThreads); |
|
694 } |
|
695 |
|
696 QTest::qWait(100); |
|
697 { |
|
698 QMutexLocker lock(&mutex); |
|
699 --activeThreads; |
|
700 } |
|
701 } |
|
702 }; |
|
703 |
|
704 CounterTask task; |
|
705 QThreadPool threadPool; |
|
706 |
|
707 for (int i = 0; i < 20; ++i) { |
|
708 if (threadPool.tryStart(&task) == false) |
|
709 QTest::qWait(10); |
|
710 } |
|
711 QCOMPARE(peakActiveThreads, QThread::idealThreadCount()); |
|
712 |
|
713 for (int i = 0; i < 20; ++i) { |
|
714 if (threadPool.tryStart(&task) == false) |
|
715 QTest::qWait(10); |
|
716 } |
|
717 QCOMPARE(peakActiveThreads, QThread::idealThreadCount()); |
|
718 } |
|
719 |
|
720 void tst_QThreadPool::tryStartCount() |
|
721 { |
|
722 class SleeperTask : public QRunnable |
|
723 { |
|
724 public: |
|
725 SleeperTask() { setAutoDelete(false); } |
|
726 |
|
727 void run() |
|
728 { |
|
729 QTest::qWait(50); |
|
730 } |
|
731 }; |
|
732 |
|
733 SleeperTask task; |
|
734 QThreadPool threadPool; |
|
735 const int runs = 5; |
|
736 |
|
737 for (int i = 0; i < runs; ++i) { |
|
738 // qDebug() << "iteration" << i; |
|
739 int count = 0; |
|
740 while (threadPool.tryStart(&task)) |
|
741 ++count; |
|
742 QCOMPARE(count, QThread::idealThreadCount()); |
|
743 |
|
744 QTest::qWait(100); |
|
745 } |
|
746 } |
|
747 |
|
748 void tst_QThreadPool::waitForDone() |
|
749 { |
|
750 QTime total, pass; |
|
751 total.start(); |
|
752 |
|
753 QThreadPool threadPool; |
|
754 while (total.elapsed() < 10000) { |
|
755 int runs; |
|
756 runs = count = 0; |
|
757 pass.restart(); |
|
758 while (pass.elapsed() < 100) { |
|
759 threadPool.start(new CountingRunnable()); |
|
760 ++runs; |
|
761 } |
|
762 threadPool.waitForDone(); |
|
763 QCOMPARE(int(count), runs); |
|
764 |
|
765 runs = count = 0; |
|
766 pass.restart(); |
|
767 while (pass.elapsed() < 100) { |
|
768 threadPool.start(new CountingRunnable()); |
|
769 threadPool.start(new CountingRunnable()); |
|
770 runs += 2; |
|
771 } |
|
772 threadPool.waitForDone(); |
|
773 QCOMPARE(int(count), runs); |
|
774 } |
|
775 } |
|
776 |
|
777 void tst_QThreadPool::destroyingWaitsForTasksToFinish() |
|
778 { |
|
779 QTime total, pass; |
|
780 total.start(); |
|
781 |
|
782 while (total.elapsed() < 10000) { |
|
783 int runs; |
|
784 runs = count = 0; |
|
785 { |
|
786 QThreadPool threadPool; |
|
787 pass.restart(); |
|
788 while (pass.elapsed() < 100) { |
|
789 threadPool.start(new CountingRunnable()); |
|
790 ++runs; |
|
791 } |
|
792 } |
|
793 QCOMPARE(int(count), runs); |
|
794 |
|
795 runs = count = 0; |
|
796 { |
|
797 QThreadPool threadPool; |
|
798 pass.restart(); |
|
799 while (pass.elapsed() < 100) { |
|
800 threadPool.start(new CountingRunnable()); |
|
801 threadPool.start(new CountingRunnable()); |
|
802 runs += 2; |
|
803 } |
|
804 } |
|
805 QCOMPARE(int(count), runs); |
|
806 } |
|
807 } |
|
808 |
|
809 void tst_QThreadPool::stressTest() |
|
810 { |
|
811 class Task : public QRunnable |
|
812 { |
|
813 QSemaphore semaphore; |
|
814 public: |
|
815 Task() { setAutoDelete(false); } |
|
816 |
|
817 void start() |
|
818 { |
|
819 QThreadPool::globalInstance()->start(this); |
|
820 } |
|
821 |
|
822 void wait() |
|
823 { |
|
824 semaphore.acquire(); |
|
825 } |
|
826 |
|
827 void run() |
|
828 { |
|
829 semaphore.release(); |
|
830 } |
|
831 }; |
|
832 |
|
833 QTime total; |
|
834 total.start(); |
|
835 while (total.elapsed() < 30000) { |
|
836 Task t; |
|
837 t.start(); |
|
838 t.wait(); |
|
839 } |
|
840 } |
|
841 |
|
842 QTEST_MAIN(tst_QThreadPool); |
|
843 #include "tst_qthreadpool.moc" |