source: trunk/src/corelib/concurrent/qthreadpool.cpp@ 440

Last change on this file since 440 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 17.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qthreadpool.h"
43#include "qthreadpool_p.h"
44
45#ifndef QT_NO_THREAD
46
47QT_BEGIN_NAMESPACE
48
49inline bool operator<(int priority, const QPair<QRunnable *, int> &p)
50{
51 return p.second < priority;
52}
53inline bool operator<(const QPair<QRunnable *, int> &p, int priority)
54{
55 return priority < p.second;
56}
57
58Q_GLOBAL_STATIC(QThreadPool, theInstance)
59
60/*
61 QThread wrapper, provides synchronizitaion against a ThreadPool
62*/
63class QThreadPoolThread : public QThread
64{
65public:
66 QThreadPoolThread(QThreadPoolPrivate *manager);
67 void run();
68 void registerTheadInactive();
69
70 QThreadPoolPrivate *manager;
71 QRunnable *runnable;
72};
73
74/*
75 QThreadPool private class.
76*/
77
78
79/*!\internal
80
81*/
82QThreadPoolThread::QThreadPoolThread(QThreadPoolPrivate *manager)
83 :manager(manager), runnable(0)
84{ }
85
86/* \internal
87
88*/
89void QThreadPoolThread::run()
90{
91 QMutexLocker locker(&manager->mutex);
92 for(;;) {
93 QRunnable *r = runnable;
94 runnable = 0;
95
96 do {
97 if (r) {
98 const bool autoDelete = r->autoDelete();
99
100
101 // run the task
102 locker.unlock();
103#ifndef QT_NO_EXCEPTIONS
104 try {
105#endif
106 r->run();
107#ifndef QT_NO_EXCEPTIONS
108 } catch (...) {
109 qWarning("Qt Concurrent has caught an exception thrown from a worker thread.\n"
110 "This is not supported, exceptions thrown in worker threads must be\n"
111 "caught before control returns to Qt Concurrent.");
112 registerTheadInactive();
113 throw;
114 }
115#endif
116 locker.relock();
117
118 if (autoDelete && !--r->ref)
119 delete r;
120 }
121
122 // if too many threads are active, expire this thread
123 if (manager->tooManyThreadsActive())
124 break;
125
126 r = !manager->queue.isEmpty() ? manager->queue.takeFirst().first : 0;
127 } while (r != 0);
128
129 if (manager->isExiting) {
130 registerTheadInactive();
131 break;
132 }
133
134 // if too many threads are active, expire this thread
135 bool expired = manager->tooManyThreadsActive();
136 if (!expired) {
137 ++manager->waitingThreads;
138 registerTheadInactive();
139 // wait for work, exiting after the expiry timeout is reached
140 expired = !manager->runnableReady.wait(locker.mutex(), manager->expiryTimeout);
141 ++manager->activeThreads;
142
143 if (expired)
144 --manager->waitingThreads;
145 }
146 if (expired) {
147 manager->expiredThreads.enqueue(this);
148 registerTheadInactive();
149 break;
150 }
151 }
152}
153
154void QThreadPoolThread::registerTheadInactive()
155{
156 if (--manager->activeThreads == 0)
157 manager->noActiveThreads.wakeAll();
158}
159
160
161/* \internal
162
163*/
164QThreadPoolPrivate:: QThreadPoolPrivate()
165 : isExiting(false),
166 expiryTimeout(30000),
167 maxThreadCount(qAbs(QThread::idealThreadCount())),
168 reservedThreads(0),
169 waitingThreads(0),
170 activeThreads(0)
171{ }
172
173bool QThreadPoolPrivate::tryStart(QRunnable *task)
174{
175 if (allThreads.isEmpty()) {
176 // always create at least one thread
177 startThread(task);
178 return true;
179 }
180
181 // can't do anything if we're over the limit
182 if (activeThreadCount() >= maxThreadCount)
183 return false;
184
185 if (waitingThreads > 0) {
186 // recycle an available thread
187 --waitingThreads;
188 enqueueTask(task);
189 return true;
190 }
191
192 if (!expiredThreads.isEmpty()) {
193 // restart an expired thread
194 QThreadPoolThread *thread = expiredThreads.dequeue();
195 Q_ASSERT(thread->runnable == 0);
196
197 ++activeThreads;
198
199 if (task->autoDelete())
200 ++task->ref;
201 thread->runnable = task;
202 thread->start();
203 return true;
204 }
205
206 // start a new thread
207 startThread(task);
208 return true;
209}
210
211void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
212{
213 if (runnable->autoDelete())
214 ++runnable->ref;
215
216 // put it on the queue
217 QList<QPair<QRunnable *, int> >::iterator at =
218 qUpperBound(queue.begin(), queue.end(), priority);
219 queue.insert(at, qMakePair(runnable, priority));
220 runnableReady.wakeOne();
221}
222
223int QThreadPoolPrivate::activeThreadCount() const
224{
225 return (allThreads.count()
226 - expiredThreads.count()
227 - waitingThreads
228 + reservedThreads);
229}
230
231void QThreadPoolPrivate::tryToStartMoreThreads()
232{
233 // try to push tasks on the queue to any available threads
234 while (!queue.isEmpty() && tryStart(queue.first().first))
235 queue.removeFirst();
236}
237
238bool QThreadPoolPrivate::tooManyThreadsActive() const
239{
240 const int activeThreadCount = this->activeThreadCount();
241 return activeThreadCount > maxThreadCount && (activeThreadCount - reservedThreads) > 1;
242}
243
244/*! \internal
245
246*/
247void QThreadPoolPrivate::startThread(QRunnable *runnable)
248{
249 QThreadPoolThread *thread = new QThreadPoolThread(this);
250 allThreads.insert(thread);
251 ++activeThreads;
252
253 if (runnable->autoDelete())
254 ++runnable->ref;
255 thread->runnable = runnable;
256 thread->start();
257}
258
259/*! \internal
260 Makes all threads exit, waits for each tread to exit and deletes it.
261*/
262void QThreadPoolPrivate::reset()
263{
264 QMutexLocker locker(&mutex);
265 isExiting = true;
266 runnableReady.wakeAll();
267
268 do {
269 // make a copy of the set so that we can iterate without the lock
270 QSet<QThreadPoolThread *> allThreadsCopy = allThreads;
271 allThreads.clear();
272 locker.unlock();
273
274 foreach (QThreadPoolThread *thread, allThreadsCopy) {
275 thread->wait();
276 delete thread;
277 }
278
279 locker.relock();
280 // repeat until all newly arrived threads have also completed
281 } while (!allThreads.isEmpty());
282
283 waitingThreads = 0;
284 expiredThreads.clear();
285
286 isExiting = false;
287}
288
289void QThreadPoolPrivate::waitForDone()
290{
291 QMutexLocker locker(&mutex);
292 while (!(queue.isEmpty() && activeThreads == 0))
293 noActiveThreads.wait(locker.mutex());
294}
295
296/*! \internal
297 Pulls a runnable from the front queue and runs it in the current thread. Blocks
298 until the runnable has completed. Returns true if a runnable was found.
299*/
300bool QThreadPoolPrivate::startFrontRunnable()
301{
302 QMutexLocker locker(&mutex);
303 if (queue.isEmpty())
304 return false;
305
306 QRunnable *runnable = queue.takeFirst().first;
307 const bool autoDelete = runnable->autoDelete();
308 bool del = autoDelete && !--runnable->ref;
309
310 locker.unlock();
311 runnable->run();
312 locker.relock();
313
314 if (del) {
315 delete runnable;
316 }
317
318 return true;
319}
320
321/*! \internal
322 Seaches for \a runnable in the queue, removes it from the queue and
323 runs it if found. This functon does not return until the runnable
324 has completed.
325*/
326void QThreadPoolPrivate::stealRunnable(QRunnable *runnable)
327{
328 if (runnable == 0 || queue.isEmpty())
329 return;
330 bool found = false;
331 {
332 QMutexLocker locker(&mutex);
333 QList<QPair<QRunnable *, int> >::iterator it = queue.begin();
334 QList<QPair<QRunnable *, int> >::iterator end = queue.end();
335
336 while (it != end) {
337 if (it->first == runnable) {
338 found = true;
339 queue.erase(it);
340 break;
341 }
342 ++it;
343 }
344 }
345
346 if (!found)
347 return;
348
349 const bool autoDelete = runnable->autoDelete();
350 bool del = autoDelete && !--runnable->ref;
351
352 runnable->run();
353
354 if (del) {
355 delete runnable;
356 }
357}
358
359/*!
360 \class QThreadPool
361 \brief The QThreadPool class manages a collection of QThreads.
362 \since 4.4
363 \threadsafe
364
365 QThreadPool manages and recyles individual QThread objects to help reduce
366 thread creation costs in programs that use threads. Each Qt application
367 has one global QThreadPool object, which can be accessed by calling
368 globalInstance().
369
370 To use one of the QThreadPool threads, subclass QRunnable and implement
371 the run() virtual function. Then create an object of that class and pass
372 it to QThreadPool::start().
373
374 \snippet doc/src/snippets/code/src_corelib_concurrent_qthreadpool.cpp 0
375
376 QThreadPool deletes the QRunnable automatically by default. Use
377 QRunnable::setAutoDelete() to change the auto-deletion flag.
378
379 QThreadPool supports executing the same QRunnable more than once
380 by calling tryStart(this) from within QRunnable::run().
381 If autoDelete is enabled the QRunnable will be deleted when
382 the last thread exits the run function. Calling start()
383 multiple times with the same QRunnable when autoDelete is enabled
384 creates a race condition and is not recommended.
385
386 Threads that are unused for a certain amount of time will expire. The
387 default expiry timeout is 30000 milliseconds (30 seconds). This can be
388 changed using setExpiryTimeout(). Setting a negative expiry timeout
389 disables the expiry mechanism.
390
391 Call maxThreadCount() to query the maximum number of threads to be used.
392 If needed, you can change the limit with setMaxThreadCount(). The default
393 maxThreadCount() is QThread::idealThreadCount(). The activeThreadCount()
394 function returns the number of threads currently doing work.
395
396 The reserveThread() function reserves a thread for external
397 use. Use releaseThread() when your are done with the thread, so
398 that it may be reused. Essentially, these functions temporarily
399 increase or reduce the active thread count and are useful when
400 implementing time-consuming operations that are not visible to the
401 QThreadPool.
402
403 Note that QThreadPool is a low-level class for managing threads, see
404 QtConcurrent::run() or the other
405 \l {threads.html#qtconcurrent-intro}{Qt Concurrent} APIs for higher
406 level alternatives.
407
408 \sa QRunnable
409*/
410
411/*!
412 Constructs a thread pool with the given \a parent.
413*/
414QThreadPool::QThreadPool(QObject *parent)
415 : QObject(*new QThreadPoolPrivate, parent)
416{ }
417
418/*!
419 Destroys the QThreadPool.
420 This function will block until all runnables have been completed.
421*/
422QThreadPool::~QThreadPool()
423{
424 d_func()->waitForDone();
425 d_func()->reset();
426}
427
428/*!
429 Returns the global QThreadPool instance.
430*/
431QThreadPool *QThreadPool::globalInstance()
432{
433 return theInstance();
434}
435
436/*!
437 Reserves a thread and uses it to run \a runnable, unless this thread will
438 make the current thread count exceed maxThreadCount(). In that case,
439 \a runnable is added to a run queue instead. The \a priority argument can
440 be used to control the run queue's order of execution.
441
442 Note that the thread pool takes ownership of the \a runnable if
443 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns true,
444 and the \a runnable will be deleted automatically by the thread
445 pool after the \l{QRunnable::run()}{runnable->run()} returns. If
446 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns false,
447 ownership of \a runnable remains with the caller. Note that
448 changing the auto-deletion on \a runnable after calling this
449 functions results in undefined behavior.
450*/
451void QThreadPool::start(QRunnable *runnable, int priority)
452{
453 if (!runnable)
454 return;
455
456 Q_D(QThreadPool);
457 QMutexLocker locker(&d->mutex);
458 if (!d->tryStart(runnable))
459 d->enqueueTask(runnable, priority);
460}
461
462/*!
463 Attempts to reserve a thread to run \a runnable.
464
465 If no threads are available at the time of calling, then this function
466 does nothing and returns false. Otherwise, \a runnable is run immediately
467 using one available thread and this function returns true.
468
469 Note that the thread pool takes ownership of the \a runnable if
470 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns true,
471 and the \a runnable will be deleted automatically by the thread
472 pool after the \l{QRunnable::run()}{runnable->run()} returns. If
473 \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns false,
474 ownership of \a runnable remains with the caller. Note that
475 changing the auto-deletion on \a runnable after calling this
476 function results in undefined behavior.
477*/
478bool QThreadPool::tryStart(QRunnable *runnable)
479{
480 if (!runnable)
481 return false;
482
483 Q_D(QThreadPool);
484 QMutexLocker locker(&d->mutex);
485 return d->tryStart(runnable);
486}
487
488/*! \property QThreadPool::expiryTimeout
489
490 Threads that are unused for \a expiryTimeout milliseconds are considered
491 to have expired and will exit. Such threads will be restarted as needed.
492 The default \a expiryTimeout is 30000 milliseconds (30 seconds). If
493 \a expiryTimeout is negative, newly created threads will not expire, e.g.,
494 they will not exit until the thread pool is destroyed.
495
496 Note that setting \a expiryTimeout has no effect on already running
497 threads. Only newly created threads will use the new \a expiryTimeout.
498 We recommend setting the \a expiryTimeout immediately after creating the
499 thread pool, but before calling start().
500*/
501
502int QThreadPool::expiryTimeout() const
503{
504 Q_D(const QThreadPool);
505 return d->expiryTimeout;
506}
507
508void QThreadPool::setExpiryTimeout(int expiryTimeout)
509{
510 Q_D(QThreadPool);
511 if (d->expiryTimeout == expiryTimeout)
512 return;
513 d->expiryTimeout = expiryTimeout;
514}
515
516/*! \property QThreadPool::maxThreadCount
517
518 This property represents the maximum number of threads used by the thread
519 pool.
520
521 \note The thread pool will always use at least 1 thread, even if
522 \a maxThreadCount limit is zero or negative.
523
524 The default \a maxThreadCount is QThread::idealThreadCount().
525*/
526
527int QThreadPool::maxThreadCount() const
528{
529 Q_D(const QThreadPool);
530 QMutexLocker locker(&d->mutex);
531 return d->maxThreadCount;
532}
533
534void QThreadPool::setMaxThreadCount(int maxThreadCount)
535{
536 Q_D(QThreadPool);
537 QMutexLocker locker(&d->mutex);
538
539 if (maxThreadCount == d->maxThreadCount)
540 return;
541
542 d->maxThreadCount = maxThreadCount;
543 d->tryToStartMoreThreads();
544}
545
546/*! \property QThreadPool::activeThreadCount
547
548 This property represents the number of active threads in the thread pool.
549
550 \note It is possible for this function to return a value that is greater
551 than maxThreadCount(). See reserveThread() for more details.
552
553 \sa reserveThread(), releaseThread()
554*/
555
556int QThreadPool::activeThreadCount() const
557{
558 Q_D(const QThreadPool);
559 QMutexLocker locker(&d->mutex);
560 return d->activeThreadCount();
561}
562
563/*!
564 Reserves one thread, disregarding activeThreadCount() and maxThreadCount().
565
566 Once you are done with the thread, call releaseThread() to allow it to be
567 reused.
568
569 \note This function will always increase the number of active threads.
570 This means that by using this function, it is possible for
571 activeThreadCount() to return a value greater than maxThreadCount() .
572
573 \sa releaseThread()
574 */
575void QThreadPool::reserveThread()
576{
577 Q_D(QThreadPool);
578 QMutexLocker locker(&d->mutex);
579 ++d->reservedThreads;
580}
581
582/*!
583 Releases a thread previously reserved by a call to reserveThread().
584
585 \note Calling this function without previously reserving a thread
586 temporarily increases maxThreadCount(). This is useful when a
587 thread goes to sleep waiting for more work, allowing other threads
588 to continue. Be sure to call reserveThread() when done waiting, so
589 that the thread pool can correctly maintain the
590 activeThreadCount().
591
592 \sa reserveThread()
593*/
594void QThreadPool::releaseThread()
595{
596 Q_D(QThreadPool);
597 QMutexLocker locker(&d->mutex);
598 --d->reservedThreads;
599 d->tryToStartMoreThreads();
600}
601
602/*!
603 Waits for each thread to exit and removes all threads from the thread pool.
604*/
605void QThreadPool::waitForDone()
606{
607 Q_D(QThreadPool);
608 d->waitForDone();
609 d->reset();
610}
611
612QT_END_NAMESPACE
613
614#endif
Note: See TracBrowser for help on using the repository browser.