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

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

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

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