Revision control
Copy as Markdown
// Copyright (C) 2016 The Qt Company Ltd.
// 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
// Qt-Security score:significant reason:default
#ifndef QREADWRITELOCK_P_H
#define QREADWRITELOCK_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of the implementation. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/private/qlocking_p.h>
#include <QtCore/private/qwaitcondition_p.h>
#include <QtCore/qreadwritelock.h>
#include <QtCore/qvarlengtharray.h>
QT_REQUIRE_CONFIG(thread);
QT_BEGIN_NAMESPACE
class QReadWriteLockPrivate
{
public:
// export the State constants protected in QReadWriteLock
static constexpr quintptr StateLockedForRead = QReadWriteLock::StateLockedForRead;
static constexpr quintptr StateLockedForWrite = QReadWriteLock::StateLockedForWrite;
static constexpr quintptr StateMask = QReadWriteLock::StateMask;
static constexpr quintptr MultiplyLocked = QReadWriteLock::Counter;
static constexpr quintptr IsRecursiveLock = MultiplyLocked << 1;
static constexpr quintptr RecursivelyLockedForWrite =
StateLockedForWrite | MultiplyLocked | IsRecursiveLock;
explicit QReadWriteLockPrivate(bool isRecursive = false)
: recursive(isRecursive) {}
alignas(QtPrivate::IdealMutexAlignment) std::condition_variable writerCond;
std::condition_variable readerCond;
alignas(QtPrivate::IdealMutexAlignment) std::mutex mutex;
int readerCount = 0;
int writerCount = 0;
int waitingReaders = 0;
int waitingWriters = 0;
const bool recursive;
//Called with the mutex locked
bool lockForWrite(std::unique_lock<std::mutex> &lock, QDeadlineTimer timeout);
bool lockForRead(std::unique_lock<std::mutex> &lock, QDeadlineTimer timeout);
void unlock();
//memory management
int id = 0;
void release();
static QReadWriteLockPrivate *allocate();
// Recursive mutex handling
Qt::HANDLE currentWriter = {};
struct Reader {
Qt::HANDLE handle;
int recursionLevel;
};
QVarLengthArray<Reader, 16> currentReaders;
// called with the mutex unlocked
bool recursiveLockForWrite(QDeadlineTimer timeout);
bool recursiveLockForRead(QDeadlineTimer timeout);
void recursiveUnlock();
/*!
* \internal
* Describes the state of the QReadWriteLock whose private is \a dd.
*
* Returns one of:
* \list
* \li 0 (unlocked)
* \li \c IsRecursiveLock (still unlocked)
* \li \c StateLockedForRead
* \li \c{StateLockedForRead | IsRecursiveLock}
* \li \c StateLockedForWrite
* \li \c{StateLockedForWrite | IsRecursiveLock}
* \li \c{StateLockedForWrite | IsRecursiveLock | MultiplyLocked} = \c RecursivelyLockedForWrite
* \endlist
*/
static quintptr describeState(void *dd) noexcept
{
quintptr u = quintptr(dd);
if (quintptr state = u & StateMask) {
// Not a pointer: a non-recursive, uncontended lock. The upper bits
// may hold a reader count, which we don't report.
return state;
}
if (!u)
return 0; // unlocked
auto d = static_cast<QReadWriteLockPrivate *>(dd);
const auto lock = qt_scoped_lock(d->mutex);
u = 0;
if (d->writerCount)
u |= StateLockedForWrite;
else if (d->readerCount)
u |= StateLockedForRead;
if (d->recursive) {
u |= IsRecursiveLock;
if (d->writerCount > 1) {
u |= MultiplyLocked;
Q_ASSERT(u == RecursivelyLockedForWrite);
}
} else {
// non-recursive, quick check of the state
Q_ASSERT(d->writerCount <= 1);
}
return u;
}
// used by QWaitCondition::wait
template <typename Prep, typename DoWait>
static bool waitConditionWait(QReadWriteLock *readWriteLock, Prep &&prep, DoWait &&doWait);
};
Q_DECLARE_TYPEINFO(QReadWriteLockPrivate::Reader, Q_PRIMITIVE_TYPE);
template <typename Prep, typename DoWait>
inline bool QReadWriteLockPrivate::waitConditionWait(QReadWriteLock *readWriteLock, Prep &&prep, DoWait &&doWait)
{
if (!readWriteLock)
return false;
auto previousState = describeState(readWriteLock->d_ptr.loadAcquire());
if (previousState == 0) // unlocked
return false;
if (previousState == RecursivelyLockedForWrite) {
qWarning("QWaitCondition: cannot wait on QReadWriteLocks with recursive lockForWrite()");
return false;
}
prep();
readWriteLock->unlock();
bool returnValue = doWait();
// relock
if (previousState & StateLockedForWrite)
readWriteLock->lockForWrite();
else
readWriteLock->lockForRead();
return returnValue;
}
QT_END_NAMESPACE
#endif // QREADWRITELOCK_P_H