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