summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread/qmutex.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/thread/qmutex.h')
-rw-r--r--src/corelib/thread/qmutex.h353
1 files changed, 174 insertions, 179 deletions
diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h
index c1c87100e6..743b86939e 100644
--- a/src/corelib/thread/qmutex.h
+++ b/src/corelib/thread/qmutex.h
@@ -1,62 +1,21 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMUTEX_H
#define QMUTEX_H
#include <QtCore/qglobal.h>
#include <QtCore/qatomic.h>
-#include <new>
+#include <QtCore/qdeadlinetimer.h>
+#include <QtCore/qtsan_impl.h>
-#if __has_include(<chrono>)
-# include <chrono>
-# include <limits>
-#endif
-
-class tst_QMutex;
+#include <chrono>
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(thread) || defined(Q_QDOC)
-#if QT_CONFIG(thread) || defined(Q_CLANG_QDOC)
-
-#ifdef Q_OS_LINUX
+#if defined(Q_OS_LINUX) || defined(Q_OS_WIN) // these platforms use futex
# define QT_MUTEX_LOCK_NOEXCEPT noexcept
#else
# define QT_MUTEX_LOCK_NOEXCEPT
@@ -64,236 +23,271 @@ QT_BEGIN_NAMESPACE
class QMutex;
class QRecursiveMutex;
-class QMutexData;
+class QMutexPrivate;
class Q_CORE_EXPORT QBasicMutex
{
+ Q_DISABLE_COPY_MOVE(QBasicMutex)
public:
-#ifdef Q_COMPILER_CONSTEXPR
constexpr QBasicMutex()
: d_ptr(nullptr)
{}
-#endif
// BasicLockable concept
inline void lock() QT_MUTEX_LOCK_NOEXCEPT {
+ QtTsan::mutexPreLock(this, 0u);
+
if (!fastTryLock())
lockInternal();
+
+ QtTsan::mutexPostLock(this, 0u, 0);
}
// BasicLockable concept
inline void unlock() noexcept {
Q_ASSERT(d_ptr.loadRelaxed()); //mutex must be locked
+
+ QtTsan::mutexPreUnlock(this, 0u);
+
if (!fastTryUnlock())
unlockInternal();
+
+ QtTsan::mutexPostUnlock(this, 0u);
}
bool tryLock() noexcept {
- return fastTryLock();
+ unsigned tsanFlags = QtTsan::TryLock;
+ QtTsan::mutexPreLock(this, tsanFlags);
+
+ const bool success = fastTryLock();
+
+ if (!success)
+ tsanFlags |= QtTsan::TryLockFailed;
+ QtTsan::mutexPostLock(this, tsanFlags, 0);
+
+ return success;
}
// Lockable concept
bool try_lock() noexcept { return tryLock(); }
- bool isRecursive() const noexcept;
private:
- inline bool fastTryLock() noexcept {
+ inline bool fastTryLock() noexcept
+ {
+ if (d_ptr.loadRelaxed() != nullptr)
+ return false;
return d_ptr.testAndSetAcquire(nullptr, dummyLocked());
}
inline bool fastTryUnlock() noexcept {
return d_ptr.testAndSetRelease(dummyLocked(), nullptr);
}
- inline bool fastTryLock(QMutexData *&current) noexcept {
- return d_ptr.testAndSetAcquire(nullptr, dummyLocked(), current);
- }
- inline bool fastTryUnlock(QMutexData *&current) noexcept {
- return d_ptr.testAndSetRelease(dummyLocked(), nullptr, current);
- }
void lockInternal() QT_MUTEX_LOCK_NOEXCEPT;
+ bool lockInternal(QDeadlineTimer timeout) QT_MUTEX_LOCK_NOEXCEPT;
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
bool lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT;
+#endif
void unlockInternal() noexcept;
+ void destroyInternal(QMutexPrivate *d);
- QBasicAtomicPointer<QMutexData> d_ptr;
- static inline QMutexData *dummyLocked() {
- return reinterpret_cast<QMutexData *>(quintptr(1));
+ QBasicAtomicPointer<QMutexPrivate> d_ptr;
+ static inline QMutexPrivate *dummyLocked() {
+ return reinterpret_cast<QMutexPrivate *>(quintptr(1));
}
friend class QMutex;
- friend class QRecursiveMutex;
- friend class QMutexData;
+ friend class QMutexPrivate;
};
class Q_CORE_EXPORT QMutex : public QBasicMutex
{
public:
-#if defined(Q_COMPILER_CONSTEXPR)
constexpr QMutex() = default;
-#else
- QMutex() { d_ptr.storeRelaxed(nullptr); }
-#endif
- enum RecursionMode { NonRecursive, Recursive };
- explicit QMutex(RecursionMode mode);
- ~QMutex();
+ ~QMutex()
+ {
+ QMutexPrivate *d = d_ptr.loadRelaxed();
+ if (d)
+ destroyInternal(d);
+ }
- // BasicLockable concept
- void lock() QT_MUTEX_LOCK_NOEXCEPT;
- bool tryLock(int timeout = 0) QT_MUTEX_LOCK_NOEXCEPT;
- // BasicLockable concept
- void unlock() noexcept;
+#ifdef Q_QDOC
+ inline void lock() QT_MUTEX_LOCK_NOEXCEPT;
+ inline void unlock() noexcept;
+ bool tryLock() noexcept;
+#endif
// Lockable concept
- bool try_lock() QT_MUTEX_LOCK_NOEXCEPT { return tryLock(); }
+ bool try_lock() noexcept { return tryLock(); }
+
+
+ using QBasicMutex::tryLock;
+ bool tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
+ {
+ return tryLock(QDeadlineTimer(timeout));
+ }
+
+ bool tryLock(QDeadlineTimer timeout) QT_MUTEX_LOCK_NOEXCEPT
+ {
+ unsigned tsanFlags = QtTsan::TryLock;
+ QtTsan::mutexPreLock(this, tsanFlags);
+
+ bool success = fastTryLock();
+
+ if (success) {
+ QtTsan::mutexPostLock(this, tsanFlags, 0);
+ return success;
+ }
+
+ success = lockInternal(timeout);
+
+ if (!success)
+ tsanFlags |= QtTsan::TryLockFailed;
+ QtTsan::mutexPostLock(this, tsanFlags, 0);
+
+ return success;
+ }
-#if __has_include(<chrono>) || defined(Q_CLANG_QDOC)
// TimedLockable concept
template <class Rep, class Period>
bool try_lock_for(std::chrono::duration<Rep, Period> duration)
{
- return tryLock(convertToMilliseconds(duration));
+ return tryLock(QDeadlineTimer(duration));
}
// TimedLockable concept
template<class Clock, class Duration>
bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
{
- // Implemented in terms of try_lock_for to honor the similar
- // requirement in N4606 § 30.4.1.3 [thread.timedmutex.requirements]/12.
-
- return try_lock_for(timePoint - Clock::now());
+ return tryLock(QDeadlineTimer(timePoint));
}
-#endif
+};
+
+class Q_CORE_EXPORT QRecursiveMutex
+{
+ Q_DISABLE_COPY_MOVE(QRecursiveMutex)
+ // written to by the thread that first owns 'mutex';
+ // read during attempts to acquire ownership of 'mutex' from any other thread:
+ QAtomicPointer<void> owner = nullptr;
+ // only ever accessed from the thread that owns 'mutex':
+ uint count = 0;
+ QMutex mutex;
- bool isRecursive() const noexcept
- { return QBasicMutex::isRecursive(); }
+public:
+ constexpr QRecursiveMutex() = default;
+ ~QRecursiveMutex();
-private:
- Q_DISABLE_COPY(QMutex)
- friend class QMutexLocker;
- friend class QRecursiveMutex;
- friend class ::tst_QMutex;
-#if __has_include(<chrono>)
- template<class Rep, class Period>
- static int convertToMilliseconds(std::chrono::duration<Rep, Period> duration)
+ // BasicLockable concept
+ void lock() QT_MUTEX_LOCK_NOEXCEPT
+ { tryLock(QDeadlineTimer(QDeadlineTimer::Forever)); }
+ QT_CORE_INLINE_SINCE(6, 6)
+ bool tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT;
+ bool tryLock(QDeadlineTimer timer = {}) QT_MUTEX_LOCK_NOEXCEPT;
+ // BasicLockable concept
+ void unlock() noexcept;
+
+ // Lockable concept
+ bool try_lock() QT_MUTEX_LOCK_NOEXCEPT { return tryLock(); }
+
+ // TimedLockable concept
+ template <class Rep, class Period>
+ bool try_lock_for(std::chrono::duration<Rep, Period> duration)
{
- // N4606 § 30.4.1.3.5 [thread.timedmutex.requirements] specifies that a
- // duration less than or equal to duration.zero() shall result in a
- // try_lock, unlike QMutex's tryLock with a negative duration which
- // results in a lock.
-
- if (duration <= duration.zero())
- return 0;
-
- // when converting from 'duration' to milliseconds, make sure that
- // the result is not shorter than 'duration':
- std::chrono::milliseconds wait = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
- if (wait < duration)
- wait += std::chrono::milliseconds(1);
- Q_ASSERT(wait >= duration);
- const auto ms = wait.count();
- const auto maxInt = (std::numeric_limits<int>::max)();
-
- return ms < maxInt ? int(ms) : maxInt;
+ return tryLock(QDeadlineTimer(duration));
+ }
+
+ // TimedLockable concept
+ template<class Clock, class Duration>
+ bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
+ {
+ return tryLock(QDeadlineTimer(timePoint));
}
-#endif
};
-class QRecursiveMutex : private QMutex
+#if QT_CORE_INLINE_IMPL_SINCE(6, 6)
+bool QRecursiveMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
{
- // ### Qt 6: make it independent of QMutex
- friend class QMutexLocker;
-public:
- Q_CORE_EXPORT QRecursiveMutex();
- Q_CORE_EXPORT ~QRecursiveMutex();
-
- using QMutex::lock;
- using QMutex::tryLock;
- using QMutex::unlock;
- using QMutex::try_lock;
-#if __has_include(<chrono>)
- using QMutex::try_lock_for;
- using QMutex::try_lock_until;
+ return tryLock(QDeadlineTimer(timeout));
+}
#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
+ Q_NODISCARD_CTOR
+ 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 = mutex;
+ if (Q_LIKELY(mutex)) {
+ mutex->lock();
+ m_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 void unlock() noexcept
+ Q_NODISCARD_CTOR
+ inline QMutexLocker(QMutexLocker &&other) noexcept
+ : m_mutex(std::exchange(other.m_mutex, nullptr)),
+ m_isLocked(std::exchange(other.m_isLocked, false))
+ {}
+
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QMutexLocker)
+
+ inline ~QMutexLocker()
{
- if ((val & quintptr(1u)) == quintptr(1u)) {
- val &= ~quintptr(1u);
- mutex()->unlock();
- }
+ if (m_isLocked)
+ unlock();
}
- inline void relock() QT_MUTEX_LOCK_NOEXCEPT
+ inline bool isLocked() const noexcept
{
- if (val) {
- if ((val & quintptr(1u)) == quintptr(0u)) {
- mutex()->lock();
- val |= quintptr(1u);
- }
- }
+ return m_isLocked;
}
-#if defined(Q_CC_MSVC)
-#pragma warning( push )
-#pragma warning( disable : 4312 ) // ignoring the warning from /Wp64
-#endif
+ inline void unlock() noexcept
+ {
+ Q_ASSERT(m_isLocked);
+ m_mutex->unlock();
+ m_isLocked = false;
+ }
- inline QMutex *mutex() const
+ inline void relock() QT_MUTEX_LOCK_NOEXCEPT
{
- return reinterpret_cast<QMutex *>(val & ~quintptr(1u));
+ Q_ASSERT(!m_isLocked);
+ m_mutex->lock();
+ m_isLocked = true;
}
-#if defined(Q_CC_MSVC)
-#pragma warning( pop )
-#endif
+ inline void swap(QMutexLocker &other) noexcept
+ {
+ qt_ptr_swap(m_mutex, other.m_mutex);
+ std::swap(m_isLocked, other.m_isLocked);
+ }
+ Mutex *mutex() const
+ {
+ return m_mutex;
+ }
private:
Q_DISABLE_COPY(QMutexLocker)
- quintptr val;
+ Mutex *m_mutex;
+ bool m_isLocked = false;
};
-#else // !QT_CONFIG(thread) && !Q_CLANG_QDOC
+#else // !QT_CONFIG(thread) && !Q_QDOC
-class Q_CORE_EXPORT QMutex
+class QMutex
{
public:
- enum RecursionMode { NonRecursive, Recursive };
- inline constexpr explicit QMutex(RecursionMode = NonRecursive) noexcept { }
+ constexpr QMutex() noexcept { }
inline void lock() noexcept {}
inline bool tryLock(int timeout = 0) noexcept { Q_UNUSED(timeout); return true; }
inline bool try_lock() noexcept { return true; }
inline void unlock() noexcept {}
- inline bool isRecursive() const noexcept { return true; }
-#if __has_include(<chrono>)
template <class Rep, class Period>
inline bool try_lock_for(std::chrono::duration<Rep, Period> duration) noexcept
{
@@ -307,7 +301,6 @@ public:
Q_UNUSED(timePoint);
return true;
}
-#endif
private:
Q_DISABLE_COPY(QMutex)
@@ -315,15 +308,17 @@ private:
class QRecursiveMutex : public QMutex {};
-class Q_CORE_EXPORT QMutexLocker
+template <typename Mutex>
+class QMutexLocker
{
public:
- inline explicit QMutexLocker(QMutex *) noexcept {}
+ Q_NODISCARD_CTOR
+ 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)
@@ -331,7 +326,7 @@ private:
typedef QMutex QBasicMutex;
-#endif // !QT_CONFIG(thread) && !Q_CLANG_QDOC
+#endif // !QT_CONFIG(thread) && !Q_QDOC
QT_END_NAMESPACE