1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // Copyright (C) 2016 Intel Corporation. |
3 | // Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com> |
4 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
5 | |
6 | #include "qplatformdefs.h" |
7 | #include "qreadwritelock.h" |
8 | |
9 | #include "qthread.h" |
10 | #include "qreadwritelock_p.h" |
11 | #include "qelapsedtimer.h" |
12 | #include "private/qfreelist_p.h" |
13 | #include "private/qlocking_p.h" |
14 | |
15 | #include <algorithm> |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | /* |
20 | * Implementation details of QReadWriteLock: |
21 | * |
22 | * Depending on the valued of d_ptr, the lock is in the following state: |
23 | * - when d_ptr == 0x0: Unlocked (no readers, no writers) and non-recursive. |
24 | * - when d_ptr & 0x1: If the least significant bit is set, we are locked for read. |
25 | * In that case, d_ptr>>4 represents the number of reading threads minus 1. No writers |
26 | * are waiting, and the lock is not recursive. |
27 | * - when d_ptr == 0x2: We are locked for write and nobody is waiting. (no contention) |
28 | * - In any other case, d_ptr points to an actual QReadWriteLockPrivate. |
29 | */ |
30 | |
31 | using namespace QReadWriteLockStates; |
32 | namespace { |
33 | |
34 | using steady_clock = std::chrono::steady_clock; |
35 | |
36 | const auto dummyLockedForRead = reinterpret_cast<QReadWriteLockPrivate *>(quintptr(StateLockedForRead)); |
37 | const auto dummyLockedForWrite = reinterpret_cast<QReadWriteLockPrivate *>(quintptr(StateLockedForWrite)); |
38 | inline bool isUncontendedLocked(const QReadWriteLockPrivate *d) |
39 | { return quintptr(d) & StateMask; } |
40 | } |
41 | |
42 | static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLockPrivate> &d_ptr, |
43 | QDeadlineTimer timeout, QReadWriteLockPrivate *d); |
44 | static bool contendedTryLockForWrite(QAtomicPointer<QReadWriteLockPrivate> &d_ptr, |
45 | QDeadlineTimer timeout, QReadWriteLockPrivate *d); |
46 | |
47 | /*! \class QReadWriteLock |
48 | \inmodule QtCore |
49 | \brief The QReadWriteLock class provides read-write locking. |
50 | |
51 | \threadsafe |
52 | |
53 | \ingroup thread |
54 | |
55 | A read-write lock is a synchronization tool for protecting |
56 | resources that can be accessed for reading and writing. This type |
57 | of lock is useful if you want to allow multiple threads to have |
58 | simultaneous read-only access, but as soon as one thread wants to |
59 | write to the resource, all other threads must be blocked until |
60 | the writing is complete. |
61 | |
62 | In many cases, QReadWriteLock is a direct competitor to QMutex. |
63 | QReadWriteLock is a good choice if there are many concurrent |
64 | reads and writing occurs infrequently. |
65 | |
66 | Example: |
67 | |
68 | \snippet code/src_corelib_thread_qreadwritelock.cpp 0 |
69 | |
70 | To ensure that writers aren't blocked forever by readers, readers |
71 | attempting to obtain a lock will not succeed if there is a blocked |
72 | writer waiting for access, even if the lock is currently only |
73 | accessed by other readers. Also, if the lock is accessed by a |
74 | writer and another writer comes in, that writer will have |
75 | priority over any readers that might also be waiting. |
76 | |
77 | Like QMutex, a QReadWriteLock can be recursively locked by the |
78 | same thread when constructed with \l{QReadWriteLock::Recursive} as |
79 | \l{QReadWriteLock::RecursionMode}. In such cases, |
80 | unlock() must be called the same number of times lockForWrite() or |
81 | lockForRead() was called. Note that the lock type cannot be |
82 | changed when trying to lock recursively, i.e. it is not possible |
83 | to lock for reading in a thread that already has locked for |
84 | writing (and vice versa). |
85 | |
86 | \sa QReadLocker, QWriteLocker, QMutex, QSemaphore |
87 | */ |
88 | |
89 | /*! |
90 | \enum QReadWriteLock::RecursionMode |
91 | \since 4.4 |
92 | |
93 | \value Recursive In this mode, a thread can lock the same |
94 | QReadWriteLock multiple times. The QReadWriteLock won't be unlocked |
95 | until a corresponding number of unlock() calls have been made. |
96 | |
97 | \value NonRecursive In this mode, a thread may only lock a |
98 | QReadWriteLock once. |
99 | |
100 | \sa QReadWriteLock() |
101 | */ |
102 | |
103 | /*! |
104 | \fn QReadWriteLock::QReadWriteLock(RecursionMode recursionMode) |
105 | \since 4.4 |
106 | |
107 | Constructs a QReadWriteLock object in the given \a recursionMode. |
108 | |
109 | The default recursion mode is NonRecursive. |
110 | |
111 | \sa lockForRead(), lockForWrite(), RecursionMode |
112 | */ |
113 | QReadWriteLockPrivate *QReadWriteLock::initRecursive() |
114 | { |
115 | auto d = new QReadWriteLockPrivate(true); |
116 | Q_ASSERT_X(!(quintptr(d) & StateMask), "QReadWriteLock::QReadWriteLock" , "bad d_ptr alignment" ); |
117 | return d; |
118 | } |
119 | |
120 | /*! |
121 | \fn QReadWriteLock::~QReadWriteLock() |
122 | Destroys the QReadWriteLock object. |
123 | |
124 | \warning Destroying a read-write lock that is in use may result |
125 | in undefined behavior. |
126 | */ |
127 | void QReadWriteLock::destroyRecursive(QReadWriteLockPrivate *d) |
128 | { |
129 | if (isUncontendedLocked(d)) { |
130 | qWarning(msg: "QReadWriteLock: destroying locked QReadWriteLock" ); |
131 | return; |
132 | } |
133 | delete d; |
134 | } |
135 | |
136 | /*! |
137 | \fn QReadWriteLock::lockForRead() |
138 | Locks the lock for reading. This function will block the current |
139 | thread if another thread has locked for writing. |
140 | |
141 | It is not possible to lock for read if the thread already has |
142 | locked for write. |
143 | |
144 | \sa unlock(), lockForWrite(), tryLockForRead() |
145 | */ |
146 | |
147 | /*! |
148 | \fn bool QReadWriteLock::tryLockForRead(int timeout) |
149 | |
150 | Attempts to lock for reading. This function returns \c true if the |
151 | lock was obtained; otherwise it returns \c false. If another thread |
152 | has locked for writing, this function will wait for at most \a |
153 | timeout milliseconds for the lock to become available. |
154 | |
155 | Note: Passing a negative number as the \a timeout is equivalent to |
156 | calling lockForRead(), i.e. this function will wait forever until |
157 | lock can be locked for reading when \a timeout is negative. |
158 | |
159 | If the lock was obtained, the lock must be unlocked with unlock() |
160 | before another thread can successfully lock it for writing. |
161 | |
162 | It is not possible to lock for read if the thread already has |
163 | locked for write. |
164 | |
165 | \sa unlock(), lockForRead() |
166 | */ |
167 | |
168 | /*! |
169 | \overload |
170 | \since 6.6 |
171 | |
172 | Attempts to lock for reading. This function returns \c true if the lock was |
173 | obtained; otherwise it returns \c false. If another thread has locked for |
174 | writing, this function will wait until \a timeout expires for the lock to |
175 | become available. |
176 | |
177 | If the lock was obtained, the lock must be unlocked with unlock() |
178 | before another thread can successfully lock it for writing. |
179 | |
180 | It is not possible to lock for read if the thread already has |
181 | locked for write. |
182 | |
183 | \sa unlock(), lockForRead() |
184 | */ |
185 | bool QReadWriteLock::tryLockForRead(QDeadlineTimer timeout) |
186 | { |
187 | // Fast case: non contended: |
188 | QReadWriteLockPrivate *d = d_ptr.loadRelaxed(); |
189 | if (d == nullptr && d_ptr.testAndSetAcquire(expectedValue: nullptr, newValue: dummyLockedForRead, currentValue&: d)) |
190 | return true; |
191 | return contendedTryLockForRead(d_ptr, timeout, d); |
192 | } |
193 | |
194 | Q_NEVER_INLINE static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLockPrivate> &d_ptr, |
195 | QDeadlineTimer timeout, QReadWriteLockPrivate *d) |
196 | { |
197 | while (true) { |
198 | if (d == nullptr) { |
199 | if (!d_ptr.testAndSetAcquire(expectedValue: nullptr, newValue: dummyLockedForRead, currentValue&: d)) |
200 | continue; |
201 | return true; |
202 | } |
203 | |
204 | if ((quintptr(d) & StateMask) == StateLockedForRead) { |
205 | // locked for read, increase the counter |
206 | const auto val = reinterpret_cast<QReadWriteLockPrivate *>(quintptr(d) + (1U<<4)); |
207 | Q_ASSERT_X(quintptr(val) > (1U<<4), "QReadWriteLock::tryLockForRead()" , |
208 | "Overflow in lock counter" ); |
209 | if (!d_ptr.testAndSetAcquire(expectedValue: d, newValue: val, currentValue&: d)) |
210 | continue; |
211 | return true; |
212 | } |
213 | |
214 | if (d == dummyLockedForWrite) { |
215 | if (timeout.hasExpired()) |
216 | return false; |
217 | |
218 | // locked for write, assign a d_ptr and wait. |
219 | auto val = QReadWriteLockPrivate::allocate(); |
220 | val->writerCount = 1; |
221 | if (!d_ptr.testAndSetOrdered(expectedValue: d, newValue: val, currentValue&: d)) { |
222 | val->writerCount = 0; |
223 | val->release(); |
224 | continue; |
225 | } |
226 | d = val; |
227 | } |
228 | Q_ASSERT(!isUncontendedLocked(d)); |
229 | // d is an actual pointer; |
230 | |
231 | if (d->recursive) |
232 | return d->recursiveLockForRead(timeout); |
233 | |
234 | auto lock = qt_unique_lock(mutex&: d->mutex); |
235 | if (d != d_ptr.loadRelaxed()) { |
236 | // d_ptr has changed: this QReadWriteLock was unlocked before we had |
237 | // time to lock d->mutex. |
238 | // We are holding a lock to a mutex within a QReadWriteLockPrivate |
239 | // that is already released (or even is already re-used). That's ok |
240 | // because the QFreeList never frees them. |
241 | // Just unlock d->mutex (at the end of the scope) and retry. |
242 | d = d_ptr.loadAcquire(); |
243 | continue; |
244 | } |
245 | return d->lockForRead(lock, timeout); |
246 | } |
247 | } |
248 | |
249 | /*! |
250 | \fn QReadWriteLock::lockForWrite() |
251 | Locks the lock for writing. This function will block the current |
252 | thread if another thread (including the current) has locked for |
253 | reading or writing (unless the lock has been created using the |
254 | \l{QReadWriteLock::Recursive} mode). |
255 | |
256 | It is not possible to lock for write if the thread already has |
257 | locked for read. |
258 | |
259 | \sa unlock(), lockForRead(), tryLockForWrite() |
260 | */ |
261 | |
262 | /*! |
263 | \fn QReadWriteLock::tryLockForWrite(int timeout) |
264 | |
265 | Attempts to lock for writing. This function returns \c true if the |
266 | lock was obtained; otherwise it returns \c false. If another thread |
267 | has locked for reading or writing, this function will wait for at |
268 | most \a timeout milliseconds for the lock to become available. |
269 | |
270 | Note: Passing a negative number as the \a timeout is equivalent to |
271 | calling lockForWrite(), i.e. this function will wait forever until |
272 | lock can be locked for writing when \a timeout is negative. |
273 | |
274 | If the lock was obtained, the lock must be unlocked with unlock() |
275 | before another thread can successfully lock it. |
276 | |
277 | It is not possible to lock for write if the thread already has |
278 | locked for read. |
279 | |
280 | \sa unlock(), lockForWrite() |
281 | */ |
282 | |
283 | /*! |
284 | \overload |
285 | \since 6.6 |
286 | |
287 | Attempts to lock for writing. This function returns \c true if the lock was |
288 | obtained; otherwise it returns \c false. If another thread has locked for |
289 | reading or writing, this function will wait until \a timeout expires for |
290 | the lock to become available. |
291 | |
292 | If the lock was obtained, the lock must be unlocked with unlock() |
293 | before another thread can successfully lock it. |
294 | |
295 | It is not possible to lock for write if the thread already has |
296 | locked for read. |
297 | |
298 | \sa unlock(), lockForWrite() |
299 | */ |
300 | bool QReadWriteLock::tryLockForWrite(QDeadlineTimer timeout) |
301 | { |
302 | // Fast case: non contended: |
303 | QReadWriteLockPrivate *d = d_ptr.loadRelaxed(); |
304 | if (d == nullptr && d_ptr.testAndSetAcquire(expectedValue: nullptr, newValue: dummyLockedForWrite, currentValue&: d)) |
305 | return true; |
306 | return contendedTryLockForWrite(d_ptr, timeout, d); |
307 | } |
308 | |
309 | Q_NEVER_INLINE static bool contendedTryLockForWrite(QAtomicPointer<QReadWriteLockPrivate> &d_ptr, |
310 | QDeadlineTimer timeout, QReadWriteLockPrivate *d) |
311 | { |
312 | while (true) { |
313 | if (d == nullptr) { |
314 | if (!d_ptr.testAndSetAcquire(expectedValue: d, newValue: dummyLockedForWrite, currentValue&: d)) |
315 | continue; |
316 | return true; |
317 | } |
318 | |
319 | if (isUncontendedLocked(d)) { |
320 | if (timeout.hasExpired()) |
321 | return false; |
322 | |
323 | // locked for either read or write, assign a d_ptr and wait. |
324 | auto val = QReadWriteLockPrivate::allocate(); |
325 | if (d == dummyLockedForWrite) |
326 | val->writerCount = 1; |
327 | else |
328 | val->readerCount = (quintptr(d) >> 4) + 1; |
329 | if (!d_ptr.testAndSetOrdered(expectedValue: d, newValue: val, currentValue&: d)) { |
330 | val->writerCount = val->readerCount = 0; |
331 | val->release(); |
332 | continue; |
333 | } |
334 | d = val; |
335 | } |
336 | Q_ASSERT(!isUncontendedLocked(d)); |
337 | // d is an actual pointer; |
338 | |
339 | if (d->recursive) |
340 | return d->recursiveLockForWrite(timeout); |
341 | |
342 | auto lock = qt_unique_lock(mutex&: d->mutex); |
343 | if (d != d_ptr.loadRelaxed()) { |
344 | // The mutex was unlocked before we had time to lock the mutex. |
345 | // We are holding to a mutex within a QReadWriteLockPrivate that is already released |
346 | // (or even is already re-used) but that's ok because the QFreeList never frees them. |
347 | d = d_ptr.loadAcquire(); |
348 | continue; |
349 | } |
350 | return d->lockForWrite(lock, timeout); |
351 | } |
352 | } |
353 | |
354 | /*! |
355 | Unlocks the lock. |
356 | |
357 | Attempting to unlock a lock that is not locked is an error, and will result |
358 | in program termination. |
359 | |
360 | \sa lockForRead(), lockForWrite(), tryLockForRead(), tryLockForWrite() |
361 | */ |
362 | void QReadWriteLock::unlock() |
363 | { |
364 | QReadWriteLockPrivate *d = d_ptr.loadAcquire(); |
365 | while (true) { |
366 | Q_ASSERT_X(d, "QReadWriteLock::unlock()" , "Cannot unlock an unlocked lock" ); |
367 | |
368 | // Fast case: no contention: (no waiters, no other readers) |
369 | if (quintptr(d) <= 2) { // 1 or 2 (StateLockedForRead or StateLockedForWrite) |
370 | if (!d_ptr.testAndSetOrdered(expectedValue: d, newValue: nullptr, currentValue&: d)) |
371 | continue; |
372 | return; |
373 | } |
374 | |
375 | if ((quintptr(d) & StateMask) == StateLockedForRead) { |
376 | Q_ASSERT(quintptr(d) > (1U<<4)); //otherwise that would be the fast case |
377 | // Just decrease the reader's count. |
378 | auto val = reinterpret_cast<QReadWriteLockPrivate *>(quintptr(d) - (1U<<4)); |
379 | if (!d_ptr.testAndSetOrdered(expectedValue: d, newValue: val, currentValue&: d)) |
380 | continue; |
381 | return; |
382 | } |
383 | |
384 | Q_ASSERT(!isUncontendedLocked(d)); |
385 | |
386 | if (d->recursive) { |
387 | d->recursiveUnlock(); |
388 | return; |
389 | } |
390 | |
391 | const auto lock = qt_scoped_lock(mutex&: d->mutex); |
392 | if (d->writerCount) { |
393 | Q_ASSERT(d->writerCount == 1); |
394 | Q_ASSERT(d->readerCount == 0); |
395 | d->writerCount = 0; |
396 | } else { |
397 | Q_ASSERT(d->readerCount > 0); |
398 | d->readerCount--; |
399 | if (d->readerCount > 0) |
400 | return; |
401 | } |
402 | |
403 | if (d->waitingReaders || d->waitingWriters) { |
404 | d->unlock(); |
405 | } else { |
406 | Q_ASSERT(d_ptr.loadRelaxed() == d); // should not change when we still hold the mutex |
407 | d_ptr.storeRelease(newValue: nullptr); |
408 | d->release(); |
409 | } |
410 | return; |
411 | } |
412 | } |
413 | |
414 | bool QReadWriteLockPrivate::lockForRead(std::unique_lock<QtPrivate::mutex> &lock, QDeadlineTimer timeout) |
415 | { |
416 | Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function |
417 | |
418 | while (waitingWriters || writerCount) { |
419 | if (timeout.hasExpired()) |
420 | return false; |
421 | if (!timeout.isForever()) { |
422 | waitingReaders++; |
423 | readerCond.wait_until(lock&: lock, atime: timeout.deadline<steady_clock>()); |
424 | } else { |
425 | waitingReaders++; |
426 | readerCond.wait(lock&: lock); |
427 | } |
428 | waitingReaders--; |
429 | } |
430 | readerCount++; |
431 | Q_ASSERT(writerCount == 0); |
432 | return true; |
433 | } |
434 | |
435 | bool QReadWriteLockPrivate::lockForWrite(std::unique_lock<QtPrivate::mutex> &lock, QDeadlineTimer timeout) |
436 | { |
437 | Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function |
438 | |
439 | while (readerCount || writerCount) { |
440 | if (timeout.hasExpired()) { |
441 | if (waitingReaders && !waitingWriters && !writerCount) { |
442 | // We timed out and now there is no more writers or waiting writers, but some |
443 | // readers were queued (probably because of us). Wake the waiting readers. |
444 | readerCond.notify_all(); |
445 | } |
446 | return false; |
447 | } |
448 | if (!timeout.isForever()) { |
449 | waitingWriters++; |
450 | writerCond.wait_until(lock&: lock, atime: timeout.deadline<steady_clock>()); |
451 | } else { |
452 | waitingWriters++; |
453 | writerCond.wait(lock&: lock); |
454 | } |
455 | waitingWriters--; |
456 | } |
457 | |
458 | Q_ASSERT(writerCount == 0); |
459 | Q_ASSERT(readerCount == 0); |
460 | writerCount = 1; |
461 | return true; |
462 | } |
463 | |
464 | void QReadWriteLockPrivate::unlock() |
465 | { |
466 | Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function |
467 | if (waitingWriters) |
468 | writerCond.notify_one(); |
469 | else if (waitingReaders) |
470 | readerCond.notify_all(); |
471 | } |
472 | |
473 | static auto handleEquals(Qt::HANDLE handle) |
474 | { |
475 | return [handle](QReadWriteLockPrivate::Reader reader) { return reader.handle == handle; }; |
476 | } |
477 | |
478 | bool QReadWriteLockPrivate::recursiveLockForRead(QDeadlineTimer timeout) |
479 | { |
480 | Q_ASSERT(recursive); |
481 | auto lock = qt_unique_lock(mutex); |
482 | |
483 | Qt::HANDLE self = QThread::currentThreadId(); |
484 | |
485 | auto it = std::find_if(first: currentReaders.begin(), last: currentReaders.end(), |
486 | pred: handleEquals(handle: self)); |
487 | if (it != currentReaders.end()) { |
488 | ++it->recursionLevel; |
489 | return true; |
490 | } |
491 | |
492 | if (!lockForRead(lock, timeout)) |
493 | return false; |
494 | |
495 | Reader r = {.handle: self, .recursionLevel: 1}; |
496 | currentReaders.append(t: std::move(r)); |
497 | return true; |
498 | } |
499 | |
500 | bool QReadWriteLockPrivate::recursiveLockForWrite(QDeadlineTimer timeout) |
501 | { |
502 | Q_ASSERT(recursive); |
503 | auto lock = qt_unique_lock(mutex); |
504 | |
505 | Qt::HANDLE self = QThread::currentThreadId(); |
506 | if (currentWriter == self) { |
507 | writerCount++; |
508 | return true; |
509 | } |
510 | |
511 | if (!lockForWrite(lock, timeout)) |
512 | return false; |
513 | |
514 | currentWriter = self; |
515 | return true; |
516 | } |
517 | |
518 | void QReadWriteLockPrivate::recursiveUnlock() |
519 | { |
520 | Q_ASSERT(recursive); |
521 | auto lock = qt_unique_lock(mutex); |
522 | |
523 | Qt::HANDLE self = QThread::currentThreadId(); |
524 | if (self == currentWriter) { |
525 | if (--writerCount > 0) |
526 | return; |
527 | currentWriter = nullptr; |
528 | } else { |
529 | auto it = std::find_if(first: currentReaders.begin(), last: currentReaders.end(), |
530 | pred: handleEquals(handle: self)); |
531 | if (it == currentReaders.end()) { |
532 | qWarning(msg: "QReadWriteLock::unlock: unlocking from a thread that did not lock" ); |
533 | return; |
534 | } else { |
535 | if (--it->recursionLevel <= 0) { |
536 | currentReaders.erase(pos: it); |
537 | readerCount--; |
538 | } |
539 | if (readerCount) |
540 | return; |
541 | } |
542 | } |
543 | |
544 | unlock(); |
545 | } |
546 | |
547 | // The freelist management |
548 | namespace { |
549 | struct QReadWriteLockFreeListConstants : QFreeListDefaultConstants |
550 | { |
551 | enum { BlockCount = 4, MaxIndex=0xffff }; |
552 | static const int Sizes[BlockCount]; |
553 | }; |
554 | Q_CONSTINIT const int |
555 | QReadWriteLockFreeListConstants::Sizes[QReadWriteLockFreeListConstants::BlockCount] = { |
556 | 16, 128, 1024, QReadWriteLockFreeListConstants::MaxIndex - (16 + 128 + 1024) |
557 | }; |
558 | |
559 | typedef QFreeList<QReadWriteLockPrivate, QReadWriteLockFreeListConstants> QReadWriteLockFreeList; |
560 | Q_GLOBAL_STATIC(QReadWriteLockFreeList, qrwl_freelist); |
561 | } |
562 | |
563 | QReadWriteLockPrivate *QReadWriteLockPrivate::allocate() |
564 | { |
565 | int i = qrwl_freelist->next(); |
566 | QReadWriteLockPrivate *d = &(*qrwl_freelist)[i]; |
567 | d->id = i; |
568 | Q_ASSERT(!d->recursive); |
569 | Q_ASSERT(!d->waitingReaders && !d->waitingWriters && !d->readerCount && !d->writerCount); |
570 | return d; |
571 | } |
572 | |
573 | void QReadWriteLockPrivate::release() |
574 | { |
575 | Q_ASSERT(!recursive); |
576 | Q_ASSERT(!waitingReaders && !waitingWriters && !readerCount && !writerCount); |
577 | qrwl_freelist->release(id); |
578 | } |
579 | |
580 | /*! |
581 | \class QReadLocker |
582 | \inmodule QtCore |
583 | \brief The QReadLocker class is a convenience class that |
584 | simplifies locking and unlocking read-write locks for read access. |
585 | |
586 | \threadsafe |
587 | |
588 | \ingroup thread |
589 | |
590 | The purpose of QReadLocker (and QWriteLocker) is to simplify |
591 | QReadWriteLock locking and unlocking. Locking and unlocking |
592 | statements or in exception handling code is error-prone and |
593 | difficult to debug. QReadLocker can be used in such situations |
594 | to ensure that the state of the lock is always well-defined. |
595 | |
596 | Here's an example that uses QReadLocker to lock and unlock a |
597 | read-write lock for reading: |
598 | |
599 | \snippet code/src_corelib_thread_qreadwritelock.cpp 1 |
600 | |
601 | It is equivalent to the following code: |
602 | |
603 | \snippet code/src_corelib_thread_qreadwritelock.cpp 2 |
604 | |
605 | The QMutexLocker documentation shows examples where the use of a |
606 | locker object greatly simplifies programming. |
607 | |
608 | \sa QWriteLocker, QReadWriteLock |
609 | */ |
610 | |
611 | /*! |
612 | \fn QReadLocker::QReadLocker(QReadWriteLock *lock) |
613 | |
614 | Constructs a QReadLocker and locks \a lock for reading. The lock |
615 | will be unlocked when the QReadLocker is destroyed. If \c lock is |
616 | zero, QReadLocker does nothing. |
617 | |
618 | \sa QReadWriteLock::lockForRead() |
619 | */ |
620 | |
621 | /*! |
622 | \fn QReadLocker::~QReadLocker() |
623 | |
624 | Destroys the QReadLocker and unlocks the lock that was passed to |
625 | the constructor. |
626 | |
627 | \sa QReadWriteLock::unlock() |
628 | */ |
629 | |
630 | /*! |
631 | \fn void QReadLocker::unlock() |
632 | |
633 | Unlocks the lock associated with this locker. |
634 | |
635 | \sa QReadWriteLock::unlock() |
636 | */ |
637 | |
638 | /*! |
639 | \fn void QReadLocker::relock() |
640 | |
641 | Relocks an unlocked lock. |
642 | |
643 | \sa unlock() |
644 | */ |
645 | |
646 | /*! |
647 | \fn QReadWriteLock *QReadLocker::readWriteLock() const |
648 | |
649 | Returns a pointer to the read-write lock that was passed |
650 | to the constructor. |
651 | */ |
652 | |
653 | /*! |
654 | \class QWriteLocker |
655 | \inmodule QtCore |
656 | \brief The QWriteLocker class is a convenience class that |
657 | simplifies locking and unlocking read-write locks for write access. |
658 | |
659 | \threadsafe |
660 | |
661 | \ingroup thread |
662 | |
663 | The purpose of QWriteLocker (and QReadLocker) is to simplify |
664 | QReadWriteLock locking and unlocking. Locking and unlocking |
665 | statements or in exception handling code is error-prone and |
666 | difficult to debug. QWriteLocker can be used in such situations |
667 | to ensure that the state of the lock is always well-defined. |
668 | |
669 | Here's an example that uses QWriteLocker to lock and unlock a |
670 | read-write lock for writing: |
671 | |
672 | \snippet code/src_corelib_thread_qreadwritelock.cpp 3 |
673 | |
674 | It is equivalent to the following code: |
675 | |
676 | \snippet code/src_corelib_thread_qreadwritelock.cpp 4 |
677 | |
678 | The QMutexLocker documentation shows examples where the use of a |
679 | locker object greatly simplifies programming. |
680 | |
681 | \sa QReadLocker, QReadWriteLock |
682 | */ |
683 | |
684 | /*! |
685 | \fn QWriteLocker::QWriteLocker(QReadWriteLock *lock) |
686 | |
687 | Constructs a QWriteLocker and locks \a lock for writing. The lock |
688 | will be unlocked when the QWriteLocker is destroyed. If \c lock is |
689 | zero, QWriteLocker does nothing. |
690 | |
691 | \sa QReadWriteLock::lockForWrite() |
692 | */ |
693 | |
694 | /*! |
695 | \fn QWriteLocker::~QWriteLocker() |
696 | |
697 | Destroys the QWriteLocker and unlocks the lock that was passed to |
698 | the constructor. |
699 | |
700 | \sa QReadWriteLock::unlock() |
701 | */ |
702 | |
703 | /*! |
704 | \fn void QWriteLocker::unlock() |
705 | |
706 | Unlocks the lock associated with this locker. |
707 | |
708 | \sa QReadWriteLock::unlock() |
709 | */ |
710 | |
711 | /*! |
712 | \fn void QWriteLocker::relock() |
713 | |
714 | Relocks an unlocked lock. |
715 | |
716 | \sa unlock() |
717 | */ |
718 | |
719 | /*! |
720 | \fn QReadWriteLock *QWriteLocker::readWriteLock() const |
721 | |
722 | Returns a pointer to the read-write lock that was passed |
723 | to the constructor. |
724 | */ |
725 | |
726 | QT_END_NAMESPACE |
727 | |