Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qthreadpool.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qthreadpool.h"
9
10#include <QtCore/qpointer.h>
11
12#include <algorithm>
13#include <climits> // For INT_MAX
14#include <memory>
15
16using namespace std::chrono_literals;
17
18QT_BEGIN_NAMESPACE
19
20using namespace Qt::StringLiterals;
21
22namespace {
23struct QThreadGlobalPool
24{
25 QPointer<QThreadPool> theInstance;
26};
27QBasicMutex theMutex;
28}
29Q_GLOBAL_STATIC(QThreadGlobalPool, globalPool)
30
31/*
32 QThread wrapper, provides synchronization against a ThreadPool
33*/
46
47/*
48 QThreadPool private class.
49*/
51
52/*!
53 \internal
54*/
55QThreadPoolThread::QThreadPoolThread(QThreadPoolPrivate *manager)
56 :manager(manager), runnable(nullptr)
57{
58 setStackSize(manager->stackSize);
59}
60
61/*
62 \internal
63*/
65{
66 QMutexLocker locker(&manager->mutex);
67 for(;;) {
68 QRunnable *r = runnable;
69 runnable = nullptr;
70
71 do {
72 if (r) {
73 // If autoDelete() is false, r might already be deleted after run(), so check status now.
74 const bool del = r->autoDelete();
75
76 // run the task
77 locker.unlock();
78#ifndef QT_NO_EXCEPTIONS
79 try {
80#endif
81 r->run();
82#ifndef QT_NO_EXCEPTIONS
83 } catch (...) {
84 qWarning("Qt Concurrent has caught an exception thrown from a worker thread.\n"
85 "This is not supported, exceptions thrown in worker threads must be\n"
86 "caught before control returns to Qt Concurrent.");
88 throw;
89 }
90#endif
91
92 if (del)
93 delete r;
94 locker.relock();
95 }
96
97 // if too many threads are active, stop working in this one
99 break;
100
101 // all work is done, time to wait for more
102 if (manager->queue.isEmpty())
103 break;
104
105 QueuePage *page = manager->queue.constFirst();
106 r = page->pop();
107
108 if (page->isFinished()) {
109 manager->queue.removeFirst();
110 delete page;
111 }
112 } while (true);
113
114 // this thread is about to be deleted, do not wait or expire
115 if (!manager->allThreads.contains(this)) {
117 return;
118 }
119
120 // if too many threads are active, expire this thread
122 manager->expiredThreads.enqueue(this);
124 return;
125 }
126 manager->waitingThreads.enqueue(this);
128 // wait for work, exiting after the expiry timeout is reached
129 runnableReady.wait(locker.mutex(), QDeadlineTimer(manager->expiryTimeout));
130 // this thread is about to be deleted, do not work or expire
131 if (!manager->allThreads.contains(this)) {
132 Q_ASSERT(manager->queue.isEmpty());
133 return;
134 }
135 if (manager->waitingThreads.removeOne(this)) {
136 manager->expiredThreads.enqueue(this);
137 return;
138 }
140 }
141}
142
144{
145 if (--manager->activeThreads == 0)
146 manager->noActiveThreads.wakeAll();
147}
148
149
150/*
151 \internal
152*/
155
157{
158 if (!globalPool.exists())
159 return;
160
161 QThreadPool *globalThreadPool = []{
162 const QMutexLocker locker(&theMutex);
163 QThreadPool *p = globalPool->theInstance.get();
164 globalPool->theInstance = nullptr;
165 return p;
166 }();
167 if (globalThreadPool)
168 delete globalThreadPool;
169}
170
171bool QThreadPoolPrivate::tryStart(QRunnable *task)
172{
173 Q_ASSERT(task != nullptr);
174 if (allThreads.isEmpty()) {
175 // always create at least one thread
176 startThread(task);
177 return true;
178 }
179
180 // can't do anything if we're over the limit
182 return false;
183
184 if (!waitingThreads.isEmpty()) {
185 // recycle an available thread
186 enqueueTask(task);
187 waitingThreads.takeFirst()->runnableReady.wakeOne();
188 return true;
189 }
190
191 if (!expiredThreads.isEmpty()) {
192 // restart an expired thread
193 QThreadPoolThread *thread = expiredThreads.dequeue();
194 Q_ASSERT(thread->runnable == nullptr);
195
197
198 thread->runnable = task;
199
200 // Ensure that the thread has actually finished, otherwise the following
201 // start() has no effect.
202 thread->wait();
203 Q_ASSERT(thread->isFinished());
204 thread->start(threadPriority);
205 return true;
206 }
207
208 // start a new thread
209 startThread(task);
210 return true;
211}
212
213inline bool comparePriority(int priority, const QueuePage *p)
214{
215 return p->priority() < priority;
216}
217
218void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
219{
220 Q_ASSERT(runnable != nullptr);
221 for (QueuePage *page : std::as_const(queue)) {
222 if (page->priority() == priority && !page->isFull()) {
223 page->push(runnable);
224 return;
225 }
226 }
227 auto it = std::upper_bound(queue.constBegin(), queue.constEnd(), priority, comparePriority);
228 queue.insert(std::distance(queue.constBegin(), it), new QueuePage(runnable, priority));
229}
230
232{
233 return (allThreads.size()
234 - expiredThreads.size()
235 - waitingThreads.size()
236 + reservedThreads);
237}
238
240{
241 // try to push tasks on the queue to any available threads
242 while (!queue.isEmpty()) {
243 QueuePage *page = queue.constFirst();
244 if (!tryStart(page->first()))
245 break;
246
247 page->pop();
248
249 if (page->isFinished()) {
250 queue.removeFirst();
251 delete page;
252 }
253 }
254}
255
257{
258 const int activeThreadCount = this->activeThreadCount();
259 return activeThreadCount >= maxThreadCount() && (activeThreadCount - reservedThreads) >= 1;
260}
261
263{
264 const int activeThreadCount = this->activeThreadCount();
265 return activeThreadCount > maxThreadCount() && (activeThreadCount - reservedThreads) > 1;
266}
267
268/*!
269 \internal
270*/
271void QThreadPoolPrivate::startThread(QRunnable *runnable)
272{
273 Q_ASSERT(runnable != nullptr);
274 auto thread = std::make_unique<QThreadPoolThread>(this);
275 if (objectName.isEmpty())
276 objectName = u"Thread (pooled)"_s;
277 thread->setObjectName(objectName);
278 thread->setServiceLevel(serviceLevel);
279 Q_ASSERT(!allThreads.contains(thread.get())); // if this assert hits, we have an ABA problem (deleted threads don't get removed here)
280 allThreads.insert(thread.get());
282
283 thread->runnable = runnable;
284 thread.release()->start(threadPriority);
285}
286
287/*!
288 \internal
289
290 Helper function only to be called from waitForDone()
291
292 Deletes all current threads.
293*/
295{
296 // move the contents of the set out so that we can iterate without the lock
297 auto allThreadsCopy = std::exchange(allThreads, {});
298 expiredThreads.clear();
299 waitingThreads.clear();
300
301 mutex.unlock();
302
303 for (QThreadPoolThread *thread : std::as_const(allThreadsCopy)) {
304 if (thread->isRunning()) {
305 thread->runnableReady.wakeAll();
306 thread->wait();
307 }
308 delete thread;
309 }
310
311 mutex.lock();
312}
313
314/*!
315 \internal
316
317 Helper function only to be called from the public waitForDone()
318*/
319bool QThreadPoolPrivate::waitForDone(const QDeadlineTimer &timer)
320{
321 QMutexLocker locker(&mutex);
322 while (!(queue.isEmpty() && activeThreads == 0) && !timer.hasExpired())
323 noActiveThreads.wait(&mutex, timer);
324
325 if (!queue.isEmpty() || activeThreads)
326 return false;
327
328 reset();
329 // New jobs might have started during reset, but return anyway
330 // as the active thread and task count did reach 0 once, and
331 // race conditions are outside our scope.
332 return true;
333}
334
336{
337 QMutexLocker locker(&mutex);
338 while (!queue.isEmpty()) {
339 auto *page = queue.takeLast();
340 while (!page->isFinished()) {
341 QRunnable *r = page->pop();
342 if (r && r->autoDelete()) {
343 locker.unlock();
344 delete r;
345 locker.relock();
346 }
347 }
348 delete page;
349 }
350}
351
352/*!
353 \since 5.9
354
355 Attempts to remove the specified \a runnable from the queue if it is not yet started.
356 If the runnable had not been started, returns \c true, and ownership of \a runnable
357 is transferred to the caller (even when \c{runnable->autoDelete() == true}).
358 Otherwise returns \c false.
359
360 \note If \c{runnable->autoDelete() == true}, this function may remove the wrong
361 runnable. This is known as the \l{https://en.wikipedia.org/wiki/ABA_problem}{ABA problem}:
362 the original \a runnable may already have executed and has since been deleted.
363 The memory is re-used for another runnable, which then gets removed instead of
364 the intended one. For this reason, we recommend calling this function only for
365 runnables that are not auto-deleting.
366
367 \sa start(), QRunnable::autoDelete()
368*/
369bool QThreadPool::tryTake(QRunnable *runnable)
370{
371 Q_D(QThreadPool);
372
373 if (runnable == nullptr)
374 return false;
375
376 QMutexLocker locker(&d->mutex);
377 for (QueuePage *page : std::as_const(d->queue)) {
378 if (page->tryTake(runnable)) {
379 if (page->isFinished()) {
380 d->queue.removeOne(page);
381 delete page;
382 }
383 return true;
384 }
385 }
386
387 return false;
388}
389
390 /*!
391 \internal
392 Searches for \a runnable in the queue, removes it from the queue and
393 runs it if found. This function does not return until the runnable
394 has completed.
395 */
396void QThreadPoolPrivate::stealAndRunRunnable(QRunnable *runnable)
397{
398 Q_Q(QThreadPool);
399 if (!q->tryTake(runnable))
400 return;
401 // If autoDelete() is false, runnable might already be deleted after run(), so check status now.
402 const bool del = runnable->autoDelete();
403
404 runnable->run();
405
406 if (del)
407 delete runnable;
408}
409
410/*!
411 \class QThreadPool
412 \inmodule QtCore
413 \brief The QThreadPool class manages a collection of QThreads.
414 \since 4.4
415 \threadsafe
416
417 \ingroup thread
418
419 QThreadPool manages and recycles individual QThread objects to help reduce
420 thread creation costs in programs that use threads. Each Qt application
421 has one global QThreadPool object, which can be accessed by calling
422 globalInstance().
423
424 To use one of the QThreadPool threads, subclass QRunnable and implement
425 the run() virtual function. Then create an object of that class and pass
426 it to QThreadPool::start().
427
428 \snippet code/src_corelib_concurrent_qthreadpool.cpp 0
429
430 QThreadPool deletes the QRunnable automatically by default. Use
431 QRunnable::setAutoDelete() to change the auto-deletion flag.
432
433 QThreadPool supports executing the same QRunnable more than once
434 by calling tryStart(this) from within QRunnable::run().
435 If autoDelete is enabled the QRunnable will be deleted when
436 the last thread exits the run function. Calling start()
437 multiple times with the same QRunnable when autoDelete is enabled
438 creates a race condition and is not recommended.
439
440 Threads that are unused for a certain amount of time will expire. The
441 default expiry timeout is 30000 milliseconds (30 seconds). This can be
442 changed using setExpiryTimeout(). Setting a negative expiry timeout
443 disables the expiry mechanism.
444
445 Call maxThreadCount() to query the maximum number of threads to be used.
446 If needed, you can change the limit with setMaxThreadCount(). The default
447 maxThreadCount() is QThread::idealThreadCount(). The activeThreadCount()
448 function returns the number of threads currently doing work.
449
450 The reserveThread() function reserves a thread for external
451 use. Use releaseThread() when your are done with the thread, so
452 that it may be reused. Essentially, these functions temporarily
453 increase or reduce the active thread count and are useful when
454 implementing time-consuming operations that are not visible to the
455 QThreadPool.
456
457 Note that QThreadPool is a low-level class for managing threads, see
458 the Qt Concurrent module for higher level alternatives.
459
460 \sa QRunnable
461*/
462
463/*!
464 Constructs a thread pool with the given \a parent.
465*/
466QThreadPool::QThreadPool(QObject *parent)
467 : QObject(*new QThreadPoolPrivate, parent)
468{
469 Q_D(QThreadPool);
470 connect(this, &QObject::objectNameChanged, this, [d](const QString &newName) {
471 // We keep a copy of the name under our own lock, so we can access it thread-safely.
472 QMutexLocker locker(&d->mutex);
473 d->objectName = newName;
474 });
475}
476
477/*!
478 Destroys the QThreadPool.
479 This function will block until all runnables have been completed.
480*/
481QThreadPool::~QThreadPool()
482{
483 Q_D(QThreadPool);
484 waitForDone();
485 Q_ASSERT(d->queue.isEmpty());
486 Q_ASSERT(d->allThreads.isEmpty());
487}
488
489/*!
490 Returns the global QThreadPool instance.
491*/
492QThreadPool *QThreadPool::globalInstance()
493{
494 if (!globalPool.exists() && QCoreApplication::closingDown())
495 return nullptr; // don't create
496
497 QPointer<QThreadPool> &theInstance = globalPool->theInstance;
498 const QMutexLocker locker(&theMutex);
499 if (!theInstance)
500 theInstance = new QThreadPool();
501 return theInstance;
502}
503
504/*!
505 Reserves a thread and uses it to run \a runnable, unless this thread will
506 make the current thread count exceed maxThreadCount(). In that case,
507 \a runnable is added to a run queue instead. The \a priority argument can
508 be used to control the run queue's order of execution.
509
510 Note that the thread pool takes ownership of the \a runnable if
511 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c true,
512 and the \a runnable will be deleted automatically by the thread
513 pool after the \l{QRunnable::run()}{runnable->run()} returns. If
514 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c false,
515 ownership of \a runnable remains with the caller. Note that
516 changing the auto-deletion on \a runnable after calling this
517 functions results in undefined behavior.
518*/
519void QThreadPool::start(QRunnable *runnable, int priority)
520{
521 if (!runnable)
522 return;
523
524 Q_D(QThreadPool);
525 QMutexLocker locker(&d->mutex);
526
527 if (!d->tryStart(runnable))
528 d->enqueueTask(runnable, priority);
529}
530
531/*!
532 \fn template<typename Callable, QRunnable::if_callable<Callable>> void QThreadPool::start(Callable &&callableToRun, int priority)
533 \overload
534 \since 5.15
535
536 Reserves a thread and uses it to run \a callableToRun, unless this thread will
537 make the current thread count exceed maxThreadCount(). In that case,
538 \a callableToRun is added to a run queue instead. The \a priority argument can
539 be used to control the run queue's order of execution.
540
541 \note In Qt version prior to 6.6, this function took std::function<void()>,
542 and therefore couldn't handle move-only callables.
543
544 \constraints \c Callable
545 is a function or function object which can be called with zero arguments.
546*/
547
548/*!
549 Attempts to reserve a thread to run \a runnable.
550
551 If no threads are available at the time of calling, then this function
552 does nothing and returns \c false. Otherwise, \a runnable is run immediately
553 using one available thread and this function returns \c true.
554
555 Note that on success the thread pool takes ownership of the \a runnable if
556 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c true,
557 and the \a runnable will be deleted automatically by the thread
558 pool after the \l{QRunnable::run()}{runnable->run()} returns. If
559 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c false,
560 ownership of \a runnable remains with the caller. Note that
561 changing the auto-deletion on \a runnable after calling this
562 function results in undefined behavior.
563*/
564bool QThreadPool::tryStart(QRunnable *runnable)
565{
566 if (!runnable)
567 return false;
568
569 Q_D(QThreadPool);
570 QMutexLocker locker(&d->mutex);
571 if (d->tryStart(runnable))
572 return true;
573
574 return false;
575}
576
577/*!
578 \fn template<typename Callable, QRunnable::if_callable<Callable>> bool QThreadPool::tryStart(Callable &&callableToRun)
579 \overload
580 \since 5.15
581 Attempts to reserve a thread to run \a callableToRun.
582
583 If no threads are available at the time of calling, then this function
584 does nothing and returns \c false. Otherwise, \a callableToRun is run immediately
585 using one available thread and this function returns \c true.
586
587 \note In Qt version prior to 6.6, this function took std::function<void()>,
588 and therefore couldn't handle move-only callables.
589
590 \constraints \c Callable
591 is a function or function object which can be called with zero arguments.
592*/
593
594/*! \property QThreadPool::expiryTimeout
595 \brief the thread expiry timeout value in milliseconds.
596
597 Threads that are unused for \e expiryTimeout milliseconds are considered
598 to have expired and will exit. Such threads will be restarted as needed.
599 The default \a expiryTimeout is 30000 milliseconds (30 seconds). If
600 \a expiryTimeout is negative, newly created threads will not expire, e.g.,
601 they will not exit until the thread pool is destroyed.
602
603 Note that setting \a expiryTimeout has no effect on already running
604 threads. Only newly created threads will use the new \a expiryTimeout.
605 We recommend setting the \a expiryTimeout immediately after creating the
606 thread pool, but before calling start().
607*/
608
609int QThreadPool::expiryTimeout() const
610{
611 using namespace std::chrono;
612 Q_D(const QThreadPool);
613 QMutexLocker locker(&d->mutex);
614 if (d->expiryTimeout == decltype(d->expiryTimeout)::max())
615 return -1;
616 return duration_cast<milliseconds>(d->expiryTimeout).count();
617}
618
619void QThreadPool::setExpiryTimeout(int expiryTimeout)
620{
621 Q_D(QThreadPool);
622 QMutexLocker locker(&d->mutex);
623 if (expiryTimeout < 0)
624 d->expiryTimeout = decltype(d->expiryTimeout)::max();
625 else
626 d->expiryTimeout = expiryTimeout * 1ms;
627}
628
629/*! \property QThreadPool::maxThreadCount
630
631 \brief the maximum number of threads used by the thread pool. This property
632 will default to the value of QThread::idealThreadCount() at the moment the
633 QThreadPool object is created.
634
635 \note The thread pool will always use at least 1 thread, even if
636 \a maxThreadCount limit is zero or negative.
637
638 The default \a maxThreadCount is QThread::idealThreadCount().
639*/
640
641int QThreadPool::maxThreadCount() const
642{
643 Q_D(const QThreadPool);
644 QMutexLocker locker(&d->mutex);
645 return d->requestedMaxThreadCount;
646}
647
648void QThreadPool::setMaxThreadCount(int maxThreadCount)
649{
650 Q_D(QThreadPool);
651 QMutexLocker locker(&d->mutex);
652
653 if (maxThreadCount == d->requestedMaxThreadCount)
654 return;
655
656 d->requestedMaxThreadCount = maxThreadCount;
657 d->tryToStartMoreThreads();
658}
659
660/*! \property QThreadPool::activeThreadCount
661
662 \brief the number of active threads in the thread pool.
663
664 \note It is possible for this function to return a value that is greater
665 than maxThreadCount(). See reserveThread() for more details.
666
667 \sa reserveThread(), releaseThread()
668*/
669
670int QThreadPool::activeThreadCount() const
671{
672 Q_D(const QThreadPool);
673 QMutexLocker locker(&d->mutex);
674 return d->activeThreadCount();
675}
676
677/*!
678 Reserves one thread, disregarding activeThreadCount() and maxThreadCount().
679
680 Once you are done with the thread, call releaseThread() to allow it to be
681 reused.
682
683 \note Even if reserving maxThreadCount() threads or more, the thread pool
684 will still allow a minimum of one thread.
685
686 \note This function will increase the reported 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 */
692void QThreadPool::reserveThread()
693{
694 Q_D(QThreadPool);
695 QMutexLocker locker(&d->mutex);
696 ++d->reservedThreads;
697}
698
699/*! \property QThreadPool::stackSize
700 \brief the stack size for the thread pool worker threads.
701
702 The value of the property is only used when the thread pool creates
703 new threads. Changing it has no effect for already created
704 or running threads.
705
706 The default value is 0, which makes QThread use the operating
707 system default stack size.
708
709 \since 5.10
710*/
711void QThreadPool::setStackSize(uint stackSize)
712{
713 Q_D(QThreadPool);
714 QMutexLocker locker(&d->mutex);
715 d->stackSize = stackSize;
716}
717
718uint QThreadPool::stackSize() const
719{
720 Q_D(const QThreadPool);
721 QMutexLocker locker(&d->mutex);
722 return d->stackSize;
723}
724
725/*! \property QThreadPool::threadPriority
726 \brief the thread priority for new worker threads.
727
728 The value of the property is only used when the thread pool starts
729 new threads. Changing it has no effect for already running threads.
730
731 The default value is QThread::InheritPriority, which makes QThread
732 use the same priority as the one the QThreadPool object lives in.
733
734 \sa QThread::Priority
735
736 \since 6.2
737*/
738
739void QThreadPool::setThreadPriority(QThread::Priority priority)
740{
741 Q_D(QThreadPool);
742 QMutexLocker locker(&d->mutex);
743 d->threadPriority = priority;
744}
745
746QThread::Priority QThreadPool::threadPriority() const
747{
748 Q_D(const QThreadPool);
749 QMutexLocker locker(&d->mutex);
750 return d->threadPriority;
751}
752
753/*!
754 Releases a thread previously reserved by a call to reserveThread().
755
756 \note Calling this function without previously reserving a thread
757 temporarily increases maxThreadCount(). This is useful when a
758 thread goes to sleep waiting for more work, allowing other threads
759 to continue. Be sure to call reserveThread() when done waiting, so
760 that the thread pool can correctly maintain the
761 activeThreadCount().
762
763 \sa reserveThread()
764*/
765void QThreadPool::releaseThread()
766{
767 Q_D(QThreadPool);
768 QMutexLocker locker(&d->mutex);
769 --d->reservedThreads;
770 d->tryToStartMoreThreads();
771}
772
773/*!
774 \since 6.9
775
776 Sets the Quality of Service level of thread objects created after the call
777 to this setter to \a serviceLevel.
778
779 Support is not available on every platform. Consult
780 QThread::setServiceLevel() for details.
781
782 \sa serviceLevel(), QThread::serviceLevel()
783*/
784void QThreadPool::setServiceLevel(QThread::QualityOfService serviceLevel)
785{
786 Q_D(QThreadPool);
787 QMutexLocker locker(&d->mutex);
788 d->serviceLevel = serviceLevel;
789}
790
791/*!
792 \since 6.9
793
794 Returns the current Quality of Service level of the thread.
795
796 \sa setServiceLevel(), QThread::serviceLevel()
797*/
798QThread::QualityOfService QThreadPool::serviceLevel() const
799{
800 Q_D(const QThreadPool);
801 QMutexLocker locker(&d->mutex);
802 return d->serviceLevel;
803}
804
805/*!
806 Releases a thread previously reserved with reserveThread() and uses it
807 to run \a runnable.
808
809 Note that the thread pool takes ownership of the \a runnable if
810 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c true,
811 and the \a runnable will be deleted automatically by the thread
812 pool after the \l{QRunnable::run()}{runnable->run()} returns. If
813 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c false,
814 ownership of \a runnable remains with the caller. Note that
815 changing the auto-deletion on \a runnable after calling this
816 functions results in undefined behavior.
817
818 \note Calling this when no threads are reserved results in
819 undefined behavior.
820
821 \since 6.3
822 \sa reserveThread(), start()
823*/
824void QThreadPool::startOnReservedThread(QRunnable *runnable)
825{
826 if (!runnable)
827 return releaseThread();
828
829 Q_D(QThreadPool);
830 QMutexLocker locker(&d->mutex);
831 Q_ASSERT(d->reservedThreads > 0);
832 --d->reservedThreads;
833
834 if (!d->tryStart(runnable)) {
835 // This can only happen if we reserved max threads,
836 // and something took the one minimum thread.
837 d->enqueueTask(runnable, INT_MAX);
838 }
839}
840
841/*!
842 \fn template<typename Callable, QRunnable::if_callable<Callable>> void QThreadPool::startOnReservedThread(Callable &&callableToRun)
843 \overload
844 \since 6.3
845
846 Releases a thread previously reserved with reserveThread() and uses it
847 to run \a callableToRun.
848
849 \note In Qt version prior to 6.6, this function took std::function<void()>,
850 and therefore couldn't handle move-only callables.
851
852 \constraints \c Callable
853 is a function or function object which can be called with zero arguments.
854*/
855
856/*!
857 \fn bool QThreadPool::waitForDone(int msecs)
858 Waits up to \a msecs milliseconds for all threads to exit and removes all
859 threads from the thread pool. Returns \c true if all threads were removed;
860 otherwise it returns \c false. If \a msecs is -1, this function waits for
861 the last thread to exit.
862*/
863
864/*!
865 \since 6.8
866
867 Waits until \a deadline expires for all threads to exit and removes all
868 threads from the thread pool. Returns \c true if all threads were removed;
869 otherwise it returns \c false.
870*/
871bool QThreadPool::waitForDone(QDeadlineTimer deadline)
872{
873 Q_D(QThreadPool);
874 return d->waitForDone(deadline);
875}
876
877/*!
878 \since 5.2
879
880 Removes the runnables that are not yet started from the queue.
881 The runnables for which \l{QRunnable::autoDelete()}{runnable->autoDelete()}
882 returns \c true are deleted.
883
884 \sa start()
885*/
886void QThreadPool::clear()
887{
888 Q_D(QThreadPool);
889 d->clear();
890}
891
892/*!
893 \since 6.0
894
895 Returns \c true if \a thread is a thread managed by this thread pool.
896*/
897bool QThreadPool::contains(const QThread *thread) const
898{
899 Q_D(const QThreadPool);
900 const QThreadPoolThread *poolThread = qobject_cast<const QThreadPoolThread *>(thread);
901 if (!poolThread)
902 return false;
903 QMutexLocker locker(&d->mutex);
904 return d->allThreads.contains(const_cast<QThreadPoolThread *>(poolThread));
905}
906
907QT_END_NAMESPACE
908
909#include "moc_qthreadpool.cpp"
910#include "qthreadpool.moc"
bool areAllThreadsActive() const
bool tryStart(QRunnable *task)
void enqueueTask(QRunnable *task, int priority=0)
static void destroyGlobalThreadPool()
int activeThreadCount() const
int maxThreadCount() const
void startThread(QRunnable *runnable=nullptr)
void stealAndRunRunnable(QRunnable *runnable)
bool waitForDone(const QDeadlineTimer &timer)
bool tooManyThreadsActive() const
void run() override
QThreadPoolPrivate * manager
void registerThreadInactive()
QRunnable * runnable
QWaitCondition runnableReady
bool comparePriority(int priority, const QueuePage *p)
QThreadPoolPrivate::QueuePage QueuePage