2022-05-10 10:06:48 +00:00
|
|
|
// 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
|
2025-07-18 10:57:56 +00:00
|
|
|
// Qt-Security score:significant reason:default
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
#ifndef QMUTEX_H
|
|
|
|
#define QMUTEX_H
|
|
|
|
|
|
|
|
#include <QtCore/qglobal.h>
|
|
|
|
#include <QtCore/qatomic.h>
|
2023-04-28 04:29:07 +00:00
|
|
|
#include <QtCore/qdeadlinetimer.h>
|
2022-04-10 10:29:47 +00:00
|
|
|
#include <QtCore/qtsan_impl.h>
|
2016-07-14 14:37:55 +00:00
|
|
|
|
2023-07-18 09:15:36 +00:00
|
|
|
#include <chrono>
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2022-10-11 08:26:28 +00:00
|
|
|
#if QT_CONFIG(thread) || defined(Q_QDOC)
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2019-06-26 21:41:40 +00:00
|
|
|
class QMutex;
|
|
|
|
class QRecursiveMutex;
|
2020-09-04 13:35:16 +00:00
|
|
|
class QMutexPrivate;
|
|
|
|
|
2025-06-11 17:15:04 +00:00
|
|
|
class QT6_ONLY(Q_CORE_EXPORT) QBasicMutex
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2020-09-04 13:35:16 +00:00
|
|
|
Q_DISABLE_COPY_MOVE(QBasicMutex)
|
2024-12-31 20:30:25 +00:00
|
|
|
protected:
|
|
|
|
static constexpr bool FutexAlwaysAvailable =
|
|
|
|
#if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX) || defined(Q_OS_WIN) // these platforms use futex
|
|
|
|
true
|
|
|
|
#else
|
|
|
|
false
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
public:
|
2017-11-12 22:15:28 +00:00
|
|
|
constexpr QBasicMutex()
|
|
|
|
: d_ptr(nullptr)
|
|
|
|
{}
|
|
|
|
|
2016-07-14 14:37:55 +00:00
|
|
|
// BasicLockable concept
|
2024-12-31 20:30:25 +00:00
|
|
|
inline void lock() noexcept(FutexAlwaysAvailable) {
|
2022-04-10 10:29:47 +00:00
|
|
|
QtTsan::mutexPreLock(this, 0u);
|
|
|
|
|
2011-07-02 13:13:12 +00:00
|
|
|
if (!fastTryLock())
|
|
|
|
lockInternal();
|
2022-04-10 10:29:47 +00:00
|
|
|
|
|
|
|
QtTsan::mutexPostLock(this, 0u, 0);
|
2011-07-02 13:13:12 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2016-07-14 14:37:55 +00:00
|
|
|
// BasicLockable concept
|
2019-04-02 08:54:54 +00:00
|
|
|
inline void unlock() noexcept {
|
2019-06-10 09:08:29 +00:00
|
|
|
Q_ASSERT(d_ptr.loadRelaxed()); //mutex must be locked
|
2022-04-10 10:29:47 +00:00
|
|
|
|
|
|
|
QtTsan::mutexPreUnlock(this, 0u);
|
|
|
|
|
2024-12-31 21:09:17 +00:00
|
|
|
if constexpr (FutexAlwaysAvailable) {
|
|
|
|
// we always unlock if we have futexes
|
2025-02-14 00:33:28 +00:00
|
|
|
if (QMutexPrivate *d = d_ptr.fetchAndStoreRelease(nullptr); Q_UNLIKELY(d != dummyLocked()))
|
2024-12-31 21:09:17 +00:00
|
|
|
unlockInternalFutex(d); // was contended
|
|
|
|
} else {
|
|
|
|
// if we don't have futexes, we can only unlock if not contended
|
|
|
|
if (QMutexPrivate *d; !d_ptr.testAndSetRelease(dummyLocked(), nullptr, d))
|
|
|
|
unlockInternal(d); // was contended
|
|
|
|
}
|
2022-04-10 10:29:47 +00:00
|
|
|
|
|
|
|
QtTsan::mutexPostUnlock(this, 0u);
|
2011-07-02 13:13:12 +00:00
|
|
|
}
|
|
|
|
|
2019-04-02 08:54:54 +00:00
|
|
|
bool tryLock() noexcept {
|
2022-04-10 10:29:47 +00:00
|
|
|
unsigned tsanFlags = QtTsan::TryLock;
|
|
|
|
QtTsan::mutexPreLock(this, tsanFlags);
|
|
|
|
|
|
|
|
const bool success = fastTryLock();
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
tsanFlags |= QtTsan::TryLockFailed;
|
|
|
|
QtTsan::mutexPostLock(this, tsanFlags, 0);
|
|
|
|
|
|
|
|
return success;
|
2011-07-02 13:13:12 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2016-07-14 14:37:55 +00:00
|
|
|
// Lockable concept
|
2019-04-02 08:54:54 +00:00
|
|
|
bool try_lock() noexcept { return tryLock(); }
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
private:
|
QMutex & QReadWriteLock: do a memory read before CAS
The testAndSet operation is expensive if the lock is contended:
attempting to CAS that lock will cause the cacheline containing the lock
to be brought to the current CPU's most local cache in exclusive mode,
which in turn causes the CPU that has the lock to stall when it attempts
to release it. That's not desirable if we were just trying an untimed
tryLock*.
In the case of timed, contended tryLocks or unconditional locks, we
still need to perform an atomic operation to indicate we're about to
wait. For that case, this patch reduces the minimum number of atomic
operations from 2 to 1, which is a gain even in the case where no other
thread has changed the lock status at all. In case they have, either by
more threads attempting to lock or by the one that has the lock
unlocking it, this avoids the cacheline bouncing around between the
multiple CPUs between those two atomic operations. For QMutex, that
second atomic is a fetchAndStore, not testAndSet.
The above explanation is valid for architectures with Compare-And-Swap
instructions, such as x86 and ARMv8.1. For architectures using Load
Linked/Store Conditional instructions, the explanation doesn't apply but
the benefits still should because we avoid the expense of the LL.
See similar change to pthread_mutex_lock in
https://sourceware.org/git/?p=glibc.git;a=commit;h=d672a98a1af106bd68deb15576710cd61363f7a6
Change-Id: I3d728c4197df49169066fffd1756dcc26b2cf5f3
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
2023-04-17 23:30:38 +00:00
|
|
|
inline bool fastTryLock() noexcept
|
|
|
|
{
|
2025-02-14 00:33:28 +00:00
|
|
|
if (Q_UNLIKELY(d_ptr.loadRelaxed() != nullptr))
|
QMutex & QReadWriteLock: do a memory read before CAS
The testAndSet operation is expensive if the lock is contended:
attempting to CAS that lock will cause the cacheline containing the lock
to be brought to the current CPU's most local cache in exclusive mode,
which in turn causes the CPU that has the lock to stall when it attempts
to release it. That's not desirable if we were just trying an untimed
tryLock*.
In the case of timed, contended tryLocks or unconditional locks, we
still need to perform an atomic operation to indicate we're about to
wait. For that case, this patch reduces the minimum number of atomic
operations from 2 to 1, which is a gain even in the case where no other
thread has changed the lock status at all. In case they have, either by
more threads attempting to lock or by the one that has the lock
unlocking it, this avoids the cacheline bouncing around between the
multiple CPUs between those two atomic operations. For QMutex, that
second atomic is a fetchAndStore, not testAndSet.
The above explanation is valid for architectures with Compare-And-Swap
instructions, such as x86 and ARMv8.1. For architectures using Load
Linked/Store Conditional instructions, the explanation doesn't apply but
the benefits still should because we avoid the expense of the LL.
See similar change to pthread_mutex_lock in
https://sourceware.org/git/?p=glibc.git;a=commit;h=d672a98a1af106bd68deb15576710cd61363f7a6
Change-Id: I3d728c4197df49169066fffd1756dcc26b2cf5f3
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
2023-04-17 23:30:38 +00:00
|
|
|
return false;
|
2017-09-18 09:49:52 +00:00
|
|
|
return d_ptr.testAndSetAcquire(nullptr, dummyLocked());
|
2011-07-02 13:13:12 +00:00
|
|
|
}
|
2025-06-11 17:10:43 +00:00
|
|
|
#if QT_CORE_REMOVED_SINCE(6, 10)
|
|
|
|
inline bool fastTryUnlock() noexcept {
|
|
|
|
return d_ptr.testAndSetRelease(dummyLocked(), nullptr);
|
|
|
|
}
|
|
|
|
#endif
|
2012-08-11 14:45:14 +00:00
|
|
|
|
2025-06-11 17:15:04 +00:00
|
|
|
QT7_ONLY(Q_CORE_EXPORT)
|
2024-12-31 20:30:25 +00:00
|
|
|
void lockInternal() noexcept(FutexAlwaysAvailable);
|
2025-06-11 17:15:04 +00:00
|
|
|
QT7_ONLY(Q_CORE_EXPORT)
|
2024-12-31 20:30:25 +00:00
|
|
|
bool lockInternal(QDeadlineTimer timeout) noexcept(FutexAlwaysAvailable);
|
2023-04-28 04:29:07 +00:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
2024-12-31 20:30:25 +00:00
|
|
|
bool lockInternal(int timeout) noexcept(FutexAlwaysAvailable);
|
2019-04-02 08:54:54 +00:00
|
|
|
void unlockInternal() noexcept;
|
2024-12-31 21:09:17 +00:00
|
|
|
#endif
|
2025-06-11 17:15:04 +00:00
|
|
|
QT7_ONLY(Q_CORE_EXPORT)
|
2024-12-31 21:09:17 +00:00
|
|
|
void unlockInternalFutex(void *d) noexcept;
|
2025-06-11 17:15:04 +00:00
|
|
|
QT7_ONLY(Q_CORE_EXPORT)
|
2024-12-31 21:09:17 +00:00
|
|
|
void unlockInternal(void *d) noexcept;
|
2024-12-04 21:58:39 +00:00
|
|
|
#if QT_CORE_REMOVED_SINCE(6, 9)
|
2020-09-04 13:35:16 +00:00
|
|
|
void destroyInternal(QMutexPrivate *d);
|
2024-12-04 21:58:39 +00:00
|
|
|
#endif
|
2025-06-11 17:15:04 +00:00
|
|
|
QT7_ONLY(Q_CORE_EXPORT)
|
2024-12-04 21:58:39 +00:00
|
|
|
void destroyInternal(void *d);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2020-09-04 13:35:16 +00:00
|
|
|
QBasicAtomicPointer<QMutexPrivate> d_ptr;
|
|
|
|
static inline QMutexPrivate *dummyLocked() {
|
|
|
|
return reinterpret_cast<QMutexPrivate *>(quintptr(1));
|
2011-07-02 13:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
friend class QMutex;
|
2020-09-04 13:35:16 +00:00
|
|
|
friend class QMutexPrivate;
|
2011-07-02 13:13:12 +00:00
|
|
|
};
|
|
|
|
|
2025-06-11 17:15:04 +00:00
|
|
|
class QT6_ONLY(Q_CORE_EXPORT) QMutex : public QBasicMutex
|
2016-04-25 23:42:11 +00:00
|
|
|
{
|
2011-07-02 13:13:12 +00:00
|
|
|
public:
|
2019-06-26 21:41:40 +00:00
|
|
|
constexpr QMutex() = default;
|
2020-09-04 13:35:16 +00:00
|
|
|
~QMutex()
|
|
|
|
{
|
|
|
|
QMutexPrivate *d = d_ptr.loadRelaxed();
|
|
|
|
if (d)
|
|
|
|
destroyInternal(d);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef Q_QDOC
|
2024-12-31 20:30:25 +00:00
|
|
|
inline void lock() noexcept(FutexAlwaysAvailable);
|
2020-09-04 13:35:16 +00:00
|
|
|
inline void unlock() noexcept;
|
|
|
|
bool tryLock() noexcept;
|
2020-09-04 08:40:29 +00:00
|
|
|
#endif
|
|
|
|
|
2020-09-04 13:35:16 +00:00
|
|
|
// Lockable concept
|
|
|
|
bool try_lock() noexcept { return tryLock(); }
|
2012-05-23 08:26:07 +00:00
|
|
|
|
|
|
|
|
2020-09-04 13:35:16 +00:00
|
|
|
using QBasicMutex::tryLock;
|
2024-12-31 20:30:25 +00:00
|
|
|
bool tryLock(int timeout) noexcept(FutexAlwaysAvailable)
|
2023-04-28 04:29:07 +00:00
|
|
|
{
|
|
|
|
return tryLock(QDeadlineTimer(timeout));
|
|
|
|
}
|
|
|
|
|
2024-12-31 20:30:25 +00:00
|
|
|
bool tryLock(QDeadlineTimer timeout) noexcept(FutexAlwaysAvailable)
|
2020-09-04 13:35:16 +00:00
|
|
|
{
|
2022-04-10 10:29:47 +00:00
|
|
|
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;
|
2020-09-04 13:35:16 +00:00
|
|
|
}
|
2016-07-14 14:37:55 +00:00
|
|
|
|
|
|
|
// TimedLockable concept
|
|
|
|
template <class Rep, class Period>
|
|
|
|
bool try_lock_for(std::chrono::duration<Rep, Period> duration)
|
|
|
|
{
|
2023-04-28 04:29:07 +00:00
|
|
|
return tryLock(QDeadlineTimer(duration));
|
2016-07-14 14:37:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TimedLockable concept
|
|
|
|
template<class Clock, class Duration>
|
|
|
|
bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
|
|
|
|
{
|
2023-04-28 04:29:07 +00:00
|
|
|
return tryLock(QDeadlineTimer(timePoint));
|
2016-07-14 14:37:55 +00:00
|
|
|
}
|
2020-09-04 13:35:16 +00:00
|
|
|
};
|
2016-07-14 14:37:55 +00:00
|
|
|
|
2025-06-11 17:15:04 +00:00
|
|
|
class QT6_ONLY(Q_CORE_EXPORT) QRecursiveMutex
|
2020-09-04 13:35:16 +00:00
|
|
|
{
|
|
|
|
Q_DISABLE_COPY_MOVE(QRecursiveMutex)
|
2020-09-10 07:54:51 +00:00
|
|
|
// 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;
|
2024-12-31 20:30:25 +00:00
|
|
|
static constexpr bool LockIsNoexcept = noexcept(std::declval<QMutex>().lock());
|
QMutex: make sure we try_lock_for no shorter than the duration passed
By templating on the <chrono> types and unconditionally using
duration_cast to coerce the duration into a milliseconds, we
allowed code such as
mutex.try_lock_for(10us)
to compile, which is misleading, since it's actually a zero-
timeout try_lock().
Feedback from the std-discussions mailing list is that the
wait_for functions should wait for _at least_ the duration
given, because that is the natural direction of variance
(tasks becoming ready to run might not get a CPU immediately,
causing delays), while an interface that documents to wait
_no more_ than the given duration is promising something it
cannot fulfill.
Fix by converting the given duration to the smallest number
of milliseconds not less than the original duration. If that
is not representable in an int, use INT_MAX, emulating the
effect of a spurious wakeup, which are allowed to happen if
the function returns false in that case.
In the above example, the try_lock_for call is now equivalent
to
mutex.tryLock(1);
The tryLock() docs state that the actual waiting time does
not exceed the given milliseconds, but fixing that is a
separate issue.
Change-Id: Id4cbbea0ecc6fd2f94bb5aef28a1658be3728e52
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2016-10-17 11:00:04 +00:00
|
|
|
|
2020-09-10 07:54:51 +00:00
|
|
|
public:
|
|
|
|
constexpr QRecursiveMutex() = default;
|
2025-06-11 17:15:04 +00:00
|
|
|
QT7_ONLY(Q_CORE_EXPORT)
|
2020-09-04 13:35:16 +00:00
|
|
|
~QRecursiveMutex();
|
QMutex: make sure we try_lock_for no shorter than the duration passed
By templating on the <chrono> types and unconditionally using
duration_cast to coerce the duration into a milliseconds, we
allowed code such as
mutex.try_lock_for(10us)
to compile, which is misleading, since it's actually a zero-
timeout try_lock().
Feedback from the std-discussions mailing list is that the
wait_for functions should wait for _at least_ the duration
given, because that is the natural direction of variance
(tasks becoming ready to run might not get a CPU immediately,
causing delays), while an interface that documents to wait
_no more_ than the given duration is promising something it
cannot fulfill.
Fix by converting the given duration to the smallest number
of milliseconds not less than the original duration. If that
is not representable in an int, use INT_MAX, emulating the
effect of a spurious wakeup, which are allowed to happen if
the function returns false in that case.
In the above example, the try_lock_for call is now equivalent
to
mutex.tryLock(1);
The tryLock() docs state that the actual waiting time does
not exceed the given milliseconds, but fixing that is a
separate issue.
Change-Id: Id4cbbea0ecc6fd2f94bb5aef28a1658be3728e52
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2016-10-17 11:00:04 +00:00
|
|
|
|
|
|
|
|
2020-09-04 13:35:16 +00:00
|
|
|
// BasicLockable concept
|
2024-12-31 20:30:25 +00:00
|
|
|
void lock() noexcept(LockIsNoexcept)
|
2023-04-28 04:29:07 +00:00
|
|
|
{ tryLock(QDeadlineTimer(QDeadlineTimer::Forever)); }
|
|
|
|
QT_CORE_INLINE_SINCE(6, 6)
|
2024-12-31 20:30:25 +00:00
|
|
|
bool tryLock(int timeout) noexcept(LockIsNoexcept);
|
2025-06-11 17:15:04 +00:00
|
|
|
QT7_ONLY(Q_CORE_EXPORT)
|
2024-12-31 20:30:25 +00:00
|
|
|
bool tryLock(QDeadlineTimer timer = {}) noexcept(LockIsNoexcept);
|
2020-09-04 13:35:16 +00:00
|
|
|
// BasicLockable concept
|
2025-06-11 17:15:04 +00:00
|
|
|
QT7_ONLY(Q_CORE_EXPORT)
|
2020-09-04 13:35:16 +00:00
|
|
|
void unlock() noexcept;
|
QMutex: make sure we try_lock_for no shorter than the duration passed
By templating on the <chrono> types and unconditionally using
duration_cast to coerce the duration into a milliseconds, we
allowed code such as
mutex.try_lock_for(10us)
to compile, which is misleading, since it's actually a zero-
timeout try_lock().
Feedback from the std-discussions mailing list is that the
wait_for functions should wait for _at least_ the duration
given, because that is the natural direction of variance
(tasks becoming ready to run might not get a CPU immediately,
causing delays), while an interface that documents to wait
_no more_ than the given duration is promising something it
cannot fulfill.
Fix by converting the given duration to the smallest number
of milliseconds not less than the original duration. If that
is not representable in an int, use INT_MAX, emulating the
effect of a spurious wakeup, which are allowed to happen if
the function returns false in that case.
In the above example, the try_lock_for call is now equivalent
to
mutex.tryLock(1);
The tryLock() docs state that the actual waiting time does
not exceed the given milliseconds, but fixing that is a
separate issue.
Change-Id: Id4cbbea0ecc6fd2f94bb5aef28a1658be3728e52
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2016-10-17 11:00:04 +00:00
|
|
|
|
2020-09-04 13:35:16 +00:00
|
|
|
// Lockable concept
|
2024-12-31 20:30:25 +00:00
|
|
|
bool try_lock() noexcept(LockIsNoexcept) { return tryLock(); }
|
2020-09-04 13:35:16 +00:00
|
|
|
|
|
|
|
// TimedLockable concept
|
|
|
|
template <class Rep, class Period>
|
|
|
|
bool try_lock_for(std::chrono::duration<Rep, Period> duration)
|
|
|
|
{
|
2023-04-28 04:29:07 +00:00
|
|
|
return tryLock(QDeadlineTimer(duration));
|
QMutex: make sure we try_lock_for no shorter than the duration passed
By templating on the <chrono> types and unconditionally using
duration_cast to coerce the duration into a milliseconds, we
allowed code such as
mutex.try_lock_for(10us)
to compile, which is misleading, since it's actually a zero-
timeout try_lock().
Feedback from the std-discussions mailing list is that the
wait_for functions should wait for _at least_ the duration
given, because that is the natural direction of variance
(tasks becoming ready to run might not get a CPU immediately,
causing delays), while an interface that documents to wait
_no more_ than the given duration is promising something it
cannot fulfill.
Fix by converting the given duration to the smallest number
of milliseconds not less than the original duration. If that
is not representable in an int, use INT_MAX, emulating the
effect of a spurious wakeup, which are allowed to happen if
the function returns false in that case.
In the above example, the try_lock_for call is now equivalent
to
mutex.tryLock(1);
The tryLock() docs state that the actual waiting time does
not exceed the given milliseconds, but fixing that is a
separate issue.
Change-Id: Id4cbbea0ecc6fd2f94bb5aef28a1658be3728e52
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2016-10-17 11:00:04 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2020-09-04 13:35:16 +00:00
|
|
|
// TimedLockable concept
|
|
|
|
template<class Clock, class Duration>
|
|
|
|
bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
|
|
|
|
{
|
2023-04-28 04:29:07 +00:00
|
|
|
return tryLock(QDeadlineTimer(timePoint));
|
2020-09-04 13:35:16 +00:00
|
|
|
}
|
2019-06-26 21:41:40 +00:00
|
|
|
};
|
|
|
|
|
2023-04-28 04:29:07 +00:00
|
|
|
#if QT_CORE_INLINE_IMPL_SINCE(6, 6)
|
2024-12-31 20:30:25 +00:00
|
|
|
bool QRecursiveMutex::tryLock(int timeout) noexcept(LockIsNoexcept)
|
2023-04-28 04:29:07 +00:00
|
|
|
{
|
|
|
|
return tryLock(QDeadlineTimer(timeout));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-09-04 08:41:56 +00:00
|
|
|
template <typename Mutex>
|
2023-06-21 19:15:31 +00:00
|
|
|
class QMutexLocker
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2024-12-31 20:30:25 +00:00
|
|
|
#ifdef Q_CC_GHS
|
|
|
|
// internal compiler error otherwise
|
|
|
|
static constexpr bool LockIsNoexcept = false;
|
|
|
|
#else
|
|
|
|
static constexpr bool LockIsNoexcept = noexcept(std::declval<Mutex>().lock());
|
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
public:
|
Short live Q_NODISCARD_CTOR
[ChangeLog][QtCore] Introduced Q_NODISCARD_CTOR which resolves to
[[nodiscard]] attribute for constructors on compilers that support
it, and does nothing on other compilers.
Using [[nodiscard]] attribute on a constructor is a C++20 feature,
however in practice it is supported on most of the compilers that
we use in Qt 6. Clang generates a [-Wunused-value] warning, GCC
and MinGW generate a [-Wunused-result] warnings, and MSVC
generates a C4834 warning.
However, there are some exceptions.
The Integrity compiler provides the following warning:
"tst_qglobal.cpp", line 699: warning #3435-D:
the "nodiscard" attribute doesn't apply to constructors,
destructors, or routines with void return type
[[nodiscard]] explicit Test(int val) : m_val(val) {}
The QNX compiler (QCC 8.3.0) and GCC 9.3.1 on OpenSUSE generate
the [-Wattributes] warning:
tst_qglobal.cpp: In member function
'void tst_QGlobal::nodiscardConstructor()':
tst_qglobal.cpp:699:44: warning: 'nodiscard' attribute applied to
'tst_QGlobal::nodiscardConstructor()::Test::Test(int)' with void
return type [-Wattributes]
[[nodiscard]] explicit Test(int val) : m_val(val) {}
These warnings will lead to build failures when compiled with
-warnings-are-errors flag, so for these compilers the macro
does not do anything.
An attempt to use __attribute__((__warn_unused_result__)) was
also unsuccessful on these compilers, so this patch goes for
an easy solution, and simply checks
__has_cpp_attribute(nodiscard) >= 201907L
to decide if the attribute is supported or not.
This commit also introduces a syntax-only test, and also applies
the new macro to QMutexLocker, because not all platforms in the
CI build and run unit tests.
Fixes: QTBUG-104161
Change-Id: Ib4230661a5ad5e8af0d67b21b034486ebcd67562
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2023-04-06 11:00:25 +00:00
|
|
|
Q_NODISCARD_CTOR
|
2024-12-31 20:30:25 +00:00
|
|
|
inline explicit QMutexLocker(Mutex *mutex) noexcept(LockIsNoexcept)
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2022-03-06 13:29:54 +00:00
|
|
|
m_mutex = mutex;
|
2020-09-04 08:41:56 +00:00
|
|
|
if (Q_LIKELY(mutex)) {
|
|
|
|
mutex->lock();
|
2022-03-06 13:29:54 +00:00
|
|
|
m_isLocked = true;
|
2012-08-01 15:54:32 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
2022-03-06 13:29:54 +00:00
|
|
|
|
Short live Q_NODISCARD_CTOR
[ChangeLog][QtCore] Introduced Q_NODISCARD_CTOR which resolves to
[[nodiscard]] attribute for constructors on compilers that support
it, and does nothing on other compilers.
Using [[nodiscard]] attribute on a constructor is a C++20 feature,
however in practice it is supported on most of the compilers that
we use in Qt 6. Clang generates a [-Wunused-value] warning, GCC
and MinGW generate a [-Wunused-result] warnings, and MSVC
generates a C4834 warning.
However, there are some exceptions.
The Integrity compiler provides the following warning:
"tst_qglobal.cpp", line 699: warning #3435-D:
the "nodiscard" attribute doesn't apply to constructors,
destructors, or routines with void return type
[[nodiscard]] explicit Test(int val) : m_val(val) {}
The QNX compiler (QCC 8.3.0) and GCC 9.3.1 on OpenSUSE generate
the [-Wattributes] warning:
tst_qglobal.cpp: In member function
'void tst_QGlobal::nodiscardConstructor()':
tst_qglobal.cpp:699:44: warning: 'nodiscard' attribute applied to
'tst_QGlobal::nodiscardConstructor()::Test::Test(int)' with void
return type [-Wattributes]
[[nodiscard]] explicit Test(int val) : m_val(val) {}
These warnings will lead to build failures when compiled with
-warnings-are-errors flag, so for these compilers the macro
does not do anything.
An attempt to use __attribute__((__warn_unused_result__)) was
also unsuccessful on these compilers, so this patch goes for
an easy solution, and simply checks
__has_cpp_attribute(nodiscard) >= 201907L
to decide if the attribute is supported or not.
This commit also introduces a syntax-only test, and also applies
the new macro to QMutexLocker, because not all platforms in the
CI build and run unit tests.
Fixes: QTBUG-104161
Change-Id: Ib4230661a5ad5e8af0d67b21b034486ebcd67562
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2023-04-06 11:00:25 +00:00
|
|
|
Q_NODISCARD_CTOR
|
2022-03-06 12:34:15 +00:00
|
|
|
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)
|
|
|
|
|
2022-03-06 13:29:54 +00:00
|
|
|
inline ~QMutexLocker()
|
|
|
|
{
|
2022-03-06 14:12:51 +00:00
|
|
|
if (m_isLocked)
|
|
|
|
unlock();
|
2020-09-04 08:41:56 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2022-03-06 13:31:49 +00:00
|
|
|
inline bool isLocked() const noexcept
|
|
|
|
{
|
|
|
|
return m_isLocked;
|
|
|
|
}
|
|
|
|
|
2019-04-02 08:54:54 +00:00
|
|
|
inline void unlock() noexcept
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2022-03-06 14:12:51 +00:00
|
|
|
Q_ASSERT(m_isLocked);
|
2022-03-06 13:29:54 +00:00
|
|
|
m_mutex->unlock();
|
|
|
|
m_isLocked = false;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2024-12-31 20:30:25 +00:00
|
|
|
inline void relock() noexcept(LockIsNoexcept)
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2022-03-06 14:12:51 +00:00
|
|
|
Q_ASSERT(!m_isLocked);
|
|
|
|
m_mutex->lock();
|
|
|
|
m_isLocked = true;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2022-03-06 12:34:15 +00:00
|
|
|
inline void swap(QMutexLocker &other) noexcept
|
|
|
|
{
|
|
|
|
qt_ptr_swap(m_mutex, other.m_mutex);
|
|
|
|
std::swap(m_isLocked, other.m_isLocked);
|
|
|
|
}
|
|
|
|
|
2020-09-04 08:41:56 +00:00
|
|
|
Mutex *mutex() const
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2022-03-06 13:29:54 +00:00
|
|
|
return m_mutex;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
private:
|
|
|
|
Q_DISABLE_COPY(QMutexLocker)
|
|
|
|
|
2022-03-06 13:29:54 +00:00
|
|
|
Mutex *m_mutex;
|
|
|
|
bool m_isLocked = false;
|
2011-04-27 10:05:43 +00:00
|
|
|
};
|
|
|
|
|
2022-10-11 08:26:28 +00:00
|
|
|
#else // !QT_CONFIG(thread) && !Q_QDOC
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2020-09-04 13:35:16 +00:00
|
|
|
class QMutex
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
2021-04-14 14:24:44 +00:00
|
|
|
constexpr QMutex() noexcept { }
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2019-04-02 08:54:54 +00:00
|
|
|
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 {}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2016-07-14 14:37:55 +00:00
|
|
|
template <class Rep, class Period>
|
2019-04-02 08:54:54 +00:00
|
|
|
inline bool try_lock_for(std::chrono::duration<Rep, Period> duration) noexcept
|
2016-07-14 14:37:55 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(duration);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class Clock, class Duration>
|
2019-04-02 08:54:54 +00:00
|
|
|
inline bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint) noexcept
|
2016-07-14 14:37:55 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(timePoint);
|
|
|
|
return true;
|
|
|
|
}
|
2021-06-30 13:38:41 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
Q_DISABLE_COPY(QMutex)
|
2011-04-27 10:05:43 +00:00
|
|
|
};
|
|
|
|
|
2019-06-26 21:41:40 +00:00
|
|
|
class QRecursiveMutex : public QMutex {};
|
|
|
|
|
2020-09-04 13:35:16 +00:00
|
|
|
template <typename Mutex>
|
2023-06-21 19:15:31 +00:00
|
|
|
class QMutexLocker
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
|
|
|
public:
|
Short live Q_NODISCARD_CTOR
[ChangeLog][QtCore] Introduced Q_NODISCARD_CTOR which resolves to
[[nodiscard]] attribute for constructors on compilers that support
it, and does nothing on other compilers.
Using [[nodiscard]] attribute on a constructor is a C++20 feature,
however in practice it is supported on most of the compilers that
we use in Qt 6. Clang generates a [-Wunused-value] warning, GCC
and MinGW generate a [-Wunused-result] warnings, and MSVC
generates a C4834 warning.
However, there are some exceptions.
The Integrity compiler provides the following warning:
"tst_qglobal.cpp", line 699: warning #3435-D:
the "nodiscard" attribute doesn't apply to constructors,
destructors, or routines with void return type
[[nodiscard]] explicit Test(int val) : m_val(val) {}
The QNX compiler (QCC 8.3.0) and GCC 9.3.1 on OpenSUSE generate
the [-Wattributes] warning:
tst_qglobal.cpp: In member function
'void tst_QGlobal::nodiscardConstructor()':
tst_qglobal.cpp:699:44: warning: 'nodiscard' attribute applied to
'tst_QGlobal::nodiscardConstructor()::Test::Test(int)' with void
return type [-Wattributes]
[[nodiscard]] explicit Test(int val) : m_val(val) {}
These warnings will lead to build failures when compiled with
-warnings-are-errors flag, so for these compilers the macro
does not do anything.
An attempt to use __attribute__((__warn_unused_result__)) was
also unsuccessful on these compilers, so this patch goes for
an easy solution, and simply checks
__has_cpp_attribute(nodiscard) >= 201907L
to decide if the attribute is supported or not.
This commit also introduces a syntax-only test, and also applies
the new macro to QMutexLocker, because not all platforms in the
CI build and run unit tests.
Fixes: QTBUG-104161
Change-Id: Ib4230661a5ad5e8af0d67b21b034486ebcd67562
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2023-04-06 11:00:25 +00:00
|
|
|
Q_NODISCARD_CTOR
|
2020-09-04 08:41:56 +00:00
|
|
|
inline explicit QMutexLocker(Mutex *) noexcept {}
|
2019-04-02 08:54:54 +00:00
|
|
|
inline ~QMutexLocker() noexcept {}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2019-04-02 08:54:54 +00:00
|
|
|
inline void unlock() noexcept {}
|
|
|
|
void relock() noexcept {}
|
2020-09-04 08:41:56 +00:00
|
|
|
inline Mutex *mutex() const noexcept { return nullptr; }
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
Q_DISABLE_COPY(QMutexLocker)
|
|
|
|
};
|
|
|
|
|
2011-07-02 13:13:12 +00:00
|
|
|
typedef QMutex QBasicMutex;
|
|
|
|
|
2022-10-11 08:26:28 +00:00
|
|
|
#endif // !QT_CONFIG(thread) && !Q_QDOC
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|
|
|
|
#endif // QMUTEX_H
|