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