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

Last change on this file since 192 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