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