~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to src/corelib/thread/qreadwritelock.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the QtCore module of the Qt Toolkit.
 
7
**
 
8
** $QT_BEGIN_LICENSE:LGPL$
 
9
** Commercial License Usage
 
10
** Licensees holding valid commercial Qt licenses may use this file in
 
11
** accordance with the commercial license agreement provided with the
 
12
** Software or, alternatively, in accordance with the terms contained in
 
13
** a written agreement between you and Digia.  For licensing terms and
 
14
** conditions see http://qt.digia.com/licensing.  For further information
 
15
** use the contact form at http://qt.digia.com/contact-us.
 
16
**
 
17
** GNU Lesser General Public License Usage
 
18
** Alternatively, this file may be used under the terms of the GNU Lesser
 
19
** General Public License version 2.1 as published by the Free Software
 
20
** Foundation and appearing in the file LICENSE.LGPL included in the
 
21
** packaging of this file.  Please review the following information to
 
22
** ensure the GNU Lesser General Public License version 2.1 requirements
 
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
24
**
 
25
** In addition, as a special exception, Digia gives you certain additional
 
26
** rights.  These rights are described in the Digia Qt LGPL Exception
 
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
28
**
 
29
** GNU General Public License Usage
 
30
** Alternatively, this file may be used under the terms of the GNU
 
31
** General Public License version 3.0 as published by the Free Software
 
32
** Foundation and appearing in the file LICENSE.GPL included in the
 
33
** packaging of this file.  Please review the following information to
 
34
** ensure the GNU General Public License version 3.0 requirements will be
 
35
** met: http://www.gnu.org/copyleft/gpl.html.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include "qplatformdefs.h"
 
43
#include "qreadwritelock.h"
 
44
 
 
45
#ifndef QT_NO_THREAD
 
46
#include "qmutex.h"
 
47
#include "qthread.h"
 
48
#include "qwaitcondition.h"
 
49
 
 
50
#include "qreadwritelock_p.h"
 
51
 
 
52
QT_BEGIN_NAMESPACE
 
53
 
 
54
/*! \class QReadWriteLock
 
55
    \inmodule QtCore
 
56
    \brief The QReadWriteLock class provides read-write locking.
 
57
 
 
58
    \threadsafe
 
59
 
 
60
    \ingroup thread
 
61
 
 
62
    A read-write lock is a synchronization tool for protecting
 
63
    resources that can be accessed for reading and writing. This type
 
64
    of lock is useful if you want to allow multiple threads to have
 
65
    simultaneous read-only access, but as soon as one thread wants to
 
66
    write to the resource, all other threads must be blocked until
 
67
    the writing is complete.
 
68
 
 
69
    In many cases, QReadWriteLock is a direct competitor to QMutex.
 
70
    QReadWriteLock is a good choice if there are many concurrent
 
71
    reads and writing occurs infrequently.
 
72
 
 
73
    Example:
 
74
 
 
75
    \snippet code/src_corelib_thread_qreadwritelock.cpp 0
 
76
 
 
77
    To ensure that writers aren't blocked forever by readers, readers
 
78
    attempting to obtain a lock will not succeed if there is a blocked
 
79
    writer waiting for access, even if the lock is currently only
 
80
    accessed by other readers. Also, if the lock is accessed by a
 
81
    writer and another writer comes in, that writer will have
 
82
    priority over any readers that might also be waiting.
 
83
 
 
84
    Like QMutex, a QReadWriteLock can be recursively locked by the
 
85
    same thread when constructed with \l{QReadWriteLock::Recursive} as
 
86
    \l{QReadWriteLock::RecursionMode}. In such cases,
 
87
    unlock() must be called the same number of times lockForWrite() or
 
88
    lockForRead() was called. Note that the lock type cannot be
 
89
    changed when trying to lock recursively, i.e. it is not possible
 
90
    to lock for reading in a thread that already has locked for
 
91
    writing (and vice versa).
 
92
 
 
93
    \sa QReadLocker, QWriteLocker, QMutex, QSemaphore
 
94
*/
 
95
 
 
96
/*! 
 
97
    \enum QReadWriteLock::RecursionMode
 
98
    \since 4.4
 
99
 
 
100
    \value Recursive In this mode, a thread can lock the same
 
101
    QReadWriteLock multiple times and the mutex won't be unlocked
 
102
    until a corresponding number of unlock() calls have been made.
 
103
 
 
104
    \value NonRecursive In this mode, a thread may only lock a
 
105
    QReadWriteLock once.
 
106
 
 
107
    \sa QReadWriteLock()
 
108
*/
 
109
 
 
110
/*!
 
111
    \since 4.4
 
112
 
 
113
    Constructs a QReadWriteLock object in the given \a recursionMode.
 
114
 
 
115
    The default recursion mode is NonRecursive.
 
116
 
 
117
    \sa lockForRead(), lockForWrite(), RecursionMode
 
118
*/
 
119
QReadWriteLock::QReadWriteLock(RecursionMode recursionMode)
 
120
    : d(new QReadWriteLockPrivate(recursionMode))
 
121
{ }
 
122
 
 
123
/*!
 
124
    Destroys the QReadWriteLock object.
 
125
 
 
126
    \warning Destroying a read-write lock that is in use may result
 
127
    in undefined behavior.
 
128
*/
 
129
QReadWriteLock::~QReadWriteLock()
 
130
{
 
131
    delete d;
 
132
}
 
133
 
 
134
/*!
 
135
    Locks the lock for reading. This function will block the current
 
136
    thread if another thread has locked for writing.
 
137
 
 
138
    It is not possible to lock for read if the thread already has
 
139
    locked for write.
 
140
 
 
141
    \sa unlock(), lockForWrite(), tryLockForRead()
 
142
*/
 
143
void QReadWriteLock::lockForRead()
 
144
{
 
145
    QMutexLocker lock(&d->mutex);
 
146
 
 
147
    Qt::HANDLE self = 0;
 
148
    if (d->recursive) {
 
149
        self = QThread::currentThreadId();
 
150
 
 
151
        QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
 
152
        if (it != d->currentReaders.end()) {
 
153
            ++it.value();
 
154
            ++d->accessCount;
 
155
            Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::lockForRead()",
 
156
                       "Overflow in lock counter");
 
157
            return;
 
158
        }
 
159
    }
 
160
 
 
161
    while (d->accessCount < 0 || d->waitingWriters) {
 
162
        ++d->waitingReaders;
 
163
        d->readerWait.wait(&d->mutex);
 
164
        --d->waitingReaders;
 
165
    }
 
166
    if (d->recursive)
 
167
        d->currentReaders.insert(self, 1);
 
168
 
 
169
    ++d->accessCount;
 
170
    Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::lockForRead()", "Overflow in lock counter");
 
171
}
 
172
 
 
173
/*!
 
174
    Attempts to lock for reading. If the lock was obtained, this
 
175
    function returns true, otherwise it returns false instead of
 
176
    waiting for the lock to become available, i.e. it does not block.
 
177
 
 
178
    The lock attempt will fail if another thread has locked for
 
179
    writing.
 
180
 
 
181
    If the lock was obtained, the lock must be unlocked with unlock()
 
182
    before another thread can successfully lock it for writing.
 
183
 
 
184
    It is not possible to lock for read if the thread already has
 
185
    locked for write.
 
186
 
 
187
    \sa unlock(), lockForRead()
 
188
*/
 
189
bool QReadWriteLock::tryLockForRead()
 
190
{
 
191
    QMutexLocker lock(&d->mutex);
 
192
 
 
193
    Qt::HANDLE self = 0;
 
194
    if (d->recursive) {
 
195
        self = QThread::currentThreadId();
 
196
 
 
197
        QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
 
198
        if (it != d->currentReaders.end()) {
 
199
            ++it.value();
 
200
            ++d->accessCount;
 
201
            Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::tryLockForRead()",
 
202
                       "Overflow in lock counter");
 
203
            return true;
 
204
        }
 
205
    }
 
206
 
 
207
    if (d->accessCount < 0)
 
208
        return false;
 
209
    if (d->recursive)
 
210
        d->currentReaders.insert(self, 1);
 
211
 
 
212
    ++d->accessCount;
 
213
    Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::tryLockForRead()", "Overflow in lock counter");
 
214
 
 
215
    return true;
 
216
}
 
217
 
 
218
/*! \overload
 
219
 
 
220
    Attempts to lock for reading. This function returns true if the
 
221
    lock was obtained; otherwise it returns false. If another thread
 
222
    has locked for writing, this function will wait for at most \a
 
223
    timeout milliseconds for the lock to become available.
 
224
 
 
225
    Note: Passing a negative number as the \a timeout is equivalent to
 
226
    calling lockForRead(), i.e. this function will wait forever until
 
227
    lock can be locked for reading when \a timeout is negative.
 
228
 
 
229
    If the lock was obtained, the lock must be unlocked with unlock()
 
230
    before another thread can successfully lock it for writing.
 
231
 
 
232
    It is not possible to lock for read if the thread already has
 
233
    locked for write.
 
234
 
 
235
    \sa unlock(), lockForRead()
 
236
*/
 
237
bool QReadWriteLock::tryLockForRead(int timeout)
 
238
{
 
239
    QMutexLocker lock(&d->mutex);
 
240
 
 
241
    Qt::HANDLE self = 0;
 
242
    if (d->recursive) {
 
243
        self = QThread::currentThreadId();
 
244
 
 
245
        QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
 
246
        if (it != d->currentReaders.end()) {
 
247
            ++it.value();
 
248
            ++d->accessCount;
 
249
            Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::tryLockForRead()",
 
250
                       "Overflow in lock counter");
 
251
            return true;
 
252
        }
 
253
    }
 
254
 
 
255
    while (d->accessCount < 0 || d->waitingWriters) {
 
256
        ++d->waitingReaders;
 
257
        bool success = d->readerWait.wait(&d->mutex, timeout < 0 ? ULONG_MAX : ulong(timeout));
 
258
        --d->waitingReaders;
 
259
        if (!success)
 
260
            return false;
 
261
    }
 
262
    if (d->recursive)
 
263
        d->currentReaders.insert(self, 1);
 
264
 
 
265
    ++d->accessCount;
 
266
    Q_ASSERT_X(d->accessCount > 0, "QReadWriteLock::tryLockForRead()", "Overflow in lock counter");
 
267
 
 
268
    return true;
 
269
}
 
270
 
 
271
/*!
 
272
    Locks the lock for writing. This function will block the current
 
273
    thread if another thread (including the current) has locked for
 
274
    reading or writing (unless the lock has been created using the
 
275
    \l{QReadWriteLock::Recursive} mode).
 
276
 
 
277
    It is not possible to lock for write if the thread already has
 
278
    locked for read.
 
279
 
 
280
    \sa unlock(), lockForRead(), tryLockForWrite()
 
281
*/
 
282
void QReadWriteLock::lockForWrite()
 
283
{
 
284
    QMutexLocker lock(&d->mutex);
 
285
 
 
286
    Qt::HANDLE self = 0;
 
287
    if (d->recursive) {
 
288
        self = QThread::currentThreadId();
 
289
 
 
290
        if (d->currentWriter == self) {
 
291
            --d->accessCount;
 
292
            Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::lockForWrite()",
 
293
                       "Overflow in lock counter");
 
294
            return;
 
295
        }
 
296
    }
 
297
 
 
298
    while (d->accessCount != 0) {
 
299
        ++d->waitingWriters;
 
300
        d->writerWait.wait(&d->mutex);
 
301
        --d->waitingWriters;
 
302
    }
 
303
    if (d->recursive)
 
304
        d->currentWriter = self;
 
305
 
 
306
    --d->accessCount;
 
307
    Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::lockForWrite()", "Overflow in lock counter");
 
308
}
 
309
 
 
310
/*!
 
311
    Attempts to lock for writing. If the lock was obtained, this
 
312
    function returns true; otherwise, it returns false immediately.
 
313
 
 
314
    The lock attempt will fail if another thread has locked for
 
315
    reading or writing.
 
316
 
 
317
    If the lock was obtained, the lock must be unlocked with unlock()
 
318
    before another thread can successfully lock it.
 
319
 
 
320
    It is not possible to lock for write if the thread already has
 
321
    locked for read.
 
322
 
 
323
    \sa unlock(), lockForWrite()
 
324
*/
 
325
bool QReadWriteLock::tryLockForWrite()
 
326
{
 
327
    QMutexLocker lock(&d->mutex);
 
328
 
 
329
    Qt::HANDLE self = 0;
 
330
    if (d->recursive) {
 
331
        self = QThread::currentThreadId();
 
332
 
 
333
        if (d->currentWriter == self) {
 
334
            --d->accessCount;
 
335
            Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::lockForWrite()",
 
336
                       "Overflow in lock counter");
 
337
            return true;
 
338
        }
 
339
    }
 
340
 
 
341
    if (d->accessCount != 0)
 
342
        return false;
 
343
    if (d->recursive)
 
344
        d->currentWriter = self;
 
345
 
 
346
    --d->accessCount;
 
347
    Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::tryLockForWrite()",
 
348
               "Overflow in lock counter");
 
349
 
 
350
    return true;
 
351
}
 
352
 
 
353
/*! \overload
 
354
 
 
355
    Attempts to lock for writing. This function returns true if the
 
356
    lock was obtained; otherwise it returns false. If another thread
 
357
    has locked for reading or writing, this function will wait for at
 
358
    most \a timeout milliseconds for the lock to become available.
 
359
 
 
360
    Note: Passing a negative number as the \a timeout is equivalent to
 
361
    calling lockForWrite(), i.e. this function will wait forever until
 
362
    lock can be locked for writing when \a timeout is negative.
 
363
 
 
364
    If the lock was obtained, the lock must be unlocked with unlock()
 
365
    before another thread can successfully lock it.
 
366
 
 
367
    It is not possible to lock for write if the thread already has
 
368
    locked for read.
 
369
 
 
370
    \sa unlock(), lockForWrite()
 
371
*/
 
372
bool QReadWriteLock::tryLockForWrite(int timeout)
 
373
{
 
374
    QMutexLocker lock(&d->mutex);
 
375
 
 
376
    Qt::HANDLE self = 0;
 
377
    if (d->recursive) {
 
378
        self = QThread::currentThreadId();
 
379
 
 
380
        if (d->currentWriter == self) {
 
381
            --d->accessCount;
 
382
            Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::lockForWrite()",
 
383
                       "Overflow in lock counter");
 
384
            return true;
 
385
        }
 
386
    }
 
387
 
 
388
    while (d->accessCount != 0) {
 
389
        ++d->waitingWriters;
 
390
        bool success = d->writerWait.wait(&d->mutex, timeout < 0 ? ULONG_MAX : ulong(timeout));
 
391
        --d->waitingWriters;
 
392
 
 
393
        if (!success)
 
394
            return false;
 
395
    }
 
396
    if (d->recursive)
 
397
        d->currentWriter = self;
 
398
 
 
399
    --d->accessCount;
 
400
    Q_ASSERT_X(d->accessCount < 0, "QReadWriteLock::tryLockForWrite()",
 
401
               "Overflow in lock counter");
 
402
 
 
403
    return true;
 
404
}
 
405
 
 
406
/*!
 
407
    Unlocks the lock.
 
408
 
 
409
    Attempting to unlock a lock that is not locked is an error, and will result
 
410
    in program termination.
 
411
 
 
412
    \sa lockForRead(), lockForWrite(), tryLockForRead(), tryLockForWrite()
 
413
*/
 
414
void QReadWriteLock::unlock()
 
415
{
 
416
    QMutexLocker lock(&d->mutex);
 
417
 
 
418
    Q_ASSERT_X(d->accessCount != 0, "QReadWriteLock::unlock()", "Cannot unlock an unlocked lock");
 
419
 
 
420
    bool unlocked = false;
 
421
    if (d->accessCount > 0) {
 
422
        // releasing a read lock
 
423
        if (d->recursive) {
 
424
            Qt::HANDLE self = QThread::currentThreadId();
 
425
            QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
 
426
            if (it != d->currentReaders.end()) {
 
427
                if (--it.value() <= 0)
 
428
                    d->currentReaders.erase(it);
 
429
            }
 
430
        }
 
431
 
 
432
        unlocked = --d->accessCount == 0;
 
433
    } else if (d->accessCount < 0 && ++d->accessCount == 0) {
 
434
        // released a write lock
 
435
        unlocked = true;
 
436
        d->currentWriter = 0;
 
437
    }
 
438
 
 
439
    if (unlocked) {
 
440
        if (d->waitingWriters) {
 
441
            d->writerWait.wakeOne();
 
442
        } else if (d->waitingReaders) {
 
443
            d->readerWait.wakeAll();
 
444
        }
 
445
    }
 
446
}
 
447
 
 
448
/*!
 
449
    \class QReadLocker
 
450
    \inmodule QtCore
 
451
    \brief The QReadLocker class is a convenience class that
 
452
    simplifies locking and unlocking read-write locks for read access.
 
453
 
 
454
    \threadsafe
 
455
 
 
456
    \ingroup thread
 
457
 
 
458
    The purpose of QReadLocker (and QWriteLocker) is to simplify
 
459
    QReadWriteLock locking and unlocking. Locking and unlocking
 
460
    statements or in exception handling code is error-prone and
 
461
    difficult to debug. QReadLocker can be used in such situations
 
462
    to ensure that the state of the lock is always well-defined.
 
463
 
 
464
    Here's an example that uses QReadLocker to lock and unlock a
 
465
    read-write lock for reading:
 
466
 
 
467
    \snippet code/src_corelib_thread_qreadwritelock.cpp 1
 
468
 
 
469
    It is equivalent to the following code:
 
470
 
 
471
    \snippet code/src_corelib_thread_qreadwritelock.cpp 2
 
472
 
 
473
    The QMutexLocker documentation shows examples where the use of a
 
474
    locker object greatly simplifies programming.
 
475
 
 
476
    \sa QWriteLocker, QReadWriteLock
 
477
*/
 
478
 
 
479
/*!
 
480
    \fn QReadLocker::QReadLocker(QReadWriteLock *lock)
 
481
 
 
482
    Constructs a QReadLocker and locks \a lock for reading. The lock
 
483
    will be unlocked when the QReadLocker is destroyed. If \c lock is
 
484
    zero, QReadLocker does nothing.
 
485
 
 
486
    \sa QReadWriteLock::lockForRead()
 
487
*/
 
488
 
 
489
/*!
 
490
    \fn QReadLocker::~QReadLocker()
 
491
 
 
492
    Destroys the QReadLocker and unlocks the lock that was passed to
 
493
    the constructor.
 
494
 
 
495
    \sa QReadWriteLock::unlock()
 
496
*/
 
497
 
 
498
/*!
 
499
    \fn void QReadLocker::unlock()
 
500
 
 
501
    Unlocks the lock associated with this locker.
 
502
 
 
503
    \sa QReadWriteLock::unlock()
 
504
*/
 
505
 
 
506
/*!
 
507
    \fn void QReadLocker::relock()
 
508
 
 
509
    Relocks an unlocked lock.
 
510
 
 
511
    \sa unlock()
 
512
*/
 
513
 
 
514
/*!
 
515
    \fn QReadWriteLock *QReadLocker::readWriteLock() const
 
516
 
 
517
    Returns a pointer to the read-write lock that was passed
 
518
    to the constructor.
 
519
*/
 
520
 
 
521
/*!
 
522
    \class QWriteLocker
 
523
    \inmodule QtCore
 
524
    \brief The QWriteLocker class is a convenience class that
 
525
    simplifies locking and unlocking read-write locks for write access.
 
526
 
 
527
    \threadsafe
 
528
 
 
529
    \ingroup thread
 
530
 
 
531
    The purpose of QWriteLocker (and QReadLocker) is to simplify
 
532
    QReadWriteLock locking and unlocking. Locking and unlocking
 
533
    statements or in exception handling code is error-prone and
 
534
    difficult to debug. QWriteLocker can be used in such situations
 
535
    to ensure that the state of the lock is always well-defined.
 
536
 
 
537
    Here's an example that uses QWriteLocker to lock and unlock a
 
538
    read-write lock for writing:
 
539
 
 
540
    \snippet code/src_corelib_thread_qreadwritelock.cpp 3
 
541
 
 
542
    It is equivalent to the following code:
 
543
 
 
544
    \snippet code/src_corelib_thread_qreadwritelock.cpp 4
 
545
 
 
546
    The QMutexLocker documentation shows examples where the use of a
 
547
    locker object greatly simplifies programming.
 
548
 
 
549
    \sa QReadLocker, QReadWriteLock
 
550
*/
 
551
 
 
552
/*!
 
553
    \fn QWriteLocker::QWriteLocker(QReadWriteLock *lock)
 
554
 
 
555
    Constructs a QWriteLocker and locks \a lock for writing. The lock
 
556
    will be unlocked when the QWriteLocker is destroyed. If \c lock is
 
557
    zero, QWriteLocker does nothing.
 
558
 
 
559
    \sa QReadWriteLock::lockForWrite()
 
560
*/
 
561
 
 
562
/*!
 
563
    \fn QWriteLocker::~QWriteLocker()
 
564
 
 
565
    Destroys the QWriteLocker and unlocks the lock that was passed to
 
566
    the constructor.
 
567
 
 
568
    \sa QReadWriteLock::unlock()
 
569
*/
 
570
 
 
571
/*!
 
572
    \fn void QWriteLocker::unlock()
 
573
 
 
574
    Unlocks the lock associated with this locker.
 
575
 
 
576
    \sa QReadWriteLock::unlock()
 
577
*/
 
578
 
 
579
/*!
 
580
    \fn void QWriteLocker::relock()
 
581
 
 
582
    Relocks an unlocked lock.
 
583
 
 
584
    \sa unlock()
 
585
*/
 
586
 
 
587
/*!
 
588
    \fn QReadWriteLock *QWriteLocker::readWriteLock() const
 
589
 
 
590
    Returns a pointer to the read-write lock that was passed
 
591
    to the constructor.
 
592
*/
 
593
 
 
594
QT_END_NAMESPACE
 
595
 
 
596
#endif // QT_NO_THREAD