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

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

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

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