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