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

source code of qtbase/src/corelib/thread/qthreadpool.cpp