Refactor QMutexLocker to be able to handle recursive mutexes

Since we're going to split QMutex and QRecursiveMutex into
separate classes, make sure QMutexLocker is prepared for that.

Change-Id: Id5e9a955d1db7c8ee663dd3811ad6448dad0aeae
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Lars Knoll 2020-09-04 10:41:56 +02:00
parent f76530a617
commit 77d812683f
10 changed files with 43 additions and 56 deletions

View File

@ -147,7 +147,7 @@ public:
inline Value prepareValue(const QString &value) const { return value; }
inline QString valueToString(const Value &value) const { return value; }
#else
struct NameMapMutexLocker : public QMutexLocker
struct NameMapMutexLocker : public QMutexLocker<QMutex>
{
NameMapMutexLocker(const QProcessEnvironmentPrivate *d) : QMutexLocker(&d->nameMapMutex) {}
};

View File

@ -179,10 +179,10 @@ int QEventLoop::exec(ProcessEventsFlags flags)
struct LoopReference {
QEventLoopPrivate *d;
QMutexLocker &locker;
QMutexLocker<QMutex> &locker;
bool exceptionCaught;
LoopReference(QEventLoopPrivate *d, QMutexLocker &locker) : d(d), locker(locker), exceptionCaught(true)
LoopReference(QEventLoopPrivate *d, QMutexLocker<QMutex> &locker) : d(d), locker(locker), exceptionCaught(true)
{
d->inExec = true;
d->exit.storeRelease(false);

View File

@ -601,7 +601,7 @@ int QtAndroidPrivate::acuqireServiceSetup(int flags)
void QtAndroidPrivate::setOnBindListener(QtAndroidPrivate::OnBindListener *listener)
{
QMutexLocker lock(g_onBindListenerMutex);
QMutexLocker lock(g_onBindListenerMutex());
*g_onBindListener = listener;
if (!g_serviceSetupLockers->deref())
g_waitForServiceSetupSemaphore->release();
@ -609,7 +609,7 @@ void QtAndroidPrivate::setOnBindListener(QtAndroidPrivate::OnBindListener *liste
jobject QtAndroidPrivate::callOnBindListener(jobject intent)
{
QMutexLocker lock(g_onBindListenerMutex);
QMutexLocker lock(g_onBindListenerMutex());
if (*g_onBindListener)
return (*g_onBindListener)->onBind(intent);
return nullptr;

View File

@ -423,7 +423,7 @@ QRecursiveMutex::~QRecursiveMutex()
\ingroup thread
Locking and unlocking a QMutex in complex functions and
Locking and unlocking a QMutex or QRecursiveMutex in complex functions and
statements or in exception handling code is error-prone and
difficult to debug. QMutexLocker can be used in such situations
to ensure that the state of the mutex is always well-defined.

View File

@ -174,6 +174,7 @@ public:
private:
Q_DISABLE_COPY(QMutex)
template<typename Mutex>
friend class QMutexLocker;
friend class QRecursiveMutex;
friend class ::tst_QMutex;
@ -207,6 +208,7 @@ private:
class QRecursiveMutex : private QMutex
{
// ### Qt 6: make it independent of QMutex
template<typename Mutex>
friend class QMutexLocker;
public:
Q_CORE_EXPORT QRecursiveMutex();
@ -222,65 +224,49 @@ public:
#endif
};
class Q_CORE_EXPORT QMutexLocker
template <typename Mutex>
class QMutexLocker
{
public:
#ifndef Q_CLANG_QDOC
inline explicit QMutexLocker(QBasicMutex *m) QT_MUTEX_LOCK_NOEXCEPT
inline explicit QMutexLocker(Mutex *mutex) QT_MUTEX_LOCK_NOEXCEPT
{
Q_ASSERT_X((reinterpret_cast<quintptr>(m) & quintptr(1u)) == quintptr(0),
"QMutexLocker", "QMutex pointer is misaligned");
val = quintptr(m);
if (Q_LIKELY(m)) {
// call QMutex::lock() instead of QBasicMutex::lock()
static_cast<QMutex *>(m)->lock();
val |= 1;
m = mutex;
if (Q_LIKELY(mutex)) {
mutex->lock();
isLocked = true;
}
}
explicit QMutexLocker(QRecursiveMutex *m) QT_MUTEX_LOCK_NOEXCEPT
: QMutexLocker{static_cast<QBasicMutex*>(m)} {}
#else
QMutexLocker(QMutex *) { }
QMutexLocker(QRecursiveMutex *) {}
#endif
inline ~QMutexLocker() { unlock(); }
inline ~QMutexLocker() {
unlock();
}
inline void unlock() noexcept
{
if ((val & quintptr(1u)) == quintptr(1u)) {
val &= ~quintptr(1u);
mutex()->unlock();
}
if (!isLocked)
return;
m->unlock();
isLocked = false;
}
inline void relock() QT_MUTEX_LOCK_NOEXCEPT
{
if (val) {
if ((val & quintptr(1u)) == quintptr(0u)) {
mutex()->lock();
val |= quintptr(1u);
}
if (isLocked)
return;
if (m) {
m->lock();
isLocked = true;
}
}
#if defined(Q_CC_MSVC)
#pragma warning( push )
#pragma warning( disable : 4312 ) // ignoring the warning from /Wp64
#endif
inline QMutex *mutex() const
Mutex *mutex() const
{
return reinterpret_cast<QMutex *>(val & ~quintptr(1u));
return m;
}
#if defined(Q_CC_MSVC)
#pragma warning( pop )
#endif
private:
Q_DISABLE_COPY(QMutexLocker)
quintptr val;
Mutex *m;
bool isLocked = false;
};
#else // !QT_CONFIG(thread) && !Q_CLANG_QDOC
@ -320,15 +306,16 @@ private:
class QRecursiveMutex : public QMutex {};
class Q_CORE_EXPORT QMutexLocker
template<typename Mutex>
class QMutexLocker
{
public:
inline explicit QMutexLocker(QMutex *) noexcept {}
inline explicit QMutexLocker(Mutex *) noexcept {}
inline ~QMutexLocker() noexcept {}
inline void unlock() noexcept {}
void relock() noexcept {}
inline QMutex *mutex() const noexcept { return nullptr; }
inline Mutex *mutex() const noexcept { return nullptr; }
private:
Q_DISABLE_COPY(QMutexLocker)

View File

@ -162,7 +162,7 @@ public:
static bool relock(QBasicMutex *, QBasicMutex *) { return false; }
};
using QBasicMutexLocker = QMutexLocker;
using QBasicMutexLocker = QMutexLocker<QBasicMutex>;
#endif

View File

@ -314,7 +314,7 @@ OSStatus QSslSocketBackendPrivate::WriteCallback(QSslSocketBackendPrivate *socke
void QSslSocketPrivate::ensureInitialized()
{
const QMutexLocker locker(qt_securetransport_mutex);
const QMutexLocker locker(qt_securetransport_mutex());
if (s_loadedCiphersAndCerts)
return;

View File

@ -2233,7 +2233,7 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
if (!q_resolveOpenSslSymbols())
return false;
const QMutexLocker locker(qt_opensslInitMutex);
const QMutexLocker locker(qt_opensslInitMutex());
if (!s_libraryLoaded) {
// Initialize OpenSSL.
@ -2265,7 +2265,7 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
{
const QMutexLocker locker(qt_opensslInitMutex);
const QMutexLocker locker(qt_opensslInitMutex());
if (s_loadedCiphersAndCerts)
return;

View File

@ -103,7 +103,7 @@ void tst_QReadWriteLock::uncontended_data()
QTest::addColumn<FunctionPtrHolder>("holder");
QTest::newRow("nothing") << FunctionPtrHolder(testUncontended<int, FakeLock>);
QTest::newRow("QMutex") << FunctionPtrHolder(testUncontended<QMutex, QMutexLocker>);
QTest::newRow("QMutex") << FunctionPtrHolder(testUncontended<QMutex, QMutexLocker<QMutex>>);
QTest::newRow("QReadWriteLock, read")
<< FunctionPtrHolder(testUncontended<QReadWriteLock, QReadLocker>);
QTest::newRow("QReadWriteLock, write")
@ -173,7 +173,7 @@ void tst_QReadWriteLock::readOnly_data()
QTest::addColumn<FunctionPtrHolder>("holder");
QTest::newRow("nothing") << FunctionPtrHolder(testReadOnly<int, FakeLock>);
QTest::newRow("QMutex") << FunctionPtrHolder(testReadOnly<QMutex, QMutexLocker>);
QTest::newRow("QMutex") << FunctionPtrHolder(testReadOnly<QMutex, QMutexLocker<QMutex>>);
QTest::newRow("QReadWriteLock") << FunctionPtrHolder(testReadOnly<QReadWriteLock, QReadLocker>);
QTest::newRow("std::mutex") << FunctionPtrHolder(
testReadOnly<std::mutex, LockerWrapper<std::unique_lock<std::mutex>>>);
@ -234,7 +234,7 @@ void tst_QReadWriteLock::writeOnly_data()
QTest::addColumn<FunctionPtrHolder>("holder");
// QTest::newRow("nothing") << FunctionPtrHolder(testWriteOnly<int, FakeLock>);
QTest::newRow("QMutex") << FunctionPtrHolder(testWriteOnly<QMutex, QMutexLocker>);
QTest::newRow("QMutex") << FunctionPtrHolder(testWriteOnly<QMutex, QMutexLocker<QMutex>>);
QTest::newRow("QReadWriteLock") << FunctionPtrHolder(testWriteOnly<QReadWriteLock, QWriteLocker>);
QTest::newRow("std::mutex") << FunctionPtrHolder(
testWriteOnly<std::mutex, LockerWrapper<std::unique_lock<std::mutex>>>);

View File

@ -110,7 +110,7 @@ void tst_QWaitCondition::oscillate_mutex_data()
void tst_QWaitCondition::oscillate_mutex()
{
QFETCH(unsigned long, timeout);
oscillate<QMutex, QMutexLocker>(timeout);
oscillate<QMutex, QMutexLocker<QMutex>>(timeout);
}
void tst_QWaitCondition::oscillate_writelock_data()