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