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