source: trunk/src/corelib/thread/qmutex.cpp@ 788

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

trunk: Merged in qt 4.6.2 sources.

File size: 15.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 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 "qplatformdefs.h"
43#include "qmutex.h"
44
45#ifndef QT_NO_THREAD
46#include "qatomic.h"
47#include "qthread.h"
48#include "qmutex_p.h"
49
50QT_BEGIN_NAMESPACE
51
52/*!
53 \class QMutex
54 \brief The QMutex class provides access serialization between threads.
55
56 \threadsafe
57
58 \ingroup thread
59
60 The purpose of a QMutex is to protect an object, data structure or
61 section of code so that only one thread can access it at a time
62 (this is similar to the Java \c synchronized keyword). It is
63 usually best to use a mutex with a QMutexLocker since this makes
64 it easy to ensure that locking and unlocking are performed
65 consistently.
66
67 For example, say there is a method that prints a message to the
68 user on two lines:
69
70 \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 0
71
72 If these two methods are called in succession, the following happens:
73
74 \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 1
75
76 If these two methods are called simultaneously from two threads then the
77 following sequence could result:
78
79 \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 2
80
81 If we add a mutex, we should get the result we want:
82
83 \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 3
84
85 Then only one thread can modify \c number at any given time and
86 the result is correct. This is a trivial example, of course, but
87 applies to any other case where things need to happen in a
88 particular sequence.
89
90 When you call lock() in a thread, other threads that try to call
91 lock() in the same place will block until the thread that got the
92 lock calls unlock(). A non-blocking alternative to lock() is
93 tryLock().
94
95 \sa QMutexLocker, QReadWriteLock, QSemaphore, QWaitCondition
96*/
97
98/*!
99 \enum QMutex::RecursionMode
100
101 \value Recursive In this mode, a thread can lock the same mutex
102 multiple times and the mutex won't be unlocked
103 until a corresponding number of unlock() calls
104 have been made.
105
106 \value NonRecursive In this mode, a thread may only lock a mutex
107 once.
108
109 \sa QMutex()
110*/
111
112/*!
113 Constructs a new mutex. The mutex is created in an unlocked state.
114
115 If \a mode is QMutex::Recursive, a thread can lock the same mutex
116 multiple times and the mutex won't be unlocked until a
117 corresponding number of unlock() calls have been made. The
118 default is QMutex::NonRecursive.
119
120 \sa lock(), unlock()
121*/
122QMutex::QMutex(RecursionMode mode)
123 : d(new QMutexPrivate(mode))
124{ }
125
126/*!
127 Destroys the mutex.
128
129 \warning Destroying a locked mutex may result in undefined behavior.
130*/
131QMutex::~QMutex()
132{ delete d; }
133
134/*!
135 Locks the mutex. If another thread has locked the mutex then this
136 call will block until that thread has unlocked it.
137
138 Calling this function multiple times on the same mutex from the
139 same thread is allowed if this mutex is a
140 \l{QMutex::Recursive}{recursive mutex}. If this mutex is a
141 \l{QMutex::NonRecursive}{non-recursive mutex}, this function will
142 \e dead-lock when the mutex is locked recursively.
143
144 \sa unlock()
145*/
146void QMutex::lock()
147{
148 Qt::HANDLE self;
149
150 if (d->recursive) {
151 self = QThread::currentThreadId();
152 if (d->owner == self) {
153 ++d->count;
154 Q_ASSERT_X(d->count != 0, "QMutex::lock", "Overflow in recursion counter");
155 return;
156 }
157
158 bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0;
159 if (!isLocked) {
160#ifndef QT_NO_DEBUG
161 if (d->owner == self)
162 qWarning("QMutex::lock: Deadlock detected in thread %ld",
163 long(d->owner));
164#endif
165
166 // didn't get the lock, wait for it
167 isLocked = d->wait();
168 Q_ASSERT_X(isLocked, "QMutex::lock",
169 "Internal error, infinite wait has timed out.");
170
171 // don't need to wait for the lock anymore
172 d->contenders.deref();
173 }
174
175 d->owner = self;
176 ++d->count;
177 Q_ASSERT_X(d->count != 0, "QMutex::lock", "Overflow in recursion counter");
178 return;
179 }
180
181#ifndef QT_NO_DEBUG
182 self = QThread::currentThreadId();
183#endif
184
185 bool isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1);
186 if (!isLocked) {
187 int spinCount = 0;
188 int lastSpinCount = d->lastSpinCount;
189
190 enum { AdditionalSpins = 20, SpinCountPenalizationDivisor = 4 };
191 const int maximumSpinCount = lastSpinCount + AdditionalSpins;
192
193 do {
194 if (spinCount++ > maximumSpinCount) {
195 // puts("spinning useless, sleeping");
196 isLocked = d->contenders.fetchAndAddAcquire(1) == 0;
197 if (!isLocked) {
198#ifndef QT_NO_DEBUG
199 if (d->owner == self)
200 qWarning("QMutex::lock: Deadlock detected in thread %ld",
201 long(d->owner));
202#endif
203
204 // didn't get the lock, wait for it
205 isLocked = d->wait();
206 Q_ASSERT_X(isLocked, "QMutex::lock",
207 "Internal error, infinite wait has timed out.");
208
209 // don't need to wait for the lock anymore
210 d->contenders.deref();
211 }
212 // decrease the lastSpinCount since we didn't actually get the lock by spinning
213 spinCount = -d->lastSpinCount / SpinCountPenalizationDivisor;
214 break;
215 }
216
217 isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1);
218 } while (!isLocked);
219
220 // adjust the last spin lock count
221 lastSpinCount = d->lastSpinCount;
222 d->lastSpinCount = spinCount >= 0
223 ? qMax(lastSpinCount, spinCount)
224 : lastSpinCount + spinCount;
225 }
226
227#ifndef QT_NO_DEBUG
228 d->owner = self;
229#endif
230}
231
232/*!
233 Attempts to lock the mutex. If the lock was obtained, this function
234 returns true. If another thread has locked the mutex, this
235 function returns false immediately.
236
237 If the lock was obtained, the mutex must be unlocked with unlock()
238 before another thread can successfully lock it.
239
240 Calling this function multiple times on the same mutex from the
241 same thread is allowed if this mutex is a
242 \l{QMutex::Recursive}{recursive mutex}. If this mutex is a
243 \l{QMutex::NonRecursive}{non-recursive mutex}, this function will
244 \e always return false when attempting to lock the mutex
245 recursively.
246
247 \sa lock(), unlock()
248*/
249bool QMutex::tryLock()
250{
251 Qt::HANDLE self;
252
253 if (d->recursive) {
254 self = QThread::currentThreadId();
255 if (d->owner == self) {
256 ++d->count;
257 Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter");
258 return true;
259 }
260
261 bool isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1);
262 if (!isLocked) {
263 // some other thread has the mutex locked, or we tried to
264 // recursively lock an non-recursive mutex
265 return isLocked;
266 }
267
268 d->owner = self;
269 ++d->count;
270 Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter");
271 return isLocked;
272 }
273
274#ifndef QT_NO_DEBUG
275 self = QThread::currentThreadId();
276#endif
277 bool isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1);
278 if (!isLocked) {
279 // some other thread has the mutex locked, or we tried to
280 // recursively lock an non-recursive mutex
281 return isLocked;
282 }
283#ifndef QT_NO_DEBUG
284 d->owner = self;
285#endif
286 return isLocked;
287}
288
289/*! \overload
290
291 Attempts to lock the mutex. This function returns true if the lock
292 was obtained; otherwise it returns false. If another thread has
293 locked the mutex, this function will wait for at most \a timeout
294 milliseconds for the mutex to become available.
295
296 Note: Passing a negative number as the \a timeout is equivalent to
297 calling lock(), i.e. this function will wait forever until mutex
298 can be locked if \a timeout is negative.
299
300 If the lock was obtained, the mutex must be unlocked with unlock()
301 before another thread can successfully lock it.
302
303 Calling this function multiple times on the same mutex from the
304 same thread is allowed if this mutex is a
305 \l{QMutex::Recursive}{recursive mutex}. If this mutex is a
306 \l{QMutex::NonRecursive}{non-recursive mutex}, this function will
307 \e always return false when attempting to lock the mutex
308 recursively.
309
310 \sa lock(), unlock()
311*/
312bool QMutex::tryLock(int timeout)
313{
314 Qt::HANDLE self;
315
316 if (d->recursive) {
317 self = QThread::currentThreadId();
318 if (d->owner == self) {
319 ++d->count;
320 Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter");
321 return true;
322 }
323
324 bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0;
325 if (!isLocked) {
326 // didn't get the lock, wait for it
327 isLocked = d->wait(timeout);
328
329 // don't need to wait for the lock anymore
330 d->contenders.deref();
331 if (!isLocked)
332 return false;
333 }
334
335 d->owner = self;
336 ++d->count;
337 Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter");
338 return true;
339 }
340
341#ifndef QT_NO_DEBUG
342 self = QThread::currentThreadId();
343#endif
344 bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0;
345 if (!isLocked) {
346 // didn't get the lock, wait for it
347 isLocked = d->wait(timeout);
348
349 // don't need to wait for the lock anymore
350 d->contenders.deref();
351 if (!isLocked)
352 return false;
353 }
354#ifndef QT_NO_DEBUG
355 d->owner = self;
356#endif
357 return true;
358}
359
360
361/*!
362 Unlocks the mutex. Attempting to unlock a mutex in a different
363 thread to the one that locked it results in an error. Unlocking a
364 mutex that is not locked results in undefined behavior.
365
366 \sa lock()
367*/
368void QMutex::unlock()
369{
370 Q_ASSERT_X(d->owner == QThread::currentThreadId(), "QMutex::unlock()",
371 "A mutex must be unlocked in the same thread that locked it.");
372
373 if (d->recursive) {
374 if (!--d->count) {
375 d->owner = 0;
376 if (!d->contenders.testAndSetRelease(1, 0))
377 d->wakeUp();
378 }
379 } else {
380#ifndef QT_NO_DEBUG
381 d->owner = 0;
382#endif
383 if (!d->contenders.testAndSetRelease(1, 0))
384 d->wakeUp();
385 }
386}
387
388/*!
389 \fn bool QMutex::locked()
390
391 Returns true if the mutex is locked by another thread; otherwise
392 returns false.
393
394 It is generally a bad idea to use this function, because code
395 that uses it has a race condition. Use tryLock() and unlock()
396 instead.
397
398 \oldcode
399 bool isLocked = mutex.locked();
400 \newcode
401 bool isLocked = true;
402 if (mutex.tryLock()) {
403 mutex.unlock();
404 isLocked = false;
405 }
406 \endcode
407*/
408
409/*!
410 \class QMutexLocker
411 \brief The QMutexLocker class is a convenience class that simplifies
412 locking and unlocking mutexes.
413
414 \threadsafe
415
416 \ingroup thread
417
418 Locking and unlocking a QMutex in complex functions and
419 statements or in exception handling code is error-prone and
420 difficult to debug. QMutexLocker can be used in such situations
421 to ensure that the state of the mutex is always well-defined.
422
423 QMutexLocker should be created within a function where a
424 QMutex needs to be locked. The mutex is locked when QMutexLocker
425 is created. You can unlock and relock the mutex with \c unlock()
426 and \c relock(). If locked, the mutex will be unlocked when the
427 QMutexLocker is destroyed.
428
429 For example, this complex function locks a QMutex upon entering
430 the function and unlocks the mutex at all the exit points:
431
432 \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 4
433
434 This example function will get more complicated as it is
435 developed, which increases the likelihood that errors will occur.
436
437 Using QMutexLocker greatly simplifies the code, and makes it more
438 readable:
439
440 \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 5
441
442 Now, the mutex will always be unlocked when the QMutexLocker
443 object is destroyed (when the function returns since \c locker is
444 an auto variable).
445
446 The same principle applies to code that throws and catches
447 exceptions. An exception that is not caught in the function that
448 has locked the mutex has no way of unlocking the mutex before the
449 exception is passed up the stack to the calling function.
450
451 QMutexLocker also provides a \c mutex() member function that returns
452 the mutex on which the QMutexLocker is operating. This is useful
453 for code that needs access to the mutex, such as
454 QWaitCondition::wait(). For example:
455
456 \snippet doc/src/snippets/code/src_corelib_thread_qmutex.cpp 6
457
458 \sa QReadLocker, QWriteLocker, QMutex
459*/
460
461/*!
462 \fn QMutexLocker::QMutexLocker(QMutex *mutex)
463
464 Constructs a QMutexLocker and locks \a mutex. The mutex will be
465 unlocked when the QMutexLocker is destroyed. If \a mutex is zero,
466 QMutexLocker does nothing.
467
468 \sa QMutex::lock()
469*/
470
471/*!
472 \fn QMutexLocker::~QMutexLocker()
473
474 Destroys the QMutexLocker and unlocks the mutex that was locked
475 in the constructor.
476
477 \sa QMutex::unlock()
478*/
479
480/*!
481 \fn QMutex *QMutexLocker::mutex() const
482
483 Returns a pointer to the mutex that was locked in the
484 constructor.
485*/
486
487/*!
488 \fn void QMutexLocker::unlock()
489
490 Unlocks this mutex locker. You can use \c relock() to lock
491 it again. It does not need to be locked when destroyed.
492
493 \sa relock()
494*/
495
496/*!
497 \fn void QMutexLocker::relock()
498
499 Relocks an unlocked mutex locker.
500
501 \sa unlock()
502*/
503
504/*!
505 \fn QMutex::QMutex(bool recursive)
506
507 Use the constructor that takes a RecursionMode parameter instead.
508*/
509
510QT_END_NAMESPACE
511
512#endif // QT_NO_THREAD
Note: See TracBrowser for help on using the repository browser.