QReadWriteLock: add tryLockForXxx overloads taking QDeadlineTimer
This propagates inside the internals, ending up in wait_until calls in the internal std::condition_variable. For systems with proper support for monotonic waiting (Linux, FreeBSD), this should improve performance. We could even remove the hasExpired() check and pass a time point in the past too. Right now, there's a minor performance drawback for tryLockForXxxx(), because we will make at least two system calls to get the time. Change-Id: I6f518d59e63249ddbf43fffd1759fc5b2e40256a Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
fcae43237b
commit
63704529b7
@ -31,7 +31,7 @@ QT_BEGIN_NAMESPACE
|
|||||||
using namespace QReadWriteLockStates;
|
using namespace QReadWriteLockStates;
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using ms = std::chrono::milliseconds;
|
using steady_clock = std::chrono::steady_clock;
|
||||||
|
|
||||||
const auto dummyLockedForRead = reinterpret_cast<QReadWriteLockPrivate *>(quintptr(StateLockedForRead));
|
const auto dummyLockedForRead = reinterpret_cast<QReadWriteLockPrivate *>(quintptr(StateLockedForRead));
|
||||||
const auto dummyLockedForWrite = reinterpret_cast<QReadWriteLockPrivate *>(quintptr(StateLockedForWrite));
|
const auto dummyLockedForWrite = reinterpret_cast<QReadWriteLockPrivate *>(quintptr(StateLockedForWrite));
|
||||||
@ -40,9 +40,9 @@ inline bool isUncontendedLocked(const QReadWriteLockPrivate *d)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
|
static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
|
||||||
int timeout, QReadWriteLockPrivate *d);
|
QDeadlineTimer timeout, QReadWriteLockPrivate *d);
|
||||||
static bool contendedTryLockForWrite(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
|
static bool contendedTryLockForWrite(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
|
||||||
int timeout, QReadWriteLockPrivate *d);
|
QDeadlineTimer timeout, QReadWriteLockPrivate *d);
|
||||||
|
|
||||||
/*! \class QReadWriteLock
|
/*! \class QReadWriteLock
|
||||||
\inmodule QtCore
|
\inmodule QtCore
|
||||||
@ -143,6 +143,7 @@ QReadWriteLock::~QReadWriteLock()
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
\fn bool QReadWriteLock::tryLockForRead(int timeout)
|
||||||
|
|
||||||
Attempts to lock for reading. This function returns \c true if the
|
Attempts to lock for reading. This function returns \c true if the
|
||||||
lock was obtained; otherwise it returns \c false. If another thread
|
lock was obtained; otherwise it returns \c false. If another thread
|
||||||
@ -161,7 +162,25 @@ QReadWriteLock::~QReadWriteLock()
|
|||||||
|
|
||||||
\sa unlock(), lockForRead()
|
\sa unlock(), lockForRead()
|
||||||
*/
|
*/
|
||||||
bool QReadWriteLock::tryLockForRead(int timeout)
|
|
||||||
|
/*!
|
||||||
|
\overload
|
||||||
|
\since 6.6
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
It is not possible to lock for read if the thread already has
|
||||||
|
locked for write.
|
||||||
|
|
||||||
|
\sa unlock(), lockForRead()
|
||||||
|
*/
|
||||||
|
bool QReadWriteLock::tryLockForRead(QDeadlineTimer timeout)
|
||||||
{
|
{
|
||||||
// Fast case: non contended:
|
// Fast case: non contended:
|
||||||
QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
|
QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
|
||||||
@ -171,7 +190,7 @@ bool QReadWriteLock::tryLockForRead(int timeout)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Q_NEVER_INLINE static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
|
Q_NEVER_INLINE static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
|
||||||
int timeout, QReadWriteLockPrivate *d)
|
QDeadlineTimer timeout, QReadWriteLockPrivate *d)
|
||||||
{
|
{
|
||||||
while (true) {
|
while (true) {
|
||||||
if (d == nullptr) {
|
if (d == nullptr) {
|
||||||
@ -191,7 +210,7 @@ Q_NEVER_INLINE static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLock
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (d == dummyLockedForWrite) {
|
if (d == dummyLockedForWrite) {
|
||||||
if (!timeout)
|
if (timeout.hasExpired())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// locked for write, assign a d_ptr and wait.
|
// locked for write, assign a d_ptr and wait.
|
||||||
@ -239,6 +258,8 @@ Q_NEVER_INLINE static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLock
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
\fn QReadWriteLock::tryLockForWrite(int timeout)
|
||||||
|
|
||||||
Attempts to lock for writing. This function returns \c true if the
|
Attempts to lock for writing. This function returns \c true if the
|
||||||
lock was obtained; otherwise it returns \c false. If another thread
|
lock was obtained; otherwise it returns \c false. If another thread
|
||||||
has locked for reading or writing, this function will wait for at
|
has locked for reading or writing, this function will wait for at
|
||||||
@ -256,7 +277,25 @@ Q_NEVER_INLINE static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLock
|
|||||||
|
|
||||||
\sa unlock(), lockForWrite()
|
\sa unlock(), lockForWrite()
|
||||||
*/
|
*/
|
||||||
bool QReadWriteLock::tryLockForWrite(int timeout)
|
|
||||||
|
/*!
|
||||||
|
\overload
|
||||||
|
\since 6.6
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
It is not possible to lock for write if the thread already has
|
||||||
|
locked for read.
|
||||||
|
|
||||||
|
\sa unlock(), lockForWrite()
|
||||||
|
*/
|
||||||
|
bool QReadWriteLock::tryLockForWrite(QDeadlineTimer timeout)
|
||||||
{
|
{
|
||||||
// Fast case: non contended:
|
// Fast case: non contended:
|
||||||
QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
|
QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
|
||||||
@ -266,7 +305,7 @@ bool QReadWriteLock::tryLockForWrite(int timeout)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Q_NEVER_INLINE static bool contendedTryLockForWrite(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
|
Q_NEVER_INLINE static bool contendedTryLockForWrite(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
|
||||||
int timeout, QReadWriteLockPrivate *d)
|
QDeadlineTimer timeout, QReadWriteLockPrivate *d)
|
||||||
{
|
{
|
||||||
while (true) {
|
while (true) {
|
||||||
if (d == nullptr) {
|
if (d == nullptr) {
|
||||||
@ -276,7 +315,7 @@ Q_NEVER_INLINE static bool contendedTryLockForWrite(QAtomicPointer<QReadWriteLoc
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isUncontendedLocked(d)) {
|
if (isUncontendedLocked(d)) {
|
||||||
if (!timeout)
|
if (timeout.hasExpired())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// locked for either read or write, assign a d_ptr and wait.
|
// locked for either read or write, assign a d_ptr and wait.
|
||||||
@ -370,23 +409,16 @@ void QReadWriteLock::unlock()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QReadWriteLockPrivate::lockForRead(std::unique_lock<QtPrivate::mutex> &lock, int timeout)
|
bool QReadWriteLockPrivate::lockForRead(std::unique_lock<QtPrivate::mutex> &lock, QDeadlineTimer timeout)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function
|
Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function
|
||||||
|
|
||||||
QElapsedTimer t;
|
|
||||||
if (timeout > 0)
|
|
||||||
t.start();
|
|
||||||
|
|
||||||
while (waitingWriters || writerCount) {
|
while (waitingWriters || writerCount) {
|
||||||
if (timeout == 0)
|
if (timeout.hasExpired())
|
||||||
return false;
|
|
||||||
if (timeout > 0) {
|
|
||||||
auto elapsed = t.elapsed();
|
|
||||||
if (elapsed > timeout)
|
|
||||||
return false;
|
return false;
|
||||||
|
if (!timeout.isForever()) {
|
||||||
waitingReaders++;
|
waitingReaders++;
|
||||||
readerCond.wait_for(lock, ms{timeout - elapsed});
|
readerCond.wait_until(lock, timeout.deadline<steady_clock>());
|
||||||
} else {
|
} else {
|
||||||
waitingReaders++;
|
waitingReaders++;
|
||||||
readerCond.wait(lock);
|
readerCond.wait(lock);
|
||||||
@ -398,20 +430,12 @@ bool QReadWriteLockPrivate::lockForRead(std::unique_lock<QtPrivate::mutex> &lock
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QReadWriteLockPrivate::lockForWrite(std::unique_lock<QtPrivate::mutex> &lock, int timeout)
|
bool QReadWriteLockPrivate::lockForWrite(std::unique_lock<QtPrivate::mutex> &lock, QDeadlineTimer timeout)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function
|
Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function
|
||||||
|
|
||||||
QElapsedTimer t;
|
|
||||||
if (timeout > 0)
|
|
||||||
t.start();
|
|
||||||
|
|
||||||
while (readerCount || writerCount) {
|
while (readerCount || writerCount) {
|
||||||
if (timeout == 0)
|
if (timeout.hasExpired()) {
|
||||||
return false;
|
|
||||||
if (timeout > 0) {
|
|
||||||
auto elapsed = t.elapsed();
|
|
||||||
if (elapsed > timeout) {
|
|
||||||
if (waitingReaders && !waitingWriters && !writerCount) {
|
if (waitingReaders && !waitingWriters && !writerCount) {
|
||||||
// We timed out and now there is no more writers or waiting writers, but some
|
// 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.
|
// readers were queued (probably because of us). Wake the waiting readers.
|
||||||
@ -419,8 +443,9 @@ bool QReadWriteLockPrivate::lockForWrite(std::unique_lock<QtPrivate::mutex> &loc
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!timeout.isForever()) {
|
||||||
waitingWriters++;
|
waitingWriters++;
|
||||||
writerCond.wait_for(lock, ms{timeout - elapsed});
|
writerCond.wait_until(lock, timeout.deadline<steady_clock>());
|
||||||
} else {
|
} else {
|
||||||
waitingWriters++;
|
waitingWriters++;
|
||||||
writerCond.wait(lock);
|
writerCond.wait(lock);
|
||||||
@ -448,7 +473,7 @@ static auto handleEquals(Qt::HANDLE handle)
|
|||||||
return [handle](QReadWriteLockPrivate::Reader reader) { return reader.handle == handle; };
|
return [handle](QReadWriteLockPrivate::Reader reader) { return reader.handle == handle; };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QReadWriteLockPrivate::recursiveLockForRead(int timeout)
|
bool QReadWriteLockPrivate::recursiveLockForRead(QDeadlineTimer timeout)
|
||||||
{
|
{
|
||||||
Q_ASSERT(recursive);
|
Q_ASSERT(recursive);
|
||||||
auto lock = qt_unique_lock(mutex);
|
auto lock = qt_unique_lock(mutex);
|
||||||
@ -470,7 +495,7 @@ bool QReadWriteLockPrivate::recursiveLockForRead(int timeout)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QReadWriteLockPrivate::recursiveLockForWrite(int timeout)
|
bool QReadWriteLockPrivate::recursiveLockForWrite(QDeadlineTimer timeout)
|
||||||
{
|
{
|
||||||
Q_ASSERT(recursive);
|
Q_ASSERT(recursive);
|
||||||
auto lock = qt_unique_lock(mutex);
|
auto lock = qt_unique_lock(mutex);
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
#define QREADWRITELOCK_H
|
#define QREADWRITELOCK_H
|
||||||
|
|
||||||
#include <QtCore/qglobal.h>
|
#include <QtCore/qglobal.h>
|
||||||
|
#include <QtCore/qdeadlinetimer.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
|
||||||
#if QT_CONFIG(thread)
|
#if QT_CONFIG(thread)
|
||||||
|
|
||||||
class QReadWriteLockPrivate;
|
class QReadWriteLockPrivate;
|
||||||
@ -26,17 +26,30 @@ public:
|
|||||||
#if QT_CORE_REMOVED_SINCE(6, 6)
|
#if QT_CORE_REMOVED_SINCE(6, 6)
|
||||||
bool tryLockForRead();
|
bool tryLockForRead();
|
||||||
#endif
|
#endif
|
||||||
bool tryLockForRead(int timeout = 0);
|
QT_CORE_INLINE_SINCE(6, 6)
|
||||||
|
bool tryLockForRead(int timeout);
|
||||||
|
bool tryLockForRead(QDeadlineTimer timeout = {});
|
||||||
|
|
||||||
QT_CORE_INLINE_SINCE(6, 6)
|
QT_CORE_INLINE_SINCE(6, 6)
|
||||||
void lockForWrite();
|
void lockForWrite();
|
||||||
#if QT_CORE_REMOVED_SINCE(6, 6)
|
#if QT_CORE_REMOVED_SINCE(6, 6)
|
||||||
bool tryLockForWrite();
|
bool tryLockForWrite();
|
||||||
#endif
|
#endif
|
||||||
bool tryLockForWrite(int timeout = 0);
|
QT_CORE_INLINE_SINCE(6, 6)
|
||||||
|
bool tryLockForWrite(int timeout);
|
||||||
|
bool tryLockForWrite(QDeadlineTimer timeout = {});
|
||||||
|
|
||||||
void unlock();
|
void unlock();
|
||||||
|
|
||||||
|
#ifndef Q_QDOC
|
||||||
|
// because tryLockForXxx(QDeadlineTimer::Forever) is the same
|
||||||
|
// as tryLockForXxx(0), which is not forever
|
||||||
|
bool tryLockForRead(QDeadlineTimer::ForeverConstant)
|
||||||
|
{ lockForRead(); return true; }
|
||||||
|
bool tryLockForWrite(QDeadlineTimer::ForeverConstant)
|
||||||
|
{ lockForWrite(); return true; }
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_DISABLE_COPY(QReadWriteLock)
|
Q_DISABLE_COPY(QReadWriteLock)
|
||||||
QAtomicPointer<QReadWriteLockPrivate> d_ptr;
|
QAtomicPointer<QReadWriteLockPrivate> d_ptr;
|
||||||
@ -46,12 +59,22 @@ private:
|
|||||||
#if QT_CORE_INLINE_IMPL_SINCE(6, 6)
|
#if QT_CORE_INLINE_IMPL_SINCE(6, 6)
|
||||||
void QReadWriteLock::lockForRead()
|
void QReadWriteLock::lockForRead()
|
||||||
{
|
{
|
||||||
tryLockForRead(-1);
|
tryLockForRead(QDeadlineTimer(QDeadlineTimer::Forever));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QReadWriteLock::tryLockForRead(int timeout)
|
||||||
|
{
|
||||||
|
return tryLockForRead(QDeadlineTimer(timeout));
|
||||||
}
|
}
|
||||||
|
|
||||||
void QReadWriteLock::lockForWrite()
|
void QReadWriteLock::lockForWrite()
|
||||||
{
|
{
|
||||||
tryLockForWrite(-1);
|
tryLockForWrite(QDeadlineTimer(QDeadlineTimer::Forever));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QReadWriteLock::tryLockForWrite(int timeout)
|
||||||
|
{
|
||||||
|
return tryLockForWrite(QDeadlineTimer(timeout));
|
||||||
}
|
}
|
||||||
#endif // inline since 6.6
|
#endif // inline since 6.6
|
||||||
|
|
||||||
@ -164,10 +187,12 @@ public:
|
|||||||
|
|
||||||
void lockForRead() noexcept { }
|
void lockForRead() noexcept { }
|
||||||
bool tryLockForRead() noexcept { return true; }
|
bool tryLockForRead() noexcept { return true; }
|
||||||
|
bool tryLockForRead(QDeadlineTimer) noexcept { return true; }
|
||||||
bool tryLockForRead(int timeout) noexcept { Q_UNUSED(timeout); return true; }
|
bool tryLockForRead(int timeout) noexcept { Q_UNUSED(timeout); return true; }
|
||||||
|
|
||||||
void lockForWrite() noexcept { }
|
void lockForWrite() noexcept { }
|
||||||
bool tryLockForWrite() noexcept { return true; }
|
bool tryLockForWrite() noexcept { return true; }
|
||||||
|
bool tryLockForWrite(QDeadlineTimer) noexcept { return true; }
|
||||||
bool tryLockForWrite(int timeout) noexcept { Q_UNUSED(timeout); return true; }
|
bool tryLockForWrite(int timeout) noexcept { Q_UNUSED(timeout); return true; }
|
||||||
|
|
||||||
void unlock() noexcept { }
|
void unlock() noexcept { }
|
||||||
|
@ -55,8 +55,8 @@ public:
|
|||||||
const bool recursive;
|
const bool recursive;
|
||||||
|
|
||||||
//Called with the mutex locked
|
//Called with the mutex locked
|
||||||
bool lockForWrite(std::unique_lock<QtPrivate::mutex> &lock, int timeout);
|
bool lockForWrite(std::unique_lock<QtPrivate::mutex> &lock, QDeadlineTimer timeout);
|
||||||
bool lockForRead(std::unique_lock<QtPrivate::mutex> &lock, int timeout);
|
bool lockForRead(std::unique_lock<QtPrivate::mutex> &lock, QDeadlineTimer timeout);
|
||||||
void unlock();
|
void unlock();
|
||||||
|
|
||||||
//memory management
|
//memory management
|
||||||
@ -75,8 +75,8 @@ public:
|
|||||||
QVarLengthArray<Reader, 16> currentReaders;
|
QVarLengthArray<Reader, 16> currentReaders;
|
||||||
|
|
||||||
// called with the mutex unlocked
|
// called with the mutex unlocked
|
||||||
bool recursiveLockForWrite(int timeout);
|
bool recursiveLockForWrite(QDeadlineTimer timeout);
|
||||||
bool recursiveLockForRead(int timeout);
|
bool recursiveLockForRead(QDeadlineTimer timeout);
|
||||||
void recursiveUnlock();
|
void recursiveUnlock();
|
||||||
|
|
||||||
static QReadWriteLockStates::StateForWaitCondition
|
static QReadWriteLockStates::StateForWaitCondition
|
||||||
|
Loading…
x
Reference in New Issue
Block a user