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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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