diff options
Diffstat (limited to 'src/corelib/thread/qmutex.h')
-rw-r--r-- | src/corelib/thread/qmutex.h | 355 |
1 files changed, 174 insertions, 181 deletions
diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h index 93c4bf23e8..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,238 +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() noexcept; //### Qt6: remove me - 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 *¤t) noexcept { - return d_ptr.testAndSetAcquire(nullptr, dummyLocked(), current); - } - inline bool fastTryUnlock(QMutexData *¤t) 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 +}; - bool isRecursive() const noexcept - { return QBasicMutex::isRecursive(); } +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; -private: - Q_DISABLE_COPY(QMutex) - friend class QMutexLocker; - friend class QRecursiveMutex; - friend class ::tst_QMutex; +public: + constexpr QRecursiveMutex() = default; + ~QRecursiveMutex(); + + + // 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(); } -#if __has_include(<chrono>) - template<class Rep, class Period> - static int convertToMilliseconds(std::chrono::duration<Rep, Period> duration) + // 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 Q_DECL_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 { @@ -309,7 +301,6 @@ public: Q_UNUSED(timePoint); return true; } -#endif private: Q_DISABLE_COPY(QMutex) @@ -317,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) @@ -333,7 +326,7 @@ private: typedef QMutex QBasicMutex; -#endif // !QT_CONFIG(thread) && !Q_CLANG_QDOC +#endif // !QT_CONFIG(thread) && !Q_QDOC QT_END_NAMESPACE |