diff options
Diffstat (limited to 'src/corelib/thread/qmutex.h')
-rw-r--r-- | src/corelib/thread/qmutex.h | 225 |
1 files changed, 111 insertions, 114 deletions
diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h index 19463da619..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 @@ -66,34 +25,6 @@ class QMutex; class QRecursiveMutex; class QMutexPrivate; -#if __has_include(<chrono>) -namespace QtPrivate -{ - template<class Rep, class Period> - static int convertToMilliseconds(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; - } -} -#endif - class Q_CORE_EXPORT QBasicMutex { Q_DISABLE_COPY_MOVE(QBasicMutex) @@ -104,26 +35,47 @@ public: // 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(); } 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 { @@ -131,7 +83,10 @@ private: } 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); @@ -168,26 +123,42 @@ public: using QBasicMutex::tryLock; bool tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT { - if (fastTryLock()) - return true; - return lockInternal(timeout); + 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; } // TimedLockable concept template <class Rep, class Period> bool try_lock_for(std::chrono::duration<Rep, Period> duration) { - return tryLock(QtPrivate::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)); } }; @@ -208,8 +179,10 @@ public: // BasicLockable concept void lock() QT_MUTEX_LOCK_NOEXCEPT - { tryLock(-1); } - bool tryLock(int timeout = 0) 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; @@ -220,66 +193,89 @@ public: template <class Rep, class Period> bool try_lock_for(std::chrono::duration<Rep, Period> duration) { - return tryLock(QtPrivate::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)); } }; +#if QT_CORE_INLINE_IMPL_SINCE(6, 6) +bool QRecursiveMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT +{ + return tryLock(QDeadlineTimer(timeout)); +} +#endif + template <typename Mutex> -class [[nodiscard]] QMutexLocker +class QMutexLocker { public: + Q_NODISCARD_CTOR inline explicit QMutexLocker(Mutex *mutex) QT_MUTEX_LOCK_NOEXCEPT { - m = mutex; + m_mutex = mutex; if (Q_LIKELY(mutex)) { mutex->lock(); - isLocked = true; + m_isLocked = true; } } - inline ~QMutexLocker() { - unlock(); + + 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 (m_isLocked) + unlock(); + } + + inline bool isLocked() const noexcept + { + return m_isLocked; } inline void unlock() noexcept { - if (!isLocked) - return; - m->unlock(); - isLocked = false; + Q_ASSERT(m_isLocked); + m_mutex->unlock(); + m_isLocked = false; } inline void relock() QT_MUTEX_LOCK_NOEXCEPT { - if (isLocked) - return; - if (m) { - m->lock(); - isLocked = true; - } + Q_ASSERT(!m_isLocked); + m_mutex->lock(); + m_isLocked = true; + } + + 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; + return m_mutex; } private: Q_DISABLE_COPY(QMutexLocker) - Mutex *m; - bool isLocked = false; + Mutex *m_mutex; + bool m_isLocked = false; }; -#else // !QT_CONFIG(thread) && !Q_CLANG_QDOC +#else // !QT_CONFIG(thread) && !Q_QDOC class QMutex { @@ -313,9 +309,10 @@ private: class QRecursiveMutex : public QMutex {}; template <typename Mutex> -class [[nodiscard]] QMutexLocker +class QMutexLocker { public: + Q_NODISCARD_CTOR inline explicit QMutexLocker(Mutex *) noexcept {} inline ~QMutexLocker() noexcept {} @@ -329,7 +326,7 @@ private: typedef QMutex QBasicMutex; -#endif // !QT_CONFIG(thread) && !Q_CLANG_QDOC +#endif // !QT_CONFIG(thread) && !Q_QDOC QT_END_NAMESPACE |