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