1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module 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 The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qthreadpool.h"
41 #include "qthreadpool_p.h"
42 #include "qdeadlinetimer.h"
43 #include "qcoreapplication.h"
44 
45 #include <algorithm>
46 
47 QT_BEGIN_NAMESPACE
48 
49 /*
50     QThread wrapper, provides synchronization against a ThreadPool
51 */
52 class QThreadPoolThread : public QThread
53 {
54     Q_OBJECT
55 public:
56     QThreadPoolThread(QThreadPoolPrivate *manager);
57     void run() override;
58     void registerThreadInactive();
59 
60     QWaitCondition runnableReady;
61     QThreadPoolPrivate *manager;
62     QRunnable *runnable;
63 };
64 
65 /*
66     QThreadPool private class.
67 */
68 
69 
70 /*!
71     \internal
72 */
QThreadPoolThread(QThreadPoolPrivate * manager)73 QThreadPoolThread::QThreadPoolThread(QThreadPoolPrivate *manager)
74     :manager(manager), runnable(nullptr)
75 {
76     setStackSize(manager->stackSize);
77 }
78 
79 /*
80     \internal
81 */
run()82 void QThreadPoolThread::run()
83 {
84     QMutexLocker locker(&manager->mutex);
85     for(;;) {
86         QRunnable *r = runnable;
87         runnable = nullptr;
88 
89         do {
90             if (r) {
91                 const bool del = r->autoDelete();
92                 Q_ASSERT(!del || r->ref == 1);
93 
94 
95                 // run the task
96                 locker.unlock();
97 #ifndef QT_NO_EXCEPTIONS
98                 try {
99 #endif
100                     r->run();
101 #ifndef QT_NO_EXCEPTIONS
102                 } catch (...) {
103                     qWarning("Qt Concurrent has caught an exception thrown from a worker thread.\n"
104                              "This is not supported, exceptions thrown in worker threads must be\n"
105                              "caught before control returns to Qt Concurrent.");
106                     registerThreadInactive();
107                     throw;
108                 }
109 #endif
110 
111                 if (del)
112                     delete r;
113                 locker.relock();
114             }
115 
116             // if too many threads are active, expire this thread
117             if (manager->tooManyThreadsActive())
118                 break;
119 
120             if (manager->queue.isEmpty()) {
121                 r = nullptr;
122                 break;
123             }
124 
125             QueuePage *page = manager->queue.first();
126             r = page->pop();
127 
128             if (page->isFinished()) {
129                 manager->queue.removeFirst();
130                 delete page;
131             }
132         } while (true);
133 
134         // if too many threads are active, expire this thread
135         bool expired = manager->tooManyThreadsActive();
136         if (!expired) {
137             manager->waitingThreads.enqueue(this);
138             registerThreadInactive();
139             // wait for work, exiting after the expiry timeout is reached
140             runnableReady.wait(locker.mutex(), QDeadlineTimer(manager->expiryTimeout));
141             ++manager->activeThreads;
142             if (manager->waitingThreads.removeOne(this))
143                 expired = true;
144             if (!manager->allThreads.contains(this)) {
145                 registerThreadInactive();
146                 break;
147             }
148         }
149         if (expired) {
150             manager->expiredThreads.enqueue(this);
151             registerThreadInactive();
152             break;
153         }
154     }
155 }
156 
registerThreadInactive()157 void QThreadPoolThread::registerThreadInactive()
158 {
159     if (--manager->activeThreads == 0)
160         manager->noActiveThreads.wakeAll();
161 }
162 
163 
164 /*
165     \internal
166 */
QThreadPoolPrivate()167 QThreadPoolPrivate:: QThreadPoolPrivate()
168 { }
169 
tryStart(QRunnable * task)170 bool QThreadPoolPrivate::tryStart(QRunnable *task)
171 {
172     Q_ASSERT(task != nullptr);
173     if (allThreads.isEmpty()) {
174         // always create at least one thread
175         startThread(task);
176         return true;
177     }
178 
179     // can't do anything if we're over the limit
180     if (activeThreadCount() >= maxThreadCount)
181         return false;
182 
183     if (waitingThreads.count() > 0) {
184         // recycle an available thread
185         enqueueTask(task);
186         waitingThreads.takeFirst()->runnableReady.wakeOne();
187         return true;
188     }
189 
190     if (!expiredThreads.isEmpty()) {
191         // restart an expired thread
192         QThreadPoolThread *thread = expiredThreads.dequeue();
193         Q_ASSERT(thread->runnable == nullptr);
194 
195         ++activeThreads;
196 
197         thread->runnable = task;
198         thread->start();
199         return true;
200     }
201 
202     // start a new thread
203     startThread(task);
204     return true;
205 }
206 
comparePriority(int priority,const QueuePage * p)207 inline bool comparePriority(int priority, const QueuePage *p)
208 {
209     return p->priority() < priority;
210 }
211 
enqueueTask(QRunnable * runnable,int priority)212 void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
213 {
214     Q_ASSERT(runnable != nullptr);
215     for (QueuePage *page : qAsConst(queue)) {
216         if (page->priority() == priority && !page->isFull()) {
217             page->push(runnable);
218             return;
219         }
220     }
221     auto it = std::upper_bound(queue.constBegin(), queue.constEnd(), priority, comparePriority);
222     queue.insert(std::distance(queue.constBegin(), it), new QueuePage(runnable, priority));
223 }
224 
activeThreadCount() const225 int QThreadPoolPrivate::activeThreadCount() const
226 {
227     return (allThreads.count()
228             - expiredThreads.count()
229             - waitingThreads.count()
230             + reservedThreads);
231 }
232 
tryToStartMoreThreads()233 void QThreadPoolPrivate::tryToStartMoreThreads()
234 {
235     // try to push tasks on the queue to any available threads
236     while (!queue.isEmpty()) {
237         QueuePage *page = queue.first();
238         if (!tryStart(page->first()))
239             break;
240 
241         page->pop();
242 
243         if (page->isFinished()) {
244             queue.removeFirst();
245             delete page;
246         }
247     }
248 }
249 
tooManyThreadsActive() const250 bool QThreadPoolPrivate::tooManyThreadsActive() const
251 {
252     const int activeThreadCount = this->activeThreadCount();
253     return activeThreadCount > maxThreadCount && (activeThreadCount - reservedThreads) > 1;
254 }
255 
256 /*!
257     \internal
258 */
startThread(QRunnable * runnable)259 void QThreadPoolPrivate::startThread(QRunnable *runnable)
260 {
261     Q_ASSERT(runnable != nullptr);
262     QScopedPointer <QThreadPoolThread> thread(new QThreadPoolThread(this));
263     thread->setObjectName(QLatin1String("Thread (pooled)"));
264     Q_ASSERT(!allThreads.contains(thread.data())); // if this assert hits, we have an ABA problem (deleted threads don't get removed here)
265     allThreads.insert(thread.data());
266     ++activeThreads;
267 
268     thread->runnable = runnable;
269     thread.take()->start();
270 }
271 
272 /*!
273     \internal
274 
275     Helper function only to be called from waitForDone(int)
276 */
reset()277 void QThreadPoolPrivate::reset()
278 {
279     // move the contents of the set out so that we can iterate without the lock
280     QSet<QThreadPoolThread *> allThreadsCopy;
281     allThreadsCopy.swap(allThreads);
282     expiredThreads.clear();
283     waitingThreads.clear();
284     mutex.unlock();
285 
286     for (QThreadPoolThread *thread: qAsConst(allThreadsCopy)) {
287         if (!thread->isFinished()) {
288             thread->runnableReady.wakeAll();
289             thread->wait();
290         }
291         delete thread;
292     }
293 
294     mutex.lock();
295 }
296 
297 /*!
298     \internal
299 
300     Helper function only to be called from waitForDone(int)
301 */
waitForDone(const QDeadlineTimer & timer)302 bool QThreadPoolPrivate::waitForDone(const QDeadlineTimer &timer)
303 {
304     while (!(queue.isEmpty() && activeThreads == 0) && !timer.hasExpired())
305         noActiveThreads.wait(&mutex, timer);
306 
307     return queue.isEmpty() && activeThreads == 0;
308 }
309 
waitForDone(int msecs)310 bool QThreadPoolPrivate::waitForDone(int msecs)
311 {
312     QMutexLocker locker(&mutex);
313     QDeadlineTimer timer(msecs);
314     do {
315         if (!waitForDone(timer))
316             return false;
317         reset();
318         // More threads can be started during reset(), in that case continue
319         // waiting if we still have time left.
320     } while ((!queue.isEmpty() || activeThreads) && !timer.hasExpired());
321 
322     return queue.isEmpty() && activeThreads == 0;
323 }
324 
clear()325 void QThreadPoolPrivate::clear()
326 {
327     QMutexLocker locker(&mutex);
328     while (!queue.isEmpty()) {
329         auto *page = queue.takeLast();
330         while (!page->isFinished()) {
331             QRunnable *r = page->pop();
332             if (r && r->autoDelete()) {
333                 Q_ASSERT(r->ref == 1);
334                 locker.unlock();
335                 delete r;
336                 locker.relock();
337             }
338         }
339         delete page;
340     }
341 }
342 
343 /*!
344     \since 5.9
345 
346     Attempts to remove the specified \a runnable from the queue if it is not yet started.
347     If the runnable had not been started, returns \c true, and ownership of \a runnable
348     is transferred to the caller (even when \c{runnable->autoDelete() == true}).
349     Otherwise returns \c false.
350 
351     \note If \c{runnable->autoDelete() == true}, this function may remove the wrong
352     runnable. This is known as the \l{https://en.wikipedia.org/wiki/ABA_problem}{ABA problem}:
353     the original \a runnable may already have executed and has since been deleted.
354     The memory is re-used for another runnable, which then gets removed instead of
355     the intended one. For this reason, we recommend calling this function only for
356     runnables that are not auto-deleting.
357 
358     \sa start(), QRunnable::autoDelete()
359 */
tryTake(QRunnable * runnable)360 bool QThreadPool::tryTake(QRunnable *runnable)
361 {
362     Q_D(QThreadPool);
363 
364     if (runnable == nullptr)
365         return false;
366 
367     QMutexLocker locker(&d->mutex);
368     for (QueuePage *page : qAsConst(d->queue)) {
369         if (page->tryTake(runnable)) {
370             if (page->isFinished()) {
371                 d->queue.removeOne(page);
372                 delete page;
373             }
374             if (runnable->autoDelete()) {
375                 Q_ASSERT(runnable->ref == 1);
376                 --runnable->ref; // undo ++ref in start()
377             }
378             return true;
379         }
380     }
381 
382     return false;
383 }
384 
385     /*!
386      \internal
387      Searches for \a runnable in the queue, removes it from the queue and
388      runs it if found. This function does not return until the runnable
389      has completed.
390      */
stealAndRunRunnable(QRunnable * runnable)391 void QThreadPoolPrivate::stealAndRunRunnable(QRunnable *runnable)
392 {
393     Q_Q(QThreadPool);
394     if (!q->tryTake(runnable))
395         return;
396     const bool del = runnable->autoDelete();
397 
398     runnable->run();
399 
400     if (del) {
401         Q_ASSERT(runnable->ref == 0); // tryTake already deref'ed
402         delete runnable;
403     }
404 }
405 
406 /*!
407     \class QThreadPool
408     \inmodule QtCore
409     \brief The QThreadPool class manages a collection of QThreads.
410     \since 4.4
411     \threadsafe
412 
413     \ingroup thread
414 
415     QThreadPool manages and recyles individual QThread objects to help reduce
416     thread creation costs in programs that use threads. Each Qt application
417     has one global QThreadPool object, which can be accessed by calling
418     globalInstance().
419 
420     To use one of the QThreadPool threads, subclass QRunnable and implement
421     the run() virtual function. Then create an object of that class and pass
422     it to QThreadPool::start().
423 
424     \snippet code/src_corelib_concurrent_qthreadpool.cpp 0
425 
426     QThreadPool deletes the QRunnable automatically by default. Use
427     QRunnable::setAutoDelete() to change the auto-deletion flag.
428 
429     QThreadPool supports executing the same QRunnable more than once
430     by calling tryStart(this) from within QRunnable::run().
431     If autoDelete is enabled the QRunnable will be deleted when
432     the last thread exits the run function. Calling start()
433     multiple times with the same QRunnable when autoDelete is enabled
434     creates a race condition and is not recommended.
435 
436     Threads that are unused for a certain amount of time will expire. The
437     default expiry timeout is 30000 milliseconds (30 seconds). This can be
438     changed using setExpiryTimeout(). Setting a negative expiry timeout
439     disables the expiry mechanism.
440 
441     Call maxThreadCount() to query the maximum number of threads to be used.
442     If needed, you can change the limit with setMaxThreadCount(). The default
443     maxThreadCount() is QThread::idealThreadCount(). The activeThreadCount()
444     function returns the number of threads currently doing work.
445 
446     The reserveThread() function reserves a thread for external
447     use. Use releaseThread() when your are done with the thread, so
448     that it may be reused.  Essentially, these functions temporarily
449     increase or reduce the active thread count and are useful when
450     implementing time-consuming operations that are not visible to the
451     QThreadPool.
452 
453     Note that QThreadPool is a low-level class for managing threads, see
454     the Qt Concurrent module for higher level alternatives.
455 
456     \sa QRunnable
457 */
458 
459 /*!
460     Constructs a thread pool with the given \a parent.
461 */
QThreadPool(QObject * parent)462 QThreadPool::QThreadPool(QObject *parent)
463     : QObject(*new QThreadPoolPrivate, parent)
464 { }
465 
466 /*!
467     Destroys the QThreadPool.
468     This function will block until all runnables have been completed.
469 */
~QThreadPool()470 QThreadPool::~QThreadPool()
471 {
472     waitForDone();
473 }
474 
475 /*!
476     Returns the global QThreadPool instance.
477 */
globalInstance()478 QThreadPool *QThreadPool::globalInstance()
479 {
480     static QPointer<QThreadPool> theInstance;
481     static QBasicMutex theMutex;
482 
483     const QMutexLocker locker(&theMutex);
484     if (theInstance.isNull() && !QCoreApplication::closingDown())
485         theInstance = new QThreadPool();
486     return theInstance;
487 }
488 
489 /*!
490     Reserves a thread and uses it to run \a runnable, unless this thread will
491     make the current thread count exceed maxThreadCount().  In that case,
492     \a runnable is added to a run queue instead. The \a priority argument can
493     be used to control the run queue's order of execution.
494 
495     Note that the thread pool takes ownership of the \a runnable if
496     \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c true,
497     and the \a runnable will be deleted automatically by the thread
498     pool after the \l{QRunnable::run()}{runnable->run()} returns. If
499     \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c false,
500     ownership of \a runnable remains with the caller. Note that
501     changing the auto-deletion on \a runnable after calling this
502     functions results in undefined behavior.
503 */
start(QRunnable * runnable,int priority)504 void QThreadPool::start(QRunnable *runnable, int priority)
505 {
506     if (!runnable)
507         return;
508 
509     Q_D(QThreadPool);
510     QMutexLocker locker(&d->mutex);
511     if (runnable->autoDelete()) {
512         Q_ASSERT(runnable->ref == 0);
513         ++runnable->ref;
514     }
515 
516     if (!d->tryStart(runnable)) {
517         d->enqueueTask(runnable, priority);
518 
519         if (!d->waitingThreads.isEmpty())
520             d->waitingThreads.takeFirst()->runnableReady.wakeOne();
521     }
522 }
523 
524 /*!
525     \overload
526     \since 5.15
527 
528     Reserves a thread and uses it to run \a functionToRun, unless this thread will
529     make the current thread count exceed maxThreadCount().  In that case,
530     \a functionToRun is added to a run queue instead. The \a priority argument can
531     be used to control the run queue's order of execution.
532 */
start(std::function<void ()> functionToRun,int priority)533 void QThreadPool::start(std::function<void()> functionToRun, int priority)
534 {
535     if (!functionToRun)
536         return;
537     start(QRunnable::create(std::move(functionToRun)), priority);
538 }
539 
540 /*!
541     Attempts to reserve a thread to run \a runnable.
542 
543     If no threads are available at the time of calling, then this function
544     does nothing and returns \c false.  Otherwise, \a runnable is run immediately
545     using one available thread and this function returns \c true.
546 
547     Note that on success the thread pool takes ownership of the \a runnable if
548     \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c true,
549     and the \a runnable will be deleted automatically by the thread
550     pool after the \l{QRunnable::run()}{runnable->run()} returns. If
551     \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c false,
552     ownership of \a runnable remains with the caller. Note that
553     changing the auto-deletion on \a runnable after calling this
554     function results in undefined behavior.
555 */
tryStart(QRunnable * runnable)556 bool QThreadPool::tryStart(QRunnable *runnable)
557 {
558     if (!runnable)
559         return false;
560 
561     if (runnable->autoDelete()) {
562         Q_ASSERT(runnable->ref == 0);
563         ++runnable->ref;
564     }
565 
566     Q_D(QThreadPool);
567     QMutexLocker locker(&d->mutex);
568     if (d->tryStart(runnable))
569         return true;
570 
571     // Undo the reference above as we did not start the runnable and
572     // take over ownership.
573     if (runnable->autoDelete()) {
574         --runnable->ref;
575         Q_ASSERT(runnable->ref == 0);
576     }
577     return false;
578 }
579 
580 /*!
581     \overload
582     \since 5.15
583     Attempts to reserve a thread to run \a functionToRun.
584 
585     If no threads are available at the time of calling, then this function
586     does nothing and returns \c false.  Otherwise, \a functionToRun is run immediately
587     using one available thread and this function returns \c true.
588 */
tryStart(std::function<void ()> functionToRun)589 bool QThreadPool::tryStart(std::function<void()> functionToRun)
590 {
591     if (!functionToRun)
592         return false;
593 
594     Q_D(QThreadPool);
595     QMutexLocker locker(&d->mutex);
596     if (!d->allThreads.isEmpty() && d->activeThreadCount() >= d->maxThreadCount)
597         return false;
598 
599     QRunnable *runnable = QRunnable::create(std::move(functionToRun));
600     if (d->tryStart(runnable))
601         return true;
602     delete runnable;
603     return false;
604 }
605 
606 /*! \property QThreadPool::expiryTimeout
607 
608     Threads that are unused for \a expiryTimeout milliseconds are considered
609     to have expired and will exit. Such threads will be restarted as needed.
610     The default \a expiryTimeout is 30000 milliseconds (30 seconds). If
611     \a expiryTimeout is negative, newly created threads will not expire, e.g.,
612     they will not exit until the thread pool is destroyed.
613 
614     Note that setting \a expiryTimeout has no effect on already running
615     threads. Only newly created threads will use the new \a expiryTimeout.
616     We recommend setting the \a expiryTimeout immediately after creating the
617     thread pool, but before calling start().
618 */
619 
expiryTimeout() const620 int QThreadPool::expiryTimeout() const
621 {
622     Q_D(const QThreadPool);
623     return d->expiryTimeout;
624 }
625 
setExpiryTimeout(int expiryTimeout)626 void QThreadPool::setExpiryTimeout(int expiryTimeout)
627 {
628     Q_D(QThreadPool);
629     if (d->expiryTimeout == expiryTimeout)
630         return;
631     d->expiryTimeout = expiryTimeout;
632 }
633 
634 /*! \property QThreadPool::maxThreadCount
635 
636     This property represents the maximum number of threads used by the thread
637     pool.
638 
639     \note The thread pool will always use at least 1 thread, even if
640     \a maxThreadCount limit is zero or negative.
641 
642     The default \a maxThreadCount is QThread::idealThreadCount().
643 */
644 
maxThreadCount() const645 int QThreadPool::maxThreadCount() const
646 {
647     Q_D(const QThreadPool);
648     return d->maxThreadCount;
649 }
650 
setMaxThreadCount(int maxThreadCount)651 void QThreadPool::setMaxThreadCount(int maxThreadCount)
652 {
653     Q_D(QThreadPool);
654     QMutexLocker locker(&d->mutex);
655 
656     if (maxThreadCount == d->maxThreadCount)
657         return;
658 
659     d->maxThreadCount = maxThreadCount;
660     d->tryToStartMoreThreads();
661 }
662 
663 /*! \property QThreadPool::activeThreadCount
664 
665     This property represents the number of active threads in the thread pool.
666 
667     \note It is possible for this function to return a value that is greater
668     than maxThreadCount(). See reserveThread() for more details.
669 
670     \sa reserveThread(), releaseThread()
671 */
672 
activeThreadCount() const673 int QThreadPool::activeThreadCount() const
674 {
675     Q_D(const QThreadPool);
676     QMutexLocker locker(&d->mutex);
677     return d->activeThreadCount();
678 }
679 
680 /*!
681     Reserves one thread, disregarding activeThreadCount() and maxThreadCount().
682 
683     Once you are done with the thread, call releaseThread() to allow it to be
684     reused.
685 
686     \note This function will always increase the number of active threads.
687     This means that by using this function, it is possible for
688     activeThreadCount() to return a value greater than maxThreadCount() .
689 
690     \sa releaseThread()
691  */
reserveThread()692 void QThreadPool::reserveThread()
693 {
694     Q_D(QThreadPool);
695     QMutexLocker locker(&d->mutex);
696     ++d->reservedThreads;
697 }
698 
699 /*! \property QThreadPool::stackSize
700 
701     This property contains the stack size for the thread pool worker
702     threads.
703 
704     The value of the property is only used when the thread pool creates
705     new threads. Changing it has no effect for already created
706     or running threads.
707 
708     The default value is 0, which makes QThread use the operating
709     system default stack size.
710 
711     \since 5.10
712 */
setStackSize(uint stackSize)713 void QThreadPool::setStackSize(uint stackSize)
714 {
715     Q_D(QThreadPool);
716     d->stackSize = stackSize;
717 }
718 
stackSize() const719 uint QThreadPool::stackSize() const
720 {
721     Q_D(const QThreadPool);
722     return d->stackSize;
723 }
724 
725 /*!
726     Releases a thread previously reserved by a call to reserveThread().
727 
728     \note Calling this function without previously reserving a thread
729     temporarily increases maxThreadCount(). This is useful when a
730     thread goes to sleep waiting for more work, allowing other threads
731     to continue. Be sure to call reserveThread() when done waiting, so
732     that the thread pool can correctly maintain the
733     activeThreadCount().
734 
735     \sa reserveThread()
736 */
releaseThread()737 void QThreadPool::releaseThread()
738 {
739     Q_D(QThreadPool);
740     QMutexLocker locker(&d->mutex);
741     --d->reservedThreads;
742     d->tryToStartMoreThreads();
743 }
744 
745 /*!
746     Waits up to \a msecs milliseconds for all threads to exit and removes all
747     threads from the thread pool. Returns \c true if all threads were removed;
748     otherwise it returns \c false. If \a msecs is -1 (the default), the timeout
749     is ignored (waits for the last thread to exit).
750 */
waitForDone(int msecs)751 bool QThreadPool::waitForDone(int msecs)
752 {
753     Q_D(QThreadPool);
754     return d->waitForDone(msecs);
755 }
756 
757 /*!
758     \since 5.2
759 
760     Removes the runnables that are not yet started from the queue.
761     The runnables for which \l{QRunnable::autoDelete()}{runnable->autoDelete()}
762     returns \c true are deleted.
763 
764     \sa start()
765 */
clear()766 void QThreadPool::clear()
767 {
768     Q_D(QThreadPool);
769     d->clear();
770 }
771 
772 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
773 /*!
774     \internal
775 
776     Returns \c true if \a thread is a thread managed by this thread pool.
777 */
778 #else
779 /*!
780     \since 6.0
781 
782     Returns \c true if \a thread is a thread managed by this thread pool.
783 */
784 #endif
contains(const QThread * thread) const785 bool QThreadPool::contains(const QThread *thread) const
786 {
787     Q_D(const QThreadPool);
788     const QThreadPoolThread *poolThread = qobject_cast<const QThreadPoolThread *>(thread);
789     if (!poolThread)
790         return false;
791     return d->allThreads.contains(const_cast<QThreadPoolThread *>(poolThread));
792 }
793 
794 #if QT_DEPRECATED_SINCE(5, 9)
795 /*!
796     \since 5.5
797     \obsolete use tryTake() instead, but note the different deletion rules.
798 
799     Removes the specified \a runnable from the queue if it is not yet started.
800     The runnables for which \l{QRunnable::autoDelete()}{runnable->autoDelete()}
801     returns \c true are deleted.
802 
803     \sa start(), tryTake()
804 */
cancel(QRunnable * runnable)805 void QThreadPool::cancel(QRunnable *runnable)
806 {
807     if (tryTake(runnable) && runnable->autoDelete() && !runnable->ref) // tryTake already deref'ed
808         delete runnable;
809 }
810 #endif
811 
812 QT_END_NAMESPACE
813 
814 #include "moc_qthreadpool.cpp"
815 #include "qthreadpool.moc"
816