|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtCore module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
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 |
|
52 QT_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}recursive mode}. 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 */ |
|
114 QReadWriteLock::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 */ |
|
125 QReadWriteLock::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 */ |
|
135 QReadWriteLock::~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 */ |
|
147 void 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 */ |
|
190 bool 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 */ |
|
235 bool 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 : 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 */ |
|
275 void 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 */ |
|
315 bool 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 */ |
|
359 bool 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 : 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 */ |
|
401 void 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 |
|
579 QT_END_NAMESPACE |
|
580 |
|
581 #endif // QT_NO_THREAD |