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

Last change on this file since 643 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 17.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this 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 have questions regarding the use of this file, please contact
37** Nokia 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;