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 <QtCore/qpointer.h>
10
11#include <algorithm>
12#include <climits> // For INT_MAX
13#include <memory>
14
15using namespace std::chrono_literals;
16
17QT_BEGIN_NAMESPACE
18
19using namespace Qt::StringLiterals;
20
21/*
22 QThread wrapper, provides synchronization against a ThreadPool
23*/
24class QThreadPoolThread : public QThread
25{
26 Q_OBJECT
27public:
28 QThreadPoolThread(QThreadPoolPrivate *manager);
29 void run() override;
30 void registerThreadInactive();
31
32 QWaitCondition runnableReady;
33 QThreadPoolPrivate *manager;
34 QRunnable *runnable;
35};
36
37/*
38 QThreadPool private class.
39*/
40
41
42/*!
43 \internal
44*/
45QThreadPoolThread::QThreadPoolThread(QThreadPoolPrivate *manager)
46 :manager(manager), runnable(nullptr)
47{
48 setStackSize(manager->stackSize);
49}
50
51/*
52 \internal
53*/
54void QThreadPoolThread::run()
55{
56 QMutexLocker locker(&manager->mutex);
57 for(;;) {
58 QRunnable *r = runnable;
59 runnable = nullptr;
60
61 do {
62 if (r) {
63 // If autoDelete() is false, r might already be deleted after run(), so check status now.
64 const bool del = r->autoDelete();
65
66 // run the task
67 locker.unlock();
68#ifndef QT_NO_EXCEPTIONS
69 try {
70#endif
71 r->run();
72#ifndef QT_NO_EXCEPTIONS
73 } catch (...) {
74 qWarning(msg: "Qt Concurrent has caught an exception thrown from a worker thread.\n"
75 "This is not supported, exceptions thrown in worker threads must be\n"
76 "caught before control returns to Qt Concurrent.");
77 registerThreadInactive();
78 throw;
79 }
80#endif
81
82 if (del)
83 delete r;
84 locker.relock();
85 }
86
87 // if too many threads are active, stop working in this one
88 if (manager->tooManyThreadsActive())
89 break;
90
91 // all work is done, time to wait for more
92 if (manager->queue.isEmpty())
93 break;
94
95 QueuePage *page = manager->queue.constFirst();
96 r = page->pop();
97
98 if (page->isFinished()) {
99 manager->queue.removeFirst();
100 delete page;
101 }
102 } while (true);
103
104 // this thread is about to be deleted, do not wait or expire
105 if (!manager->allThreads.contains(value: this)) {
106 registerThreadInactive();
107 return;
108 }
109
110 // if too many threads are active, expire this thread
111 if (manager->tooManyThreadsActive()) {
112 manager->expiredThreads.enqueue(t: this);
113 registerThreadInactive();
114 return;
115 }
116 manager->waitingThreads.enqueue(t: this);
117 registerThreadInactive();
118 // wait for work, exiting after the expiry timeout is reached
119 runnableReady.wait(lockedMutex: locker.mutex(), deadline: QDeadlineTimer(manager->expiryTimeout));
120 // this thread is about to be deleted, do not work or expire
121 if (!manager->allThreads.contains(value: this)) {
122 Q_ASSERT(manager->queue.isEmpty());
123 return;
124 }
125 if (manager->waitingThreads.removeOne(t: this)) {
126 manager->expiredThreads.enqueue(t: this);
127 return;
128 }
129 ++manager->activeThreads;
130 }
131}
132
133void QThreadPoolThread::registerThreadInactive()
134{
135 if (--manager->activeThreads == 0)
136 manager->noActiveThreads.wakeAll();
137}
138
139
140/*
141 \internal
142*/
143QThreadPoolPrivate:: QThreadPoolPrivate()
144{ }
145
146bool QThreadPoolPrivate::tryStart(QRunnable *task)
147{
148 Q_ASSERT(task != nullptr);
149 if (allThreads.isEmpty()) {
150 // always create at least one thread
151 startThread(runnable: task);
152 return true;
153 }
154
155 // can't do anything if we're over the limit
156 if (areAllThreadsActive())
157 return false;
158
159 if (!waitingThreads.isEmpty()) {
160 // recycle an available thread
161 enqueueTask(task);
162 waitingThreads.takeFirst()->runnableReady.wakeOne();
163 return true;
164 }
165
166 if (!expiredThreads.isEmpty()) {
167 // restart an expired thread
168 QThreadPoolThread *thread = expiredThreads.dequeue();
169 Q_ASSERT(thread->runnable == nullptr);
170
171 ++activeThreads;
172
173 thread->runnable = task;
174
175 // Ensure that the thread has actually finished, otherwise the following
176 // start() has no effect.
177 thread->wait();
178 Q_ASSERT(thread->isFinished());
179 thread->start(threadPriority);
180 return true;
181 }
182
183 // start a new thread
184 startThread(runnable: task);
185 return true;
186}
187
188inline bool comparePriority(int priority, const QueuePage *p)
189{
190 return p->priority() < priority;
191}
192
193void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
194{
195 Q_ASSERT(runnable != nullptr);
196 for (QueuePage *page : std::as_const(t&: queue)) {
197 if (page->priority() == priority && !page->isFull()) {
198 page->push(runnable);
199 return;
200 }
201 }
202 auto it = std::upper_bound(first: queue.constBegin(), last: queue.constEnd(), val: priority, comp: comparePriority);
203 queue.insert(i: std::distance(first: queue.constBegin(), last: it), t: new QueuePage(runnable, priority));
204}
205
206int QThreadPoolPrivate::activeThreadCount() const
207{
208 return (allThreads.size()
209 - expiredThreads.size()
210 - waitingThreads.size()
211 + reservedThreads);
212}
213
214void QThreadPoolPrivate::tryToStartMoreThreads()
215{
216 // try to push tasks on the queue to any available threads
217 while (!queue.isEmpty()) {
218 QueuePage *page = queue.constFirst();
219 if (!tryStart(task: page->first()))
220 break;
221
222 page->pop();
223
224 if (page->isFinished()) {
225 queue.removeFirst();
226 delete page;
227 }
228 }
229}
230
231bool QThreadPoolPrivate::areAllThreadsActive() const
232{
233 const int activeThreadCount = this->activeThreadCount();
234 return activeThreadCount >= maxThreadCount() && (activeThreadCount - reservedThreads) >= 1;
235}
236
237bool QThreadPoolPrivate::tooManyThreadsActive() const
238{
239 const int activeThreadCount = this->activeThreadCount();
240 return activeThreadCount > maxThreadCount() && (activeThreadCount - reservedThreads) > 1;
241}
242
243/*!
244 \internal
245*/
246void QThreadPoolPrivate::startThread(QRunnable *runnable)
247{
248 Q_ASSERT(runnable != nullptr);
249 auto thread = std::make_unique<QThreadPoolThread>(args: this);
250 if (objectName.isEmpty())
251 objectName = u"Thread (pooled)"_s;
252 thread->setObjectName(objectName);
253 Q_ASSERT(!allThreads.contains(thread.get())); // if this assert hits, we have an ABA problem (deleted threads don't get removed here)
254 allThreads.insert(value: thread.get());
255 ++activeThreads;
256
257 thread->runnable = runnable;
258 thread.release()->start(threadPriority);
259}
260
261/*!
262 \internal
263
264 Helper function only to be called from waitForDone()
265
266 Deletes all current threads.
267*/
268void QThreadPoolPrivate::reset()
269{
270 // move the contents of the set out so that we can iterate without the lock
271 auto allThreadsCopy = std::exchange(obj&: allThreads, new_val: {});
272 expiredThreads.clear();
273 waitingThreads.clear();
274
275 mutex.unlock();
276
277 for (QThreadPoolThread *thread : std::as_const(t&: allThreadsCopy)) {
278 if (thread->isRunning()) {
279 thread->runnableReady.wakeAll();
280 thread->wait();
281 }
282 delete thread;
283 }
284
285 mutex.lock();
286}
287
288/*!
289 \internal
290
291 Helper function only to be called from the public waitForDone()
292*/
293bool QThreadPoolPrivate::waitForDone(const QDeadlineTimer &timer)
294{
295 QMutexLocker locker(&mutex);
296 while (!(queue.isEmpty() && activeThreads == 0) && !timer.hasExpired())
297 noActiveThreads.wait(lockedMutex: &mutex, deadline: timer);
298
299 if (!queue.isEmpty() || activeThreads)
300 return false;
301
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 const static bool runtime_disable = qEnvironmentVariableIsSet(varName: "QT_NO_GUI_THREADPOOL");
486 if (runtime_disable)
487 return nullptr;
488 const QMutexLocker locker(&theMutex);
489 if (guiInstance.isNull() && !QCoreApplication::closingDown()) {
490 guiInstance = new QThreadPool();
491 // Limit max thread to avoid too many parallel threads.
492 // We are not optimized for much more than 4 or 8 threads.
493 if (guiInstance && guiInstance->maxThreadCount() > 4)
494 guiInstance->setMaxThreadCount(qBound(min: 4, val: guiInstance->maxThreadCount() / 2, max: 8));
495 }
496 return guiInstance;
497}
498
499/*!
500 Reserves a thread and uses it to run \a runnable, unless this thread will
501 make the current thread count exceed maxThreadCount(). In that case,
502 \a runnable is added to a run queue instead. The \a priority argument can
503 be used to control the run queue's order of execution.
504
505 Note that the thread pool takes ownership of the \a runnable if
506 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c true,
507 and the \a runnable will be deleted automatically by the thread
508 pool after the \l{QRunnable::run()}{runnable->run()} returns. If
509 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c false,
510 ownership of \a runnable remains with the caller. Note that
511 changing the auto-deletion on \a runnable after calling this
512 functions results in undefined behavior.
513*/
514void QThreadPool::start(QRunnable *runnable, int priority)
515{
516 if (!runnable)
517 return;
518
519 Q_D(QThreadPool);
520 QMutexLocker locker(&d->mutex);
521
522 if (!d->tryStart(task: runnable))
523 d->enqueueTask(runnable, priority);
524}
525
526/*!
527 \fn template<typename Callable, QRunnable::if_callable<Callable>> void QThreadPool::start(Callable &&callableToRun, int priority)
528 \overload
529 \since 5.15
530
531 Reserves a thread and uses it to run \a callableToRun, unless this thread will
532 make the current thread count exceed maxThreadCount(). In that case,
533 \a callableToRun is added to a run queue instead. The \a priority argument can
534 be used to control the run queue's order of execution.
535
536 \note This function participates in overload resolution only if \c Callable
537 is a function or function object which can be called with zero arguments.
538
539 \note In Qt version prior to 6.6, this function took std::function<void()>,
540 and therefore couldn't handle move-only callables.
541*/
542
543/*!
544 Attempts to reserve a thread to run \a runnable.
545
546 If no threads are available at the time of calling, then this function
547 does nothing and returns \c false. Otherwise, \a runnable is run immediately
548 using one available thread and this function returns \c true.
549
550 Note that on success the thread pool takes ownership of the \a runnable if
551 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c true,
552 and the \a runnable will be deleted automatically by the thread
553 pool after the \l{QRunnable::run()}{runnable->run()} returns. If
554 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c false,
555 ownership of \a runnable remains with the caller. Note that
556 changing the auto-deletion on \a runnable after calling this
557 function results in undefined behavior.
558*/
559bool QThreadPool::tryStart(QRunnable *runnable)
560{
561 if (!runnable)
562 return false;
563
564 Q_D(QThreadPool);
565 QMutexLocker locker(&d->mutex);
566 if (d->tryStart(task: runnable))
567 return true;
568
569 return false;
570}
571
572/*!
573 \fn template<typename Callable, QRunnable::if_callable<Callable>> bool QThreadPool::tryStart(Callable &&callableToRun)
574 \overload
575 \since 5.15
576 Attempts to reserve a thread to run \a callableToRun.
577
578 If no threads are available at the time of calling, then this function
579 does nothing and returns \c false. Otherwise, \a callableToRun is run immediately
580 using one available thread and this function returns \c true.
581
582 \note This function participates in overload resolution only if \c Callable
583 is a function or function object which can be called with zero arguments.
584
585 \note In Qt version prior to 6.6, this function took std::function<void()>,
586 and therefore couldn't handle move-only callables.
587*/
588
589/*! \property QThreadPool::expiryTimeout
590 \brief the thread expiry timeout value in milliseconds.
591
592 Threads that are unused for \e expiryTimeout milliseconds are considered
593 to have expired and will exit. Such threads will be restarted as needed.
594 The default \a expiryTimeout is 30000 milliseconds (30 seconds). If
595 \a expiryTimeout is negative, newly created threads will not expire, e.g.,
596 they will not exit until the thread pool is destroyed.
597
598 Note that setting \a expiryTimeout has no effect on already running
599 threads. Only newly created threads will use the new \a expiryTimeout.
600 We recommend setting the \a expiryTimeout immediately after creating the
601 thread pool, but before calling start().
602*/
603
604int QThreadPool::expiryTimeout() const
605{
606 using namespace std::chrono;
607 Q_D(const QThreadPool);
608 QMutexLocker locker(&d->mutex);
609 if (d->expiryTimeout == decltype(d->expiryTimeout)::max())
610 return -1;
611 return duration_cast<milliseconds>(d: d->expiryTimeout).count();
612}
613
614void QThreadPool::setExpiryTimeout(int expiryTimeout)
615{
616 Q_D(QThreadPool);
617 QMutexLocker locker(&d->mutex);
618 if (expiryTimeout < 0)
619 d->expiryTimeout = decltype(d->expiryTimeout)::max();
620 else
621 d->expiryTimeout = expiryTimeout * 1ms;
622}
623
624/*! \property QThreadPool::maxThreadCount
625
626 \brief the maximum number of threads used by the thread pool. This property
627 will default to the value of QThread::idealThreadCount() at the moment the
628 QThreadPool object is created.
629
630 \note The thread pool will always use at least 1 thread, even if
631 \a maxThreadCount limit is zero or negative.
632
633 The default \a maxThreadCount is QThread::idealThreadCount().
634*/
635
636int QThreadPool::maxThreadCount() const
637{
638 Q_D(const QThreadPool);
639 QMutexLocker locker(&d->mutex);
640 return d->requestedMaxThreadCount;
641}
642
643void QThreadPool::setMaxThreadCount(int maxThreadCount)
644{
645 Q_D(QThreadPool);
646 QMutexLocker locker(&d->mutex);
647
648 if (maxThreadCount == d->requestedMaxThreadCount)
649 return;
650
651 d->requestedMaxThreadCount = maxThreadCount;
652 d->tryToStartMoreThreads();
653}
654
655/*! \property QThreadPool::activeThreadCount
656
657 \brief the number of active threads in the thread pool.
658
659 \note It is possible for this function to return a value that is greater
660 than maxThreadCount(). See reserveThread() for more details.
661
662 \sa reserveThread(), releaseThread()
663*/
664
665int QThreadPool::activeThreadCount() const
666{
667 Q_D(const QThreadPool);
668 QMutexLocker locker(&d->mutex);
669 return d->activeThreadCount();
670}
671
672/*!
673 Reserves one thread, disregarding activeThreadCount() and maxThreadCount().
674
675 Once you are done with the thread, call releaseThread() to allow it to be
676 reused.
677
678 \note Even if reserving maxThreadCount() threads or more, the thread pool
679 will still allow a minimum of one thread.
680
681 \note This function will increase the reported number of active threads.
682 This means that by using this function, it is possible for
683 activeThreadCount() to return a value greater than maxThreadCount() .
684
685 \sa releaseThread()
686 */
687void QThreadPool::reserveThread()
688{
689 Q_D(QThreadPool);
690 QMutexLocker locker(&d->mutex);
691 ++d->reservedThreads;
692}
693
694/*! \property QThreadPool::stackSize
695 \brief the stack size for the thread pool worker threads.
696
697 The value of the property is only used when the thread pool creates
698 new threads. Changing it has no effect for already created
699 or running threads.
700
701 The default value is 0, which makes QThread use the operating
702 system default stack size.
703
704 \since 5.10
705*/
706void QThreadPool::setStackSize(uint stackSize)
707{
708 Q_D(QThreadPool);
709 QMutexLocker locker(&d->mutex);
710 d->stackSize = stackSize;
711}
712
713uint QThreadPool::stackSize() const
714{
715 Q_D(const QThreadPool);
716 QMutexLocker locker(&d->mutex);
717 return d->stackSize;
718}
719
720/*! \property QThreadPool::threadPriority
721 \brief the thread priority for new worker threads.
722
723 The value of the property is only used when the thread pool starts
724 new threads. Changing it has no effect for already running threads.
725
726 The default value is QThread::InheritPriority, which makes QThread
727 use the same priority as the one the QThreadPool object lives in.
728
729 \sa QThread::Priority
730
731 \since 6.2
732*/
733
734void QThreadPool::setThreadPriority(QThread::Priority priority)
735{
736 Q_D(QThreadPool);
737 QMutexLocker locker(&d->mutex);
738 d->threadPriority = priority;
739}
740
741QThread::Priority QThreadPool::threadPriority() const
742{
743 Q_D(const QThreadPool);
744 QMutexLocker locker(&d->mutex);
745 return d->threadPriority;
746}
747
748/*!
749 Releases a thread previously reserved by a call to reserveThread().
750
751 \note Calling this function without previously reserving a thread
752 temporarily increases maxThreadCount(). This is useful when a
753 thread goes to sleep waiting for more work, allowing other threads
754 to continue. Be sure to call reserveThread() when done waiting, so
755 that the thread pool can correctly maintain the
756 activeThreadCount().
757
758 \sa reserveThread()
759*/
760void QThreadPool::releaseThread()
761{
762 Q_D(QThreadPool);
763 QMutexLocker locker(&d->mutex);
764 --d->reservedThreads;
765 d->tryToStartMoreThreads();
766}
767
768/*!
769 Releases a thread previously reserved with reserveThread() and uses it
770 to run \a runnable.
771
772 Note that the thread pool takes ownership of the \a runnable if
773 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c true,
774 and the \a runnable will be deleted automatically by the thread
775 pool after the \l{QRunnable::run()}{runnable->run()} returns. If
776 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c false,
777 ownership of \a runnable remains with the caller. Note that
778 changing the auto-deletion on \a runnable after calling this
779 functions results in undefined behavior.
780
781 \note Calling this when no threads are reserved results in
782 undefined behavior.
783
784 \since 6.3
785 \sa reserveThread(), start()
786*/
787void QThreadPool::startOnReservedThread(QRunnable *runnable)
788{
789 if (!runnable)
790 return releaseThread();
791
792 Q_D(QThreadPool);
793 QMutexLocker locker(&d->mutex);
794 Q_ASSERT(d->reservedThreads > 0);
795 --d->reservedThreads;
796
797 if (!d->tryStart(task: runnable)) {
798 // This can only happen if we reserved max threads,
799 // and something took the one minimum thread.
800 d->enqueueTask(runnable, INT_MAX);
801 }
802}
803
804/*!
805 \fn template<typename Callable, QRunnable::if_callable<Callable>> void QThreadPool::startOnReservedThread(Callable &&callableToRun)
806 \overload
807 \since 6.3
808
809 Releases a thread previously reserved with reserveThread() and uses it
810 to run \a callableToRun.
811
812 \note This function participates in overload resolution only if \c Callable
813 is a function or function object which can be called with zero arguments.
814
815 \note In Qt version prior to 6.6, this function took std::function<void()>,
816 and therefore couldn't handle move-only callables.
817*/
818
819/*!
820 \fn bool QThreadPool::waitForDone(int msecs)
821 Waits up to \a msecs milliseconds for all threads to exit and removes all
822 threads from the thread pool. Returns \c true if all threads were removed;
823 otherwise it returns \c false. If \a msecs is -1, this function waits for
824 the last thread to exit.
825*/
826
827/*!
828 \since 6.8
829
830 Waits until \a deadline expires for all threads to exit and removes all
831 threads from the thread pool. Returns \c true if all threads were removed;
832 otherwise it returns \c false.
833*/
834bool QThreadPool::waitForDone(QDeadlineTimer deadline)
835{
836 Q_D(QThreadPool);
837 return d->waitForDone(timer: deadline);
838}
839
840/*!
841 \since 5.2
842
843 Removes the runnables that are not yet started from the queue.
844 The runnables for which \l{QRunnable::autoDelete()}{runnable->autoDelete()}
845 returns \c true are deleted.
846
847 \sa start()
848*/
849void QThreadPool::clear()
850{
851 Q_D(QThreadPool);
852 d->clear();
853}
854
855/*!
856 \since 6.0
857
858 Returns \c true if \a thread is a thread managed by this thread pool.
859*/
860bool QThreadPool::contains(const QThread *thread) const
861{
862 Q_D(const QThreadPool);
863 const QThreadPoolThread *poolThread = qobject_cast<const QThreadPoolThread *>(object: thread);
864 if (!poolThread)
865 return false;
866 QMutexLocker locker(&d->mutex);
867 return d->allThreads.contains(value: const_cast<QThreadPoolThread *>(poolThread));
868}
869
870QT_END_NAMESPACE
871
872#include "moc_qthreadpool.cpp"
873#include "qthreadpool.moc"
874

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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