source: trunk/src/corelib/thread/qreadwritelock.cpp@ 1023

Last change on this file since 1023 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 16.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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 "qreadwritelock.h"
44
45#ifndef QT_NO_THREAD
46#include "qmutex.h"
47#include "qthread.h"
48#include "qwaitcondition.h"
49
50#include "qreadwritelock_p.h"
51
52QT_BEGIN_NAMESPACE
53
54/*! \class QReadWriteLock
55 \brief The QReadWriteLock class provides read-write locking.
56
57 \threadsafe
58
59 \ingroup thread
60
61 A read-write lock is a synchronization tool for protecting
62 resources that can be accessed for reading and writing. This type
63 of lock is useful if you want to allow multiple threads to have
64 simultaneous read-only access, but as soon as one thread wants to
65 write to the resource, all other threads must be blocked until
66 the writing is complete.
67
68 In many cases, QReadWriteLock is a direct competitor to QMutex.
69 QReadWriteLock is a good choice if there are many concurrent
70 reads and writing occurs infrequently.
71
72 Example:
73
74 \snippet doc/src/snippets/code/src_corelib_thread_qreadwritelock.cpp 0
75
76 To ensure that writers aren't blocked forever by readers, readers
77 attempting to obtain a lock will not succeed if there is a blocked
78 writer waiting for access, even if the lock is currently only
79 accessed by other readers. Also, if the lock is accessed by a
80 writer and another writer comes in, that writer will have
81 priority over any readers that might also be waiting.
82
83 Like QMutex, a QReadWriteLock can be recursively locked by the
84 same thread when constructed in
85 \l{QReadWriteLock::RecursionMode}. In such cases,
86 unlock() must be called the same number of times lockForWrite() or
87 lockForRead() was called. Note that the lock type cannot be
88 changed when trying to lock recursively, i.e. it is not possible
89 to lock for reading in a thread that already has locked for
90 writing (and vice versa).
91
92 \sa QReadLocker, QWriteLocker, QMutex, QSemaphore
93*/
94
95/*!
96 \enum QReadWriteLock::RecursionMode
97 \since 4.4
98
99 \value Recursive In this mode, a thread can lock the same
100 QReadWriteLock multiple times and the mutex won't be unlocked
101 until a corresponding number of unlock() calls have been made.
102
103 \value NonRecursive In this mode, a thread may only lock a
104 QReadWriteLock once.
105
106 \sa QReadWriteLock()
107*/
108
109/*!
110 Constructs a QReadWriteLock object in NonRecursive mode.
111
112 \sa lockForRead(), lockForWrite()
113*/
114QReadWriteLock::QReadWriteLock()
115 :d(new QReadWriteLockPrivate(NonRecursive))
116{ }
117
118/*!
119 \since 4.4
120
121 Constructs a QReadWriteLock object in the given \a recursionMode.
122
123 \sa lockForRead(), lockForWrite(), RecursionMode
124*/
125QReadWriteLock::QReadWriteLock(RecursionMode recursionMode)
126 : d(new QReadWriteLockPrivate(recursionMode))
127{ }
128
129/*!
130 Destroys the QReadWriteLock object.
131
132 \warning Destroying a read-write lock that is in use may result
133 in undefined behavior.
134*/
135QReadWriteLock::~QReadWriteLock()
136{
137 delete d;
138}
139
140/*!
141 Locks the lock for reading. This function will block the current
142 thread if any thread (including the current) has locked for
143 writing.
144
145 \sa unlock() lockForWrite() tryLockForRead()
146*/
147void QReadWriteLock::lockForRead()
148{
149 QMutexLocker lock(&d->mutex);
150
151 Qt::HANDLE self = 0;
152 if (d->recursive) {
153 self = QThread::currentThreadId();
154
155 QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
156 if (it != d->currentReaders.end()) {
157 ++it.value();
158 ++d->accessCount;
159 Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::lockForRead()",
160 "Overflow in lock counter");
161 return;
162 }
163 }
164
165 while (d->accessCount < 0 || d->waitingWriters) {
166 ++d->waitingReaders;
167 d->readerWait.wait(&d->mutex);
168 --d->waitingReaders;
169 }
170 if (d->recursive)
171 d->currentReaders.insert(self, 1);
172
173 ++d->accessCount;
174 Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::lockForRead()", "Overflow in lock counter");
175}
176
177/*!
178 Attempts to lock for reading. If the lock was obtained, this
179 function returns true, otherwise it returns false instead of
180 waiting for the lock to become available, i.e. it does not block.
181
182 The lock attempt will fail if another thread has locked for
183 writing.
184
185 If the lock was obtained, the lock must be unlocked with unlock()
186 before another thread can successfully lock it.
187
188 \sa unlock() lockForRead()
189*/
190bool QReadWriteLock::tryLockForRead()
191{
192 QMutexLocker lock(&d->mutex);
193
194 Qt::HANDLE self = 0;
195 if (d->recursive) {
196 self = QThread::currentThreadId();
197
198 QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
199 if (it != d->currentReaders.end()) {
200 ++it.value();
201 ++d->accessCount;
202 Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::tryLockForRead()",
203 "Overflow in lock counter");
204 return true;
205 }
206 }
207
208 if (d->accessCount < 0)
209 return false;
210 if (d->recursive)
211 d->currentReaders.insert(self, 1);
212
213 ++d->accessCount;
214 Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::tryLockForRead()", "Overflow in lock counter");
215
216 return true;
217}
218
219/*! \overload
220
221 Attempts to lock for reading. This function returns true if the
222 lock was obtained; otherwise it returns false. If another thread
223 has locked for writing, this function will wait for at most \a
224 timeout milliseconds for the lock to become available.
225
226 Note: Passing a negative number as the \a timeout is equivalent to
227 calling lockForRead(), i.e. this function will wait forever until
228 lock can be locked for reading when \a timeout is negative.
229
230 If the lock was obtained, the lock must be unlocked with unlock()
231 before another thread can successfully lock it.
232
233 \sa unlock() lockForRead()
234*/
235bool QReadWriteLock::tryLockForRead(int timeout)
236{
237 QMutexLocker lock(&d->mutex);
238
239 Qt::HANDLE self = 0;
240 if (d->recursive) {
241 self = QThread::currentThreadId();
242
243 QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
244 if (it != d->currentReaders.end()) {
245 ++it.value();
246 ++d->accessCount;
247 Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::tryLockForRead()",
248 "Overflow in lock counter");
249 return true;
250 }
251 }
252
253 while (d->accessCount < 0 || d->waitingWriters) {
254 ++d->waitingReaders;
255 bool success = d->readerWait.wait(&d->mutex, timeout < 0 ? ULONG_MAX : ulong(timeout));
256 --d->waitingReaders;
257 if (!success)
258 return false;
259 }
260 if (d->recursive)
261 d->currentReaders.insert(self, 1);
262
263 ++d->accessCount;
264 Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::tryLockForRead()", "Overflow in lock counter");
265
266 return true;
267}
268
269/*!
270 Locks the lock for writing. This function will block the current
271 thread if another thread has locked for reading or writing.
272
273 \sa unlock() lockForRead() tryLockForWrite()
274*/
275void QReadWriteLock::lockForWrite()
276{
277 QMutexLocker lock(&d->mutex);
278
279 Qt::HANDLE self = 0;
280 if (d->recursive) {
281 self = QThread::currentThreadId();
282
283 if (d->currentWriter == self) {
284 --d->accessCount;
285 Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::lockForWrite()",
286 "Overflow in lock counter");
287 return;
288 }
289 }
290
291 while (d->accessCount != 0) {
292 ++d->waitingWriters;
293 d->writerWait.wait(&d->mutex);
294 --d->waitingWriters;
295 }
296 if (d->recursive)
297 d->currentWriter = self;
298
299 --d->accessCount;
300 Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::lockForWrite()", "Overflow in lock counter");
301}
302
303/*!
304 Attempts to lock for writing. If the lock was obtained, this
305 function returns true; otherwise, it returns false immediately.
306
307 The lock attempt will fail if another thread has locked for
308 reading or writing.
309
310 If the lock was obtained, the lock must be unlocked with unlock()
311 before another thread can successfully lock it.
312
313 \sa unlock() lockForWrite()
314*/
315bool QReadWriteLock::tryLockForWrite()
316{
317 QMutexLocker lock(&d->mutex);
318
319 Qt::HANDLE self = 0;
320 if (d->recursive) {
321 self = QThread::currentThreadId();
322
323 if (d->currentWriter == self) {
324 --d->accessCount;
325 Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::lockForWrite()",
326 "Overflow in lock counter");
327 return true;
328 }
329 }
330
331 if (d->accessCount != 0)
332 return false;
333 if (d->recursive)
334 d->currentWriter = self;
335
336 --d->accessCount;
337 Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::tryLockForWrite()",
338 "Overflow in lock counter");
339
340 return true;
341}
342
343/*! \overload
344
345 Attempts to lock for writing. This function returns true if the
346 lock was obtained; otherwise it returns false. If another thread
347 has locked for reading or writing, this function will wait for at
348 most \a timeout milliseconds for the lock to become available.
349
350 Note: Passing a negative number as the \a timeout is equivalent to
351 calling lockForWrite(), i.e. this function will wait forever until
352 lock can be locked for writing when \a timeout is negative.
353
354 If the lock was obtained, the lock must be unlocked with unlock()
355 before another thread can successfully lock it.
356
357 \sa unlock() lockForWrite()
358*/
359bool QReadWriteLock::tryLockForWrite(int timeout)
360{
361 QMutexLocker lock(&d->mutex);
362
363 Qt::HANDLE self = 0;
364 if (d->recursive) {
365 self = QThread::currentThreadId();
366
367 if (d->currentWriter == self) {
368 --d->accessCount;
369 Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::lockForWrite()",
370 "Overflow in lock counter");
371 return true;
372 }
373 }
374
375 while (d->accessCount != 0) {
376 ++d->waitingWriters;
377 bool success = d->writerWait.wait(&d->mutex, timeout < 0 ? ULONG_MAX : ulong(timeout));
378 --d->waitingWriters;
379
380 if (!success)
381 return false;
382 }
383 if (d->recursive)
384 d->currentWriter = self;
385
386 --d->accessCount;
387 Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::tryLockForWrite()",
388 "Overflow in lock counter");
389
390 return true;
391}
392
393/*!
394 Unlocks the lock.
395
396 Attempting to unlock a lock that is not locked is an error, and will result
397 in program termination.
398
399 \sa lockForRead() lockForWrite() tryLockForRead() tryLockForWrite()
400*/
401void QReadWriteLock::unlock()
402{
403 QMutexLocker lock(&d->mutex);
404
405 Q_ASSERT_X(d->accessCount != 0, "QReadWriteLock::unlock()", "Cannot unlock an unlocked lock");
406
407 bool unlocked = false;
408 if (d->accessCount > 0) {
409 // releasing a read lock
410 if (d->recursive) {
411 Qt::HANDLE self = QThread::currentThreadId();
412 QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
413 if (it != d->currentReaders.end()) {
414 if (--it.value() <= 0)
415 d->currentReaders.erase(it);
416 }
417 }
418
419 unlocked = --d->accessCount == 0;
420 } else if (d->accessCount < 0 && ++d->accessCount == 0) {
421 // released a write lock
422 unlocked = true;
423 d->currentWriter = 0;
424 }
425
426 if (unlocked) {
427 if (d->waitingWriters) {
428 d->writerWait.wakeOne();
429 } else if (d->waitingReaders) {
430 d->readerWait.wakeAll();
431 }
432 }
433}
434
435/*!
436 \class QReadLocker
437 \brief The QReadLocker class is a convenience class that
438 simplifies locking and unlocking read-write locks for read access.
439
440 \threadsafe
441
442 \ingroup thread
443
444 The purpose of QReadLocker (and QWriteLocker) is to simplify
445 QReadWriteLock locking and unlocking. Locking and unlocking
446 statements or in exception handling code is error-prone and
447 difficult to debug. QReadLocker can be used in such situations
448 to ensure that the state of the lock is always well-defined.
449
450 Here's an example that uses QReadLocker to lock and unlock a
451 read-write lock for reading:
452
453 \snippet doc/src/snippets/code/src_corelib_thread_qreadwritelock.cpp 1
454
455 It is equivalent to the following code:
456
457 \snippet doc/src/snippets/code/src_corelib_thread_qreadwritelock.cpp 2
458
459 The QMutexLocker documentation shows examples where the use of a
460 locker object greatly simplifies programming.
461
462 \sa QWriteLocker, QReadWriteLock
463*/
464
465/*!
466 \fn QReadLocker::QReadLocker(QReadWriteLock *lock)
467
468 Constructs a QReadLocker and locks \a lock for reading. The lock
469 will be unlocked when the QReadLocker is destroyed. If \c lock is
470 zero, QReadLocker does nothing.
471
472 \sa QReadWriteLock::lockForRead()
473*/
474
475/*!
476 \fn QReadLocker::~QReadLocker()
477
478 Destroys the QReadLocker and unlocks the lock that was passed to
479 the constructor.
480
481 \sa QReadWriteLock::unlock()
482*/
483
484/*!
485 \fn void QReadLocker::unlock()
486
487 Unlocks the lock associated with this locker.
488
489 \sa QReadWriteLock::unlock()
490*/
491
492/*!
493 \fn void QReadLocker::relock()
494
495 Relocks an unlocked lock.
496
497 \sa unlock()
498*/
499
500/*!
501 \fn QReadWriteLock *QReadLocker::readWriteLock() const
502
503 Returns a pointer to the read-write lock that was passed
504 to the constructor.
505*/
506
507/*!
508 \class QWriteLocker
509 \brief The QWriteLocker class is a convenience class that
510 simplifies locking and unlocking read-write locks for write access.
511
512 \threadsafe
513
514 \ingroup thread
515
516 The purpose of QWriteLocker (and QReadLocker is to simplify
517 QReadWriteLock locking and unlocking. Locking and unlocking
518 statements or in exception handling code is error-prone and
519 difficult to debug. QWriteLocker can be used in such situations
520 to ensure that the state of the lock is always well-defined.
521
522 Here's an example that uses QWriteLocker to lock and unlock a
523 read-write lock for writing:
524
525 \snippet doc/src/snippets/code/src_corelib_thread_qreadwritelock.cpp 3
526
527 It is equivalent to the following code:
528
529 \snippet doc/src/snippets/code/src_corelib_thread_qreadwritelock.cpp 4
530
531 The QMutexLocker documentation shows examples where the use of a
532 locker object greatly simplifies programming.
533
534 \sa QReadLocker, QReadWriteLock
535*/
536
537/*!
538 \fn QWriteLocker::QWriteLocker(QReadWriteLock *lock)
539
540 Constructs a QWriteLocker and locks \a lock for writing. The lock
541 will be unlocked when the QWriteLocker is destroyed. If \c lock is
542 zero, QWriteLocker does nothing.
543
544 \sa QReadWriteLock::lockForWrite()
545*/
546
547/*!
548 \fn QWriteLocker::~QWriteLocker()
549
550 Destroys the QWriteLocker and unlocks the lock that was passed to
551 the constructor.
552
553 \sa QReadWriteLock::unlock()
554*/
555
556/*!
557 \fn void QWriteLocker::unlock()
558
559 Unlocks the lock associated with this locker.
560
561 \sa QReadWriteLock::unlock()
562*/
563
564/*!
565 \fn void QWriteLocker::relock()
566
567 Relocks an unlocked lock.
568
569 \sa unlock()
570*/
571
572/*!
573 \fn QReadWriteLock *QWriteLocker::readWriteLock() const
574
575 Returns a pointer to the read-write lock that was passed
576 to the constructor.
577*/
578
579QT_END_NAMESPACE
580
581#endif // QT_NO_THREAD
Note: See TracBrowser for help on using the repository browser.