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 Value prepareValue(const QString &value) const { return value; }
inline QString valueToString(const Value &value) const { return value; } inline QString valueToString(const Value &value) const { return value; }
#else #else
struct NameMapMutexLocker : public QMutexLocker struct NameMapMutexLocker : public QMutexLocker<QMutex>
{ {
NameMapMutexLocker(const QProcessEnvironmentPrivate *d) : QMutexLocker(&d->nameMapMutex) {} NameMapMutexLocker(const QProcessEnvironmentPrivate *d) : QMutexLocker(&d->nameMapMutex) {}
}; };

View File

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

View File

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

View File

@ -423,7 +423,7 @@ QRecursiveMutex::~QRecursiveMutex()
\ingroup thread \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 statements or in exception handling code is error-prone and
difficult to debug. QMutexLocker can be used in such situations difficult to debug. QMutexLocker can be used in such situations
to ensure that the state of the mutex is always well-defined. to ensure that the state of the mutex is always well-defined.

View File

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

View File

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

View File

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

View File

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

View File

@ -103,7 +103,7 @@ void tst_QReadWriteLock::uncontended_data()
QTest::addColumn<FunctionPtrHolder>("holder"); QTest::addColumn<FunctionPtrHolder>("holder");
QTest::newRow("nothing") << FunctionPtrHolder(testUncontended<int, FakeLock>); 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") QTest::newRow("QReadWriteLock, read")
<< FunctionPtrHolder(testUncontended<QReadWriteLock, QReadLocker>); << FunctionPtrHolder(testUncontended<QReadWriteLock, QReadLocker>);
QTest::newRow("QReadWriteLock, write") QTest::newRow("QReadWriteLock, write")
@ -173,7 +173,7 @@ void tst_QReadWriteLock::readOnly_data()
QTest::addColumn<FunctionPtrHolder>("holder"); QTest::addColumn<FunctionPtrHolder>("holder");
QTest::newRow("nothing") << FunctionPtrHolder(testReadOnly<int, FakeLock>); 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("QReadWriteLock") << FunctionPtrHolder(testReadOnly<QReadWriteLock, QReadLocker>);
QTest::newRow("std::mutex") << FunctionPtrHolder( QTest::newRow("std::mutex") << FunctionPtrHolder(
testReadOnly<std::mutex, LockerWrapper<std::unique_lock<std::mutex>>>); testReadOnly<std::mutex, LockerWrapper<std::unique_lock<std::mutex>>>);
@ -234,7 +234,7 @@ void tst_QReadWriteLock::writeOnly_data()
QTest::addColumn<FunctionPtrHolder>("holder"); QTest::addColumn<FunctionPtrHolder>("holder");
// QTest::newRow("nothing") << FunctionPtrHolder(testWriteOnly<int, FakeLock>); // 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("QReadWriteLock") << FunctionPtrHolder(testWriteOnly<QReadWriteLock, QWriteLocker>);
QTest::newRow("std::mutex") << FunctionPtrHolder( QTest::newRow("std::mutex") << FunctionPtrHolder(
testWriteOnly<std::mutex, LockerWrapper<std::unique_lock<std::mutex>>>); 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() void tst_QWaitCondition::oscillate_mutex()
{ {
QFETCH(unsigned long, timeout); QFETCH(unsigned long, timeout);
oscillate<QMutex, QMutexLocker>(timeout); oscillate<QMutex, QMutexLocker<QMutex>>(timeout);
} }
void tst_QWaitCondition::oscillate_writelock_data() void tst_QWaitCondition::oscillate_writelock_data()