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 | |
12 | QT_BEGIN_NAMESPACE |
13 | |
14 | using namespace Qt::StringLiterals; |
15 | |
16 | /* |
17 | QThread wrapper, provides synchronization against a ThreadPool |
18 | */ |
19 | class QThreadPoolThread : public QThread |
20 | { |
21 | Q_OBJECT |
22 | public: |
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 | */ |
40 | QThreadPoolThread::QThreadPoolThread(QThreadPoolPrivate *manager) |
41 | :manager(manager), runnable(nullptr) |
42 | { |
43 | setStackSize(manager->stackSize); |
44 | } |
45 | |
46 | /* |
47 | \internal |
48 | */ |
49 | void 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 | |
128 | void QThreadPoolThread::registerThreadInactive() |
129 | { |
130 | if (--manager->activeThreads == 0) |
131 | manager->noActiveThreads.wakeAll(); |
132 | } |
133 | |
134 | |
135 | /* |
136 | \internal |
137 | */ |
138 | QThreadPoolPrivate:: QThreadPoolPrivate() |
139 | { } |
140 | |
141 | bool 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 | |
183 | inline bool comparePriority(int priority, const QueuePage *p) |
184 | { |
185 | return p->priority() < priority; |
186 | } |
187 | |
188 | void 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 | |
201 | int QThreadPoolPrivate::activeThreadCount() const |
202 | { |
203 | return (allThreads.size() |
204 | - expiredThreads.size() |
205 | - waitingThreads.size() |
206 | + reservedThreads); |
207 | } |
208 | |
209 | void 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 | |
226 | bool QThreadPoolPrivate::areAllThreadsActive() const |
227 | { |
228 | const int activeThreadCount = this->activeThreadCount(); |
229 | return activeThreadCount >= maxThreadCount() && (activeThreadCount - reservedThreads) >= 1; |
230 | } |
231 | |
232 | bool QThreadPoolPrivate::tooManyThreadsActive() const |
233 | { |
234 | const int activeThreadCount = this->activeThreadCount(); |
235 | return activeThreadCount > maxThreadCount() && (activeThreadCount - reservedThreads) > 1; |
236 | } |
237 | |
238 | /*! |
239 | \internal |
240 | */ |
241 | void 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 | */ |
263 | void 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 | */ |
288 | bool 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 | |
296 | bool 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 | |
309 | void 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 | */ |
343 | bool 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 | */ |
370 | void 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 | */ |
440 | QThreadPool::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 | */ |
455 | QThreadPool::~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 | */ |
466 | QThreadPool *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 | */ |
481 | QThreadPool *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 | */ |
507 | void 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 | */ |
552 | bool 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 | |
597 | int QThreadPool::expiryTimeout() const |
598 | { |
599 | Q_D(const QThreadPool); |
600 | QMutexLocker locker(&d->mutex); |
601 | return d->expiryTimeout; |
602 | } |
603 | |
604 | void 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 | |
625 | int QThreadPool::maxThreadCount() const |
626 | { |
627 | Q_D(const QThreadPool); |
628 | QMutexLocker locker(&d->mutex); |
629 | return d->requestedMaxThreadCount; |
630 | } |
631 | |
632 | void 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 | |
654 | int 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 | */ |
676 | void 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 | */ |
695 | void QThreadPool::setStackSize(uint stackSize) |
696 | { |
697 | Q_D(QThreadPool); |
698 | QMutexLocker locker(&d->mutex); |
699 | d->stackSize = stackSize; |
700 | } |
701 | |
702 | uint 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 | |
723 | void QThreadPool::setThreadPriority(QThread::Priority priority) |
724 | { |
725 | Q_D(QThreadPool); |
726 | QMutexLocker locker(&d->mutex); |
727 | d->threadPriority = priority; |
728 | } |
729 | |
730 | QThread::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 | */ |
749 | void 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 | */ |
776 | void 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 | */ |
814 | bool 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 | */ |
829 | void 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 | */ |
840 | bool 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 | |
850 | QT_END_NAMESPACE |
851 | |
852 | #include "moc_qthreadpool.cpp" |
853 | #include "qthreadpool.moc" |
854 | |