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