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