summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread/qreadwritelock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/thread/qreadwritelock.cpp')
-rw-r--r--src/corelib/thread/qreadwritelock.cpp265
1 files changed, 97 insertions, 168 deletions
diff --git a/src/corelib/thread/qreadwritelock.cpp b/src/corelib/thread/qreadwritelock.cpp
index 4c2a9c4251..e79bed2231 100644
--- a/src/corelib/thread/qreadwritelock.cpp
+++ b/src/corelib/thread/qreadwritelock.cpp
@@ -1,52 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformdefs.h"
#include "qreadwritelock.h"
-#include "qmutex.h"
#include "qthread.h"
-#include "qwaitcondition.h"
#include "qreadwritelock_p.h"
-#include "qelapsedtimer.h"
#include "private/qfreelist_p.h"
#include "private/qlocking_p.h"
@@ -66,21 +27,22 @@ QT_BEGIN_NAMESPACE
* - In any other case, d_ptr points to an actual QReadWriteLockPrivate.
*/
+using namespace QReadWriteLockStates;
namespace {
-using ms = std::chrono::milliseconds;
+using steady_clock = std::chrono::steady_clock;
-enum {
- StateMask = 0x3,
- StateLockedForRead = 0x1,
- StateLockedForWrite = 0x2,
-};
const auto dummyLockedForRead = reinterpret_cast<QReadWriteLockPrivate *>(quintptr(StateLockedForRead));
const auto dummyLockedForWrite = reinterpret_cast<QReadWriteLockPrivate *>(quintptr(StateLockedForWrite));
inline bool isUncontendedLocked(const QReadWriteLockPrivate *d)
{ return quintptr(d) & StateMask; }
}
+static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
+ QDeadlineTimer timeout, QReadWriteLockPrivate *d);
+static bool contendedTryLockForWrite(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
+ QDeadlineTimer timeout, QReadWriteLockPrivate *d);
+
/*! \class QReadWriteLock
\inmodule QtCore
\brief The QReadWriteLock class provides read-write locking.
@@ -138,6 +100,7 @@ inline bool isUncontendedLocked(const QReadWriteLockPrivate *d)
*/
/*!
+ \fn QReadWriteLock::QReadWriteLock(RecursionMode recursionMode)
\since 4.4
Constructs a QReadWriteLock object in the given \a recursionMode.
@@ -146,21 +109,22 @@ inline bool isUncontendedLocked(const QReadWriteLockPrivate *d)
\sa lockForRead(), lockForWrite(), RecursionMode
*/
-QReadWriteLock::QReadWriteLock(RecursionMode recursionMode)
- : d_ptr(recursionMode == Recursive ? new QReadWriteLockPrivate(true) : nullptr)
+QReadWriteLockPrivate *QReadWriteLock::initRecursive()
{
- Q_ASSERT_X(!(quintptr(d_ptr.loadRelaxed()) & StateMask), "QReadWriteLock::QReadWriteLock", "bad d_ptr alignment");
+ auto d = new QReadWriteLockPrivate(true);
+ Q_ASSERT_X(!(quintptr(d) & StateMask), "QReadWriteLock::QReadWriteLock", "bad d_ptr alignment");
+ return d;
}
/*!
+ \fn QReadWriteLock::~QReadWriteLock()
Destroys the QReadWriteLock object.
\warning Destroying a read-write lock that is in use may result
in undefined behavior.
*/
-QReadWriteLock::~QReadWriteLock()
+void QReadWriteLock::destroyRecursive(QReadWriteLockPrivate *d)
{
- auto d = d_ptr.loadRelaxed();
if (isUncontendedLocked(d)) {
qWarning("QReadWriteLock: destroying locked QReadWriteLock");
return;
@@ -169,6 +133,7 @@ QReadWriteLock::~QReadWriteLock()
}
/*!
+ \fn QReadWriteLock::lockForRead()
Locks the lock for reading. This function will block the current
thread if another thread has locked for writing.
@@ -177,20 +142,18 @@ QReadWriteLock::~QReadWriteLock()
\sa unlock(), lockForWrite(), tryLockForRead()
*/
-void QReadWriteLock::lockForRead()
-{
- if (d_ptr.testAndSetAcquire(nullptr, dummyLockedForRead))
- return;
- tryLockForRead(-1);
-}
/*!
- Attempts to lock for reading. If the lock was obtained, this
- function returns \c true, otherwise it returns \c false instead of
- waiting for the lock to become available, i.e. it does not block.
+ \fn bool QReadWriteLock::tryLockForRead(int timeout)
- The lock attempt will fail if another thread has locked for
- writing.
+ Attempts to lock for reading. This function returns \c true if the
+ lock was obtained; otherwise it returns \c false. If another thread
+ has locked for writing, this function will wait for at most \a
+ timeout milliseconds for the lock to become available.
+
+ Note: Passing a negative number as the \a timeout is equivalent to
+ calling lockForRead(), i.e. this function will wait forever until
+ lock can be locked for reading when \a timeout is negative.
If the lock was obtained, the lock must be unlocked with unlock()
before another thread can successfully lock it for writing.
@@ -200,21 +163,15 @@ void QReadWriteLock::lockForRead()
\sa unlock(), lockForRead()
*/
-bool QReadWriteLock::tryLockForRead()
-{
- return tryLockForRead(0);
-}
-
-/*! \overload
- Attempts to lock for reading. This function returns \c true if the
- lock was obtained; otherwise it returns \c false. If another thread
- has locked for writing, this function will wait for at most \a
- timeout milliseconds for the lock to become available.
+/*!
+ \overload
+ \since 6.6
- Note: Passing a negative number as the \a timeout is equivalent to
- calling lockForRead(), i.e. this function will wait forever until
- lock can be locked for reading when \a timeout is negative.
+ Attempts to lock for reading. This function returns \c true if the lock was
+ obtained; otherwise it returns \c false. If another thread has locked for
+ writing, this function will wait until \a timeout expires for the lock to
+ become available.
If the lock was obtained, the lock must be unlocked with unlock()
before another thread can successfully lock it for writing.
@@ -224,13 +181,18 @@ bool QReadWriteLock::tryLockForRead()
\sa unlock(), lockForRead()
*/
-bool QReadWriteLock::tryLockForRead(int timeout)
+bool QReadWriteLock::tryLockForRead(QDeadlineTimer timeout)
{
// Fast case: non contended:
- QReadWriteLockPrivate *d;
- if (d_ptr.testAndSetAcquire(nullptr, dummyLockedForRead, d))
+ QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
+ if (d == nullptr && d_ptr.testAndSetAcquire(nullptr, dummyLockedForRead, d))
return true;
+ return contendedTryLockForRead(d_ptr, timeout, d);
+}
+Q_NEVER_INLINE static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
+ QDeadlineTimer timeout, QReadWriteLockPrivate *d)
+{
while (true) {
if (d == nullptr) {
if (!d_ptr.testAndSetAcquire(nullptr, dummyLockedForRead, d))
@@ -249,7 +211,7 @@ bool QReadWriteLock::tryLockForRead(int timeout)
}
if (d == dummyLockedForWrite) {
- if (!timeout)
+ if (timeout.hasExpired())
return false;
// locked for write, assign a d_ptr and wait.
@@ -284,6 +246,7 @@ bool QReadWriteLock::tryLockForRead(int timeout)
}
/*!
+ \fn QReadWriteLock::lockForWrite()
Locks the lock for writing. This function will block the current
thread if another thread (including the current) has locked for
reading or writing (unless the lock has been created using the
@@ -294,17 +257,18 @@ bool QReadWriteLock::tryLockForRead(int timeout)
\sa unlock(), lockForRead(), tryLockForWrite()
*/
-void QReadWriteLock::lockForWrite()
-{
- tryLockForWrite(-1);
-}
/*!
- Attempts to lock for writing. If the lock was obtained, this
- function returns \c true; otherwise, it returns \c false immediately.
+ \fn QReadWriteLock::tryLockForWrite(int timeout)
+
+ Attempts to lock for writing. This function returns \c true if the
+ lock was obtained; otherwise it returns \c false. If another thread
+ has locked for reading or writing, this function will wait for at
+ most \a timeout milliseconds for the lock to become available.
- The lock attempt will fail if another thread has locked for
- reading or writing.
+ Note: Passing a negative number as the \a timeout is equivalent to
+ calling lockForWrite(), i.e. this function will wait forever until
+ lock can be locked for writing when \a timeout is negative.
If the lock was obtained, the lock must be unlocked with unlock()
before another thread can successfully lock it.
@@ -314,21 +278,15 @@ void QReadWriteLock::lockForWrite()
\sa unlock(), lockForWrite()
*/
-bool QReadWriteLock::tryLockForWrite()
-{
- return tryLockForWrite(0);
-}
-
-/*! \overload
- Attempts to lock for writing. This function returns \c true if the
- lock was obtained; otherwise it returns \c false. If another thread
- has locked for reading or writing, this function will wait for at
- most \a timeout milliseconds for the lock to become available.
+/*!
+ \overload
+ \since 6.6
- Note: Passing a negative number as the \a timeout is equivalent to
- calling lockForWrite(), i.e. this function will wait forever until
- lock can be locked for writing when \a timeout is negative.
+ Attempts to lock for writing. This function returns \c true if the lock was
+ obtained; otherwise it returns \c false. If another thread has locked for
+ reading or writing, this function will wait until \a timeout expires for
+ the lock to become available.
If the lock was obtained, the lock must be unlocked with unlock()
before another thread can successfully lock it.
@@ -338,13 +296,18 @@ bool QReadWriteLock::tryLockForWrite()
\sa unlock(), lockForWrite()
*/
-bool QReadWriteLock::tryLockForWrite(int timeout)
+bool QReadWriteLock::tryLockForWrite(QDeadlineTimer timeout)
{
// Fast case: non contended:
- QReadWriteLockPrivate *d;
- if (d_ptr.testAndSetAcquire(nullptr, dummyLockedForWrite, d))
+ QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
+ if (d == nullptr && d_ptr.testAndSetAcquire(nullptr, dummyLockedForWrite, d))
return true;
+ return contendedTryLockForWrite(d_ptr, timeout, d);
+}
+Q_NEVER_INLINE static bool contendedTryLockForWrite(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
+ QDeadlineTimer timeout, QReadWriteLockPrivate *d)
+{
while (true) {
if (d == nullptr) {
if (!d_ptr.testAndSetAcquire(d, dummyLockedForWrite, d))
@@ -353,7 +316,7 @@ bool QReadWriteLock::tryLockForWrite(int timeout)
}
if (isUncontendedLocked(d)) {
- if (!timeout)
+ if (timeout.hasExpired())
return false;
// locked for either read or write, assign a d_ptr and wait.
@@ -447,42 +410,16 @@ void QReadWriteLock::unlock()
}
}
-/*! \internal Helper for QWaitCondition::wait */
-QReadWriteLock::StateForWaitCondition QReadWriteLock::stateForWaitCondition() const
-{
- QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
- switch (quintptr(d) & StateMask) {
- case StateLockedForRead: return LockedForRead;
- case StateLockedForWrite: return LockedForWrite;
- }
-
- if (!d)
- return Unlocked;
- if (d->writerCount > 1)
- return RecursivelyLocked;
- else if (d->writerCount == 1)
- return LockedForWrite;
- return LockedForRead;
-
-}
-
-bool QReadWriteLockPrivate::lockForRead(std::unique_lock<QtPrivate::mutex> &lock, int timeout)
+bool QReadWriteLockPrivate::lockForRead(std::unique_lock<std::mutex> &lock, QDeadlineTimer timeout)
{
Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function
- QElapsedTimer t;
- if (timeout > 0)
- t.start();
-
while (waitingWriters || writerCount) {
- if (timeout == 0)
+ if (timeout.hasExpired())
return false;
- if (timeout > 0) {
- auto elapsed = t.elapsed();
- if (elapsed > timeout)
- return false;
+ if (!timeout.isForever()) {
waitingReaders++;
- readerCond.wait_for(lock, ms{timeout - elapsed});
+ readerCond.wait_until(lock, timeout.deadline<steady_clock>());
} else {
waitingReaders++;
readerCond.wait(lock);
@@ -494,29 +431,22 @@ bool QReadWriteLockPrivate::lockForRead(std::unique_lock<QtPrivate::mutex> &lock
return true;
}
-bool QReadWriteLockPrivate::lockForWrite(std::unique_lock<QtPrivate::mutex> &lock, int timeout)
+bool QReadWriteLockPrivate::lockForWrite(std::unique_lock<std::mutex> &lock, QDeadlineTimer timeout)
{
Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function
- QElapsedTimer t;
- if (timeout > 0)
- t.start();
-
while (readerCount || writerCount) {
- if (timeout == 0)
- return false;
- if (timeout > 0) {
- auto elapsed = t.elapsed();
- if (elapsed > timeout) {
- if (waitingReaders && !waitingWriters && !writerCount) {
- // We timed out and now there is no more writers or waiting writers, but some
- // readers were queued (probably because of us). Wake the waiting readers.
- readerCond.notify_all();
- }
- return false;
+ if (timeout.hasExpired()) {
+ if (waitingReaders && !waitingWriters && !writerCount) {
+ // We timed out and now there is no more writers or waiting writers, but some
+ // readers were queued (probably because of us). Wake the waiting readers.
+ readerCond.notify_all();
}
+ return false;
+ }
+ if (!timeout.isForever()) {
waitingWriters++;
- writerCond.wait_for(lock, ms{timeout - elapsed});
+ writerCond.wait_until(lock, timeout.deadline<steady_clock>());
} else {
waitingWriters++;
writerCond.wait(lock);
@@ -544,7 +474,7 @@ static auto handleEquals(Qt::HANDLE handle)
return [handle](QReadWriteLockPrivate::Reader reader) { return reader.handle == handle; };
}
-bool QReadWriteLockPrivate::recursiveLockForRead(int timeout)
+bool QReadWriteLockPrivate::recursiveLockForRead(QDeadlineTimer timeout)
{
Q_ASSERT(recursive);
auto lock = qt_unique_lock(mutex);
@@ -566,7 +496,7 @@ bool QReadWriteLockPrivate::recursiveLockForRead(int timeout)
return true;
}
-bool QReadWriteLockPrivate::recursiveLockForWrite(int timeout)
+bool QReadWriteLockPrivate::recursiveLockForWrite(QDeadlineTimer timeout)
{
Q_ASSERT(recursive);
auto lock = qt_unique_lock(mutex);
@@ -615,25 +545,24 @@ void QReadWriteLockPrivate::recursiveUnlock()
// The freelist management
namespace {
-struct FreeListConstants : QFreeListDefaultConstants {
+struct QReadWriteLockFreeListConstants : QFreeListDefaultConstants
+{
enum { BlockCount = 4, MaxIndex=0xffff };
static const int Sizes[BlockCount];
};
-const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = {
- 16,
- 128,
- 1024,
- FreeListConstants::MaxIndex - (16 + 128 + 1024)
-};
+Q_CONSTINIT const int
+ QReadWriteLockFreeListConstants::Sizes[QReadWriteLockFreeListConstants::BlockCount] = {
+ 16, 128, 1024, QReadWriteLockFreeListConstants::MaxIndex - (16 + 128 + 1024)
+ };
-typedef QFreeList<QReadWriteLockPrivate, FreeListConstants> FreeList;
-Q_GLOBAL_STATIC(FreeList, freelist);
+typedef QFreeList<QReadWriteLockPrivate, QReadWriteLockFreeListConstants> QReadWriteLockFreeList;
+Q_GLOBAL_STATIC(QReadWriteLockFreeList, qrwl_freelist);
}
QReadWriteLockPrivate *QReadWriteLockPrivate::allocate()
{
- int i = freelist->next();
- QReadWriteLockPrivate *d = &(*freelist)[i];
+ int i = qrwl_freelist->next();
+ QReadWriteLockPrivate *d = &(*qrwl_freelist)[i];
d->id = i;
Q_ASSERT(!d->recursive);
Q_ASSERT(!d->waitingReaders && !d->waitingWriters && !d->readerCount && !d->writerCount);
@@ -644,7 +573,7 @@ void QReadWriteLockPrivate::release()
{
Q_ASSERT(!recursive);
Q_ASSERT(!waitingReaders && !waitingWriters && !readerCount && !writerCount);
- freelist->release(id);
+ qrwl_freelist->release(id);
}
/*!