summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/thread')
-rw-r--r--src/corelib/thread/qatomic.cpp226
-rw-r--r--src/corelib/thread/qatomic.h115
-rw-r--r--src/corelib/thread/qatomic_bootstrap.h97
-rw-r--r--src/corelib/thread/qatomic_cxx11.h111
-rw-r--r--src/corelib/thread/qatomic_msvc.h485
-rw-r--r--src/corelib/thread/qbasicatomic.h122
-rw-r--r--src/corelib/thread/qexception.cpp150
-rw-r--r--src/corelib/thread/qexception.h78
-rw-r--r--src/corelib/thread/qfutex_freebsd_p.h82
-rw-r--r--src/corelib/thread/qfutex_linux_p.h95
-rw-r--r--src/corelib/thread/qfutex_mac_p.h140
-rw-r--r--src/corelib/thread/qfutex_p.h149
-rw-r--r--src/corelib/thread/qfutex_win_p.h58
-rw-r--r--src/corelib/thread/qfuture.h262
-rw-r--r--src/corelib/thread/qfuture.qdoc803
-rw-r--r--src/corelib/thread/qfuture_impl.h841
-rw-r--r--src/corelib/thread/qfutureinterface.cpp466
-rw-r--r--src/corelib/thread/qfutureinterface.h267
-rw-r--r--src/corelib/thread/qfutureinterface_p.h137
-rw-r--r--src/corelib/thread/qfuturesynchronizer.h61
-rw-r--r--src/corelib/thread/qfuturesynchronizer.qdoc34
-rw-r--r--src/corelib/thread/qfuturewatcher.cpp92
-rw-r--r--src/corelib/thread/qfuturewatcher.h48
-rw-r--r--src/corelib/thread/qfuturewatcher_p.h41
-rw-r--r--src/corelib/thread/qgenericatomic.h383
-rw-r--r--src/corelib/thread/qlocking_p.h52
-rw-r--r--src/corelib/thread/qmutex.cpp642
-rw-r--r--src/corelib/thread/qmutex.h355
-rw-r--r--src/corelib/thread/qmutex_linux.cpp182
-rw-r--r--src/corelib/thread/qmutex_mac.cpp51
-rw-r--r--src/corelib/thread/qmutex_p.h101
-rw-r--r--src/corelib/thread/qmutex_unix.cpp120
-rw-r--r--src/corelib/thread/qmutex_win.cpp66
-rw-r--r--src/corelib/thread/qorderedmutexlocker_p.h112
-rw-r--r--src/corelib/thread/qpromise.h119
-rw-r--r--src/corelib/thread/qpromise.qdoc152
-rw-r--r--src/corelib/thread/qreadwritelock.cpp308
-rw-r--r--src/corelib/thread/qreadwritelock.h110
-rw-r--r--src/corelib/thread/qreadwritelock_p.h114
-rw-r--r--src/corelib/thread/qresultstore.cpp119
-rw-r--r--src/corelib/thread/qresultstore.h121
-rw-r--r--src/corelib/thread/qrunnable.cpp88
-rw-r--r--src/corelib/thread/qrunnable.h160
-rw-r--r--src/corelib/thread/qsemaphore.cpp347
-rw-r--r--src/corelib/thread/qsemaphore.h85
-rw-r--r--src/corelib/thread/qthread.cpp380
-rw-r--r--src/corelib/thread/qthread.h115
-rw-r--r--src/corelib/thread/qthread_p.h213
-rw-r--r--src/corelib/thread/qthread_unix.cpp232
-rw-r--r--src/corelib/thread/qthread_win.cpp145
-rw-r--r--src/corelib/thread/qthreadpool.cpp392
-rw-r--r--src/corelib/thread/qthreadpool.h83
-rw-r--r--src/corelib/thread/qthreadpool_p.h86
-rw-r--r--src/corelib/thread/qthreadstorage.cpp52
-rw-r--r--src/corelib/thread/qthreadstorage.h46
-rw-r--r--src/corelib/thread/qtsan_impl.h79
-rw-r--r--src/corelib/thread/qwaitcondition.h42
-rw-r--r--src/corelib/thread/qwaitcondition.qdoc42
-rw-r--r--src/corelib/thread/qwaitcondition_p.h141
-rw-r--r--src/corelib/thread/qwaitcondition_unix.cpp199
-rw-r--r--src/corelib/thread/qwaitcondition_win.cpp113
-rw-r--r--src/corelib/thread/qyieldcpu.h64
-rw-r--r--src/corelib/thread/qyieldcpu.qdoc59
-rw-r--r--src/corelib/thread/thread.pri85
64 files changed, 5710 insertions, 5605 deletions
diff --git a/src/corelib/thread/qatomic.cpp b/src/corelib/thread/qatomic.cpp
index d302da72eb..a437eb3319 100644
--- a/src/corelib/thread/qatomic.cpp
+++ b/src/corelib/thread/qatomic.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** 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.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qatomic.h"
@@ -67,19 +31,19 @@
The template parameter \c T must be a C++ integer type:
\list
- \li 8-bit: char, signed char, unsigned char, qint8, quint8
- \li 16-bit: short, unsigned short, qint16, quint16, char16_t (C++11)
- \li 32-bit: int, unsigned int, qint32, quint32, char32_t (C++11)
+ \li 8-bit: bool, char, signed char, unsigned char, qint8, quint8, char8_t (C++20)
+ \li 16-bit: short, unsigned short, qint16, quint16, char16_t
+ \li 32-bit: int, unsigned int, qint32, quint32, char32_t
\li 64-bit: long long, unsigned long long, qint64, quint64
\li platform-specific size: long, unsigned long
\li pointer size: qintptr, quintptr, qptrdiff
\endlist
- Of the list above, only the 32-bit- and pointer-sized instantiations are guaranteed to
- work on all platforms. Support for other sizes depends on the compiler and
- processor architecture the code is being compiled for. To test whether the
- other types are supported, check the macro \c Q_ATOMIC_INT\e{nn}_IS_SUPPORTED,
- where \c{\e{nn}} is the number of bits desired.
+ Of the list above, only the 8-bit, 16-bit, 32-bit- and pointer-sized
+ instantiations are guaranteed to work on all platforms. Support for other
+ sizes depends on the compiler and processor architecture the code is being
+ compiled for. To test whether the 64-bit types are supported on 32-bit
+ platforms, check the macro \c Q_ATOMIC_INT64_IS_SUPPORTED.
\section1 The Atomic API
@@ -261,19 +225,6 @@
/*!
- \fn template <typename T> T QAtomicInteger<T>::load() const
- \obsolete
-
- Use loadRelaxed() instead.
-
- Atomically loads the value of this QAtomicInteger using relaxed memory
- ordering. The value is not modified in any way, but note that there's no
- guarantee that it remains so.
-
- \sa storeRelaxed(), loadAcquire()
-*/
-
-/*!
\fn template <typename T> T QAtomicInteger<T>::loadRelaxed() const
\since 5.14
@@ -295,18 +246,6 @@
*/
/*!
- \fn template <typename T> void QAtomicInteger<T>::store(T newValue)
- \obsolete
-
- Use storeRelaxed() instead.
-
- Atomically stores the \a newValue value into this atomic type, using
- relaxed memory ordering.
-
- \sa storeRelease(), loadRelaxed()
-*/
-
-/*!
\fn template <typename T> void QAtomicInteger<T>::storeRelaxed(T newValue)
\since 5.14
@@ -322,7 +261,7 @@
Atomically stores the \a newValue value into this atomic type, using
the "Release" memory ordering.
- \sa store(), loadAcquire()
+ \sa storeRelaxed(), loadAcquire()
*/
/*!
@@ -458,14 +397,21 @@
Atomic test-and-set.
+ \note If you use this function in a loop, consider using the overload with the
+ additional \c{T &currentValue} argument instead, which avoids the extra load() on
+ failure.
+
If the current value of this QAtomicInteger is the \a expectedValue,
the test-and-set functions assign the \a newValue to this
QAtomicInteger and return true. If the values are \e not the same,
this function does nothing and returns \c false.
+//![memory-order-relaxed]
This function uses \e relaxed \l {QAtomicInteger#Memory
ordering}{memory ordering} semantics, leaving the compiler and
processor to freely reorder memory accesses.
+//![memory-order-relaxed]
+
*/
/*!
@@ -473,15 +419,21 @@
Atomic test-and-set.
+ \note If you use this function in a loop, consider using the overload with the
+ additional \c{T &currentValue} argument instead, which avoids the extra load() on
+ failure.
+
If the current value of this QAtomicInteger is the \a expectedValue,
the test-and-set functions assign the \a newValue to this
QAtomicInteger and return true. If the values are \e not the same,
this function does nothing and returns \c false.
+//![memory-order-acquire]
This function uses \e acquire \l {QAtomicInteger#Memory
ordering}{memory ordering} semantics, which ensures that memory
access following the atomic operation (in program order) may not
be re-ordered before the atomic operation.
+//![memory-order-acquire]
*/
/*!
@@ -489,15 +441,21 @@
Atomic test-and-set.
+ \note If you use this function in a loop, consider using the overload with the
+ additional \c{T &currentValue} argument instead, which avoids the extra load() on
+ failure.
+
If the current value of this QAtomicInteger is the \a expectedValue,
the test-and-set functions assign the \a newValue to this
QAtomicInteger and return true. If the values are \e not the same,
this function does nothing and returns \c false.
+//![memory-order-release]
This function uses \e release \l {QAtomicInteger#Memory
ordering}{memory ordering} semantics, which ensures that memory
access before the atomic operation (in program order) may not be
re-ordered after the atomic operation.
+//![memory-order-release]
*/
/*!
@@ -505,15 +463,78 @@
Atomic test-and-set.
+ \note If you use this function in a loop, consider using the overload with the
+ additional \c{T &currentValue} argument instead, which avoids the extra load() on
+ failure.
+
If the current value of this QAtomicInteger is the \a expectedValue,
the test-and-set functions assign the \a newValue to this
QAtomicInteger and return true. If the values are \e not the same,
this function does nothing and returns \c false.
+//![memory-order-ordered]
This function uses \e ordered \l {QAtomicInteger#Memory
ordering}{memory ordering} semantics, which ensures that memory
access before and after the atomic operation (in program order)
may not be re-ordered.
+//![memory-order-ordered]
+
+*/
+
+/*!
+ \fn template <typename T> bool QAtomicInteger<T>::testAndSetRelaxed(T expectedValue, T newValue, T &currentValue)
+ \since 5.3
+
+ Atomic test-and-set.
+
+ If the current value of this QAtomicInteger is the \a expectedValue, the
+ test-and-set functions assign the \a newValue to this QAtomicInteger and
+ return \c true. If the values are \e not the same, the functions load the
+ current value of this QAtomicInteger into \a currentValue and return \c false.
+
+ \include qatomic.cpp memory-order-relaxed
+*/
+
+/*!
+ \fn template <typename T> bool QAtomicInteger<T>::testAndSetAcquire(T expectedValue, T newValue, T &currentValue)
+ \since 5.3
+
+ Atomic test-and-set.
+
+ If the current value of this QAtomicInteger is the \a expectedValue, the
+ test-and-set functions assign the \a newValue to this QAtomicInteger and
+ return \c true. If the values are \e not the same, the functions load the
+ current value of this QAtomicInteger into \a currentValue and return \c false.
+
+ \include qatomic.cpp memory-order-acquire
+*/
+
+/*!
+ \fn template <typename T> bool QAtomicInteger<T>::testAndSetRelease(T expectedValue, T newValue, T &currentValue)
+ \since 5.3
+
+ Atomic test-and-set.
+
+ If the current value of this QAtomicInteger is the \a expectedValue, the
+ test-and-set functions assign the \a newValue to this QAtomicInteger and
+ return \c true. If the values are \e not the same, the functions loads the
+ current value of this QAtomicInteger into \a currentValue and return \c false.
+
+ \include qatomic.cpp memory-order-release
+*/
+
+/*!
+ \fn template <typename T> bool QAtomicInteger<T>::testAndSetOrdered(T expectedValue, T newValue, T &currentValue)
+ \since 5.3
+
+ Atomic test-and-set.
+
+ If the current value of this QAtomicInteger is the \a expectedValue, the
+ test-and-set functions assign the \a newValue to this QAtomicInteger and
+ return \c true. If the values are \e not the same, it loads the current
+ value of this QAtomicInteger into \a currentValue and return \c false.
+
+ \include qatomic.cpp memory-order-ordered
*/
/*!
@@ -1011,9 +1032,16 @@
This macro is defined if atomic integers of size \e{nn} (in bits) are
supported in this compiler / architecture combination.
- Q_ATOMIC_INT32_IS_SUPPORTED is always defined.
\e{nn} is the size of the integer, in bits (8, 16, 32 or 64).
+
+ The following macros always defined:
+
+ \list
+ \li Q_ATOMIC_INT8_IS_SUPPORTED
+ \li Q_ATOMIC_INT16_IS_SUPPORTED
+ \li Q_ATOMIC_INT32_IS_SUPPORTED
+ \endlist
*/
/*!
@@ -1331,7 +1359,7 @@
\endlist
- \sa QAtomicInteger
+ \sa QAtomicInteger, qYieldCpu()
*/
/*!
@@ -1354,19 +1382,6 @@
*/
/*!
- \fn template <typename T> T *QAtomicPointer<T>::load() const
- \obsolete
-
- Use loadRelaxed() instead.
-
- Atomically loads the value of this QAtomicPointer using relaxed memory
- ordering. The value is not modified in any way, but note that there's no
- guarantee that it remains so.
-
- \sa storeRelaxed(), loadAcquire()
-*/
-
-/*!
\fn template <typename T> T *QAtomicPointer<T>::loadRelaxed() const
\since 5.14
@@ -1389,18 +1404,6 @@
*/
/*!
- \fn template <typename T> void QAtomicPointer<T>::store(T *newValue)
- \obsolete
-
- Use storeRelaxed() instead.
-
- Atomically stores the \a newValue value into this atomic type, using
- relaxed memory ordering.
-
- \sa storeRelease(), loadRelaxed()
-*/
-
-/*!
\fn template <typename T> void QAtomicPointer<T>::storeRelaxed(T *newValue)
\since 5.14
@@ -1739,6 +1742,12 @@
*/
// static checks
+#ifndef Q_ATOMIC_INT8_IS_SUPPORTED
+# error "Q_ATOMIC_INT8_IS_SUPPORTED must be defined"
+#endif
+#ifndef Q_ATOMIC_INT16_IS_SUPPORTED
+# error "Q_ATOMIC_INT16_IS_SUPPORTED must be defined"
+#endif
#ifndef Q_ATOMIC_INT32_IS_SUPPORTED
# error "Q_ATOMIC_INT32_IS_SUPPORTED must be defined"
#endif
@@ -1755,28 +1764,21 @@ static_assert(sizeof(QAtomicInteger<long>));
static_assert(sizeof(QAtomicInteger<unsigned long>));
static_assert(sizeof(QAtomicInteger<quintptr>));
static_assert(sizeof(QAtomicInteger<qptrdiff>));
-#ifdef Q_COMPILER_UNICODE_STRINGS
static_assert(sizeof(QAtomicInteger<char32_t>));
-#endif
-#ifdef Q_ATOMIC_INT16_IS_SUPPORTED
static_assert(sizeof(QAtomicInteger<short>));
static_assert(sizeof(QAtomicInteger<unsigned short>));
-# if WCHAR_MAX < 0x10000
static_assert(sizeof(QAtomicInteger<wchar_t>));
-# endif
-# ifdef Q_COMPILER_UNICODE_STRINGS
static_assert(sizeof(QAtomicInteger<char16_t>));
-# endif
-#endif
+
+static_assert(sizeof(QAtomicInteger<char>));
+static_assert(sizeof(QAtomicInteger<unsigned char>));
+static_assert(sizeof(QAtomicInteger<signed char>));
+static_assert(sizeof(QAtomicInteger<bool>));
#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
static_assert(sizeof(QAtomicInteger<qint64>));
static_assert(sizeof(QAtomicInteger<quint64>));
#endif
-#if WCHAR_MAX == INT_MAX
-static_assert(sizeof(QAtomicInteger<wchar_t>));
-#endif
-
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qatomic.h b/src/corelib/thread/qatomic.h
index aa57ddc610..7fe5ac69b9 100644
--- a/src/corelib/thread/qatomic.h
+++ b/src/corelib/thread/qatomic.h
@@ -1,44 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** 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$
-**
-****************************************************************************/
-
-#include <QtCore/qglobal.h>
+// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QATOMIC_H
#define QATOMIC_H
@@ -50,29 +12,16 @@ QT_BEGIN_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wextra")
-#ifdef Q_CLANG_QDOC
-# undef QT_BASIC_ATOMIC_HAS_CONSTRUCTORS
-#endif
-
// High-level atomic integer operations
template <typename T>
class QAtomicInteger : public QBasicAtomicInteger<T>
{
public:
// Non-atomic API
-#ifdef QT_BASIC_ATOMIC_HAS_CONSTRUCTORS
constexpr QAtomicInteger(T value = 0) noexcept : QBasicAtomicInteger<T>(value) {}
-#else
- inline QAtomicInteger(T value = 0) noexcept
- {
- this->_q_value = value;
- }
-#endif
inline QAtomicInteger(const QAtomicInteger &other) noexcept
-#ifdef QT_BASIC_ATOMIC_HAS_CONSTRUCTORS
: QBasicAtomicInteger<T>()
-#endif
{
this->storeRelease(other.loadAcquire());
}
@@ -83,41 +32,44 @@ public:
return *this;
}
-#ifdef Q_CLANG_QDOC
- T load() const;
+#ifdef Q_QDOC
T loadRelaxed() const;
T loadAcquire() const;
- void store(T newValue);
void storeRelaxed(T newValue);
void storeRelease(T newValue);
operator T() const;
QAtomicInteger &operator=(T);
- static Q_DECL_CONSTEXPR bool isReferenceCountingNative();
- static Q_DECL_CONSTEXPR bool isReferenceCountingWaitFree();
+ static constexpr bool isReferenceCountingNative();
+ static constexpr bool isReferenceCountingWaitFree();
bool ref();
bool deref();
- static Q_DECL_CONSTEXPR bool isTestAndSetNative();
- static Q_DECL_CONSTEXPR bool isTestAndSetWaitFree();
+ static constexpr bool isTestAndSetNative();
+ static constexpr bool isTestAndSetWaitFree();
bool testAndSetRelaxed(T expectedValue, T newValue);
bool testAndSetAcquire(T expectedValue, T newValue);
bool testAndSetRelease(T expectedValue, T newValue);
bool testAndSetOrdered(T expectedValue, T newValue);
- static Q_DECL_CONSTEXPR bool isFetchAndStoreNative();
- static Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree();
+ bool testAndSetRelaxed(T expectedValue, T newValue, T &currentValue);
+ bool testAndSetAcquire(T expectedValue, T newValue, T &currentValue);
+ bool testAndSetRelease(T expectedValue, T newValue, T &currentValue);
+ bool testAndSetOrdered(T expectedValue, T newValue, T &currentValue);
+
+ static constexpr bool isFetchAndStoreNative();
+ static constexpr bool isFetchAndStoreWaitFree();
T fetchAndStoreRelaxed(T newValue);
T fetchAndStoreAcquire(T newValue);
T fetchAndStoreRelease(T newValue);
T fetchAndStoreOrdered(T newValue);
- static Q_DECL_CONSTEXPR bool isFetchAndAddNative();
- static Q_DECL_CONSTEXPR bool isFetchAndAddWaitFree();
+ static constexpr bool isFetchAndAddNative();
+ static constexpr bool isFetchAndAddWaitFree();
T fetchAndAddRelaxed(T valueToAdd);
T fetchAndAddAcquire(T valueToAdd);
@@ -162,10 +114,7 @@ public:
// Non-atomic API
// We could use QT_COMPILER_INHERITING_CONSTRUCTORS, but we need only one;
// the implicit definition for all the others is fine.
-#ifdef QT_BASIC_ATOMIC_HAS_CONSTRUCTORS
- constexpr
-#endif
- QAtomicInt(int value = 0) noexcept : QAtomicInteger<int>(value) {}
+ constexpr QAtomicInt(int value = 0) noexcept : QAtomicInteger<int>(value) {}
};
// High-level atomic pointer operations
@@ -173,18 +122,10 @@ template <typename T>
class QAtomicPointer : public QBasicAtomicPointer<T>
{
public:
-#ifdef QT_BASIC_ATOMIC_HAS_CONSTRUCTORS
constexpr QAtomicPointer(T *value = nullptr) noexcept : QBasicAtomicPointer<T>(value) {}
-#else
- inline QAtomicPointer(T *value = nullptr) noexcept
- {
- this->storeRelaxed(value);
- }
-#endif
+
inline QAtomicPointer(const QAtomicPointer<T> &other) noexcept
-#ifdef QT_BASIC_ATOMIC_HAS_CONSTRUCTORS
: QBasicAtomicPointer<T>()
-#endif
{
this->storeRelease(other.loadAcquire());
}
@@ -196,31 +137,29 @@ public:
}
#ifdef Q_QDOC
- T *load() const;
T *loadAcquire() const;
T *loadRelaxed() const;
- void store(T *newValue);
void storeRelaxed(T *newValue);
void storeRelease(T *newValue);
- static Q_DECL_CONSTEXPR bool isTestAndSetNative();
- static Q_DECL_CONSTEXPR bool isTestAndSetWaitFree();
+ static constexpr bool isTestAndSetNative();
+ static constexpr bool isTestAndSetWaitFree();
bool testAndSetRelaxed(T *expectedValue, T *newValue);
bool testAndSetAcquire(T *expectedValue, T *newValue);
bool testAndSetRelease(T *expectedValue, T *newValue);
bool testAndSetOrdered(T *expectedValue, T *newValue);
- static Q_DECL_CONSTEXPR bool isFetchAndStoreNative();
- static Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree();
+ static constexpr bool isFetchAndStoreNative();
+ static constexpr bool isFetchAndStoreWaitFree();
T *fetchAndStoreRelaxed(T *newValue);
T *fetchAndStoreAcquire(T *newValue);
T *fetchAndStoreRelease(T *newValue);
T *fetchAndStoreOrdered(T *newValue);
- static Q_DECL_CONSTEXPR bool isFetchAndAddNative();
- static Q_DECL_CONSTEXPR bool isFetchAndAddWaitFree();
+ static constexpr bool isFetchAndAddNative();
+ static constexpr bool isFetchAndAddWaitFree();
T *fetchAndAddRelaxed(qptrdiff valueToAdd);
T *fetchAndAddAcquire(qptrdiff valueToAdd);
@@ -231,10 +170,6 @@ public:
QT_WARNING_POP
-#ifdef QT_BASIC_ATOMIC_HAS_CONSTRUCTORS
-# undef QT_BASIC_ATOMIC_HAS_CONSTRUCTORS
-#endif
-
/*!
This is a helper for the assignment operators of implicitly
shared classes. Your assignment operator should look like this:
diff --git a/src/corelib/thread/qatomic_bootstrap.h b/src/corelib/thread/qatomic_bootstrap.h
deleted file mode 100644
index c4279ee7b1..0000000000
--- a/src/corelib/thread/qatomic_bootstrap.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2011 Thiago Macieira <thiago@kde.org>
-** 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$
-**
-****************************************************************************/
-
-#ifndef QATOMIC_BOOTSTRAP_H
-#define QATOMIC_BOOTSTRAP_H
-
-#include <QtCore/qgenericatomic.h>
-
-QT_BEGIN_NAMESPACE
-
-#if 0
-// silence syncqt warnings
-QT_END_NAMESPACE
-#pragma qt_sync_skip_header_check
-#pragma qt_sync_stop_processing
-#endif
-
-template <typename T> struct QAtomicOps: QGenericAtomicOps<QAtomicOps<T> >
-{
- typedef T Type;
-
- static bool ref(T &_q_value) noexcept
- {
- return ++_q_value != 0;
- }
- static bool deref(T &_q_value) noexcept
- {
- return --_q_value != 0;
- }
-
- static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue = nullptr) noexcept
- {
- if (currentValue)
- *currentValue = _q_value;
- if (_q_value == expectedValue) {
- _q_value = newValue;
- return true;
- }
- return false;
- }
-
- static T fetchAndStoreRelaxed(T &_q_value, T newValue) noexcept
- {
- T tmp = _q_value;
- _q_value = newValue;
- return tmp;
- }
-
- template <typename AdditiveType> static
- T fetchAndAddRelaxed(T &_q_value, AdditiveType valueToAdd) noexcept
- {
- T returnValue = _q_value;
- _q_value += valueToAdd;
- return returnValue;
- }
-};
-
-QT_END_NAMESPACE
-
-#endif // QATOMIC_BOOTSTRAP_H
diff --git a/src/corelib/thread/qatomic_cxx11.h b/src/corelib/thread/qatomic_cxx11.h
index 9669554515..47a7bc9a10 100644
--- a/src/corelib/thread/qatomic_cxx11.h
+++ b/src/corelib/thread/qatomic_cxx11.h
@@ -1,47 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Thiago Macieira <thiago@kde.org>
-** Copyright (C) 2016 Intel Corporation.
-** 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) 2011 Thiago Macieira <thiago@kde.org>
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QATOMIC_CXX11_H
#define QATOMIC_CXX11_H
#include <QtCore/qgenericatomic.h>
+#include <QtCore/qyieldcpu.h>
#include <atomic>
QT_BEGIN_NAMESPACE
@@ -70,10 +35,8 @@ QT_END_NAMESPACE
* QAtomicInteger requires a constexpr answer (defect introduced in Qt 5.0). So
* we'll err in the side of caution and say it isn't.
*/
-
-// ### Qt 6: make non-constexpr (see above)
template <int N> struct QAtomicTraits
-{ static Q_DECL_CONSTEXPR inline bool isLockFree(); };
+{ static inline bool isLockFree(); };
#define Q_ATOMIC_INT32_IS_SUPPORTED
#if ATOMIC_INT_LOCK_FREE == 2
@@ -86,7 +49,7 @@ template <int N> struct QAtomicTraits
# define Q_ATOMIC_INT32_FETCH_AND_STORE_IS_ALWAYS_NATIVE
# define Q_ATOMIC_INT32_FETCH_AND_ADD_IS_ALWAYS_NATIVE
-template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<4>::isLockFree()
+template <> inline bool QAtomicTraits<4>::isLockFree()
{ return true; }
#elif ATOMIC_INT_LOCK_FREE == 1
# define Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE
@@ -98,7 +61,7 @@ template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<4>::isLockFree()
# define Q_ATOMIC_INT32_FETCH_AND_STORE_IS_SOMETIMES_NATIVE
# define Q_ATOMIC_INT32_FETCH_AND_ADD_IS_SOMETIMES_NATIVE
-template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<4>::isLockFree()
+template <> inline bool QAtomicTraits<4>::isLockFree()
{ return false; }
#else
# define Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NEVER_NATIVE
@@ -110,7 +73,7 @@ template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<4>::isLockFree()
# define Q_ATOMIC_INT32_FETCH_AND_STORE_IS_NEVER_NATIVE
# define Q_ATOMIC_INT32_FETCH_AND_ADD_IS_NEVER_NATIVE
-template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<4>::isLockFree()
+template <> inline bool QAtomicTraits<4>::isLockFree()
{ return false; }
#endif
@@ -139,7 +102,7 @@ template<> struct QAtomicOpsSupport<1> { enum { IsSupported = 1 }; };
# define Q_ATOMIC_INT8_FETCH_AND_STORE_IS_ALWAYS_NATIVE
# define Q_ATOMIC_INT8_FETCH_AND_ADD_IS_ALWAYS_NATIVE
-template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<1>::isLockFree()
+template <> inline bool QAtomicTraits<1>::isLockFree()
{ return true; }
#elif ATOMIC_CHAR_LOCK_FREE == 1
# define Q_ATOMIC_INT8_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE
@@ -147,7 +110,7 @@ template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<1>::isLockFree()
# define Q_ATOMIC_INT8_FETCH_AND_STORE_IS_SOMETIMES_NATIVE
# define Q_ATOMIC_INT8_FETCH_AND_ADD_IS_SOMETIMES_NATIVE
-template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<1>::isLockFree()
+template <> inline bool QAtomicTraits<1>::isLockFree()
{ return false; }
#else
# define Q_ATOMIC_INT8_REFERENCE_COUNTING_IS_NEVER_NATIVE
@@ -155,7 +118,7 @@ template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<1>::isLockFree()
# define Q_ATOMIC_INT8_FETCH_AND_STORE_IS_NEVER_NATIVE
# define Q_ATOMIC_INT8_FETCH_AND_ADD_IS_NEVER_NATIVE
-template <> Q_DECL_CONSTEXPR bool QAtomicTraits<1>::isLockFree()
+template <> bool QAtomicTraits<1>::isLockFree()
{ return false; }
#endif
@@ -167,7 +130,7 @@ template<> struct QAtomicOpsSupport<2> { enum { IsSupported = 1 }; };
# define Q_ATOMIC_INT16_FETCH_AND_STORE_IS_ALWAYS_NATIVE
# define Q_ATOMIC_INT16_FETCH_AND_ADD_IS_ALWAYS_NATIVE
-template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<2>::isLockFree()
+template <> inline bool QAtomicTraits<2>::isLockFree()
{ return false; }
#elif ATOMIC_SHORT_LOCK_FREE == 1
# define Q_ATOMIC_INT16_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE
@@ -175,7 +138,7 @@ template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<2>::isLockFree()
# define Q_ATOMIC_INT16_FETCH_AND_STORE_IS_SOMETIMES_NATIVE
# define Q_ATOMIC_INT16_FETCH_AND_ADD_IS_SOMETIMES_NATIVE
-template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<2>::isLockFree()
+template <> inline bool QAtomicTraits<2>::isLockFree()
{ return false; }
#else
# define Q_ATOMIC_INT16_REFERENCE_COUNTING_IS_NEVER_NATIVE
@@ -183,11 +146,11 @@ template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<2>::isLockFree()
# define Q_ATOMIC_INT16_FETCH_AND_STORE_IS_NEVER_NATIVE
# define Q_ATOMIC_INT16_FETCH_AND_ADD_IS_NEVER_NATIVE
-template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<2>::isLockFree()
+template <> inline bool QAtomicTraits<2>::isLockFree()
{ return false; }
#endif
-#if QT_CONFIG(std_atomic64)
+#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(std_atomic64)
template<> struct QAtomicOpsSupport<8> { enum { IsSupported = 1 }; };
# define Q_ATOMIC_INT64_IS_SUPPORTED
# if ATOMIC_LLONG_LOCK_FREE == 2
@@ -196,7 +159,7 @@ template<> struct QAtomicOpsSupport<8> { enum { IsSupported = 1 }; };
# define Q_ATOMIC_INT64_FETCH_AND_STORE_IS_ALWAYS_NATIVE
# define Q_ATOMIC_INT64_FETCH_AND_ADD_IS_ALWAYS_NATIVE
-template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<8>::isLockFree()
+template <> inline bool QAtomicTraits<8>::isLockFree()
{ return true; }
# elif ATOMIC_LLONG_LOCK_FREE == 1
# define Q_ATOMIC_INT64_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE
@@ -204,7 +167,7 @@ template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<8>::isLockFree()
# define Q_ATOMIC_INT64_FETCH_AND_STORE_IS_SOMETIMES_NATIVE
# define Q_ATOMIC_INT64_FETCH_AND_ADD_IS_SOMETIMES_NATIVE
-template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<8>::isLockFree()
+template <> inline bool QAtomicTraits<8>::isLockFree()
{ return false; }
# else
# define Q_ATOMIC_INT64_REFERENCE_COUNTING_IS_NEVER_NATIVE
@@ -212,7 +175,7 @@ template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<8>::isLockFree()
# define Q_ATOMIC_INT64_FETCH_AND_STORE_IS_NEVER_NATIVE
# define Q_ATOMIC_INT64_FETCH_AND_ADD_IS_NEVER_NATIVE
-template <> Q_DECL_CONSTEXPR inline bool QAtomicTraits<8>::isLockFree()
+template <> inline bool QAtomicTraits<8>::isLockFree()
{ return false; }
# endif
#endif
@@ -275,23 +238,37 @@ template <typename X> struct QAtomicOps
_q_value.store(newValue, std::memory_order_release);
}
- static inline Q_DECL_CONSTEXPR bool isReferenceCountingNative() noexcept { return isTestAndSetNative(); }
- static inline Q_DECL_CONSTEXPR bool isReferenceCountingWaitFree() noexcept { return false; }
+ static inline bool isReferenceCountingNative() noexcept { return isTestAndSetNative(); }
+ static inline constexpr bool isReferenceCountingWaitFree() noexcept { return false; }
template <typename T>
static inline bool ref(std::atomic<T> &_q_value)
{
- return ++_q_value != 0;
+ /* Conceptually, we want to
+ * return ++_q_value != 0;
+ * However, that would be sequentially consistent, and thus stronger
+ * than what we need. Based on
+ * http://eel.is/c++draft/atomics.types.memop#6, we know that
+ * pre-increment is equivalent to fetch_add(1) + 1. Unlike
+ * pre-increment, fetch_add takes a memory order argument, so we can get
+ * the desired acquire-release semantics.
+ * One last gotcha is that fetch_add(1) + 1 would need to be converted
+ * back to T, because it's susceptible to integer promotion. To sidestep
+ * this issue and to avoid UB on signed overflow, we rewrite the
+ * expression to:
+ */
+ return _q_value.fetch_add(1, std::memory_order_acq_rel) != T(-1);
}
template <typename T>
static inline bool deref(std::atomic<T> &_q_value) noexcept
{
- return --_q_value != 0;
+ // compare with ref
+ return _q_value.fetch_sub(1, std::memory_order_acq_rel) != T(1);
}
- static inline Q_DECL_CONSTEXPR bool isTestAndSetNative() noexcept
+ static inline bool isTestAndSetNative() noexcept
{ return QAtomicTraits<sizeof(X)>::isLockFree(); }
- static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() noexcept { return false; }
+ static inline constexpr bool isTestAndSetWaitFree() noexcept { return false; }
template <typename T>
static bool testAndSetRelaxed(std::atomic<T> &_q_value, T expectedValue, T newValue, T *currentValue = nullptr) noexcept
@@ -329,8 +306,8 @@ template <typename X> struct QAtomicOps
return tmp;
}
- static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() noexcept { return isTestAndSetNative(); }
- static inline Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() noexcept { return false; }
+ static inline bool isFetchAndStoreNative() noexcept { return isTestAndSetNative(); }
+ static inline constexpr bool isFetchAndStoreWaitFree() noexcept { return false; }
template <typename T>
static T fetchAndStoreRelaxed(std::atomic<T> &_q_value, T newValue) noexcept
@@ -356,8 +333,8 @@ template <typename X> struct QAtomicOps
return _q_value.exchange(newValue, std::memory_order_acq_rel);
}
- static inline Q_DECL_CONSTEXPR bool isFetchAndAddNative() noexcept { return isTestAndSetNative(); }
- static inline Q_DECL_CONSTEXPR bool isFetchAndAddWaitFree() noexcept { return false; }
+ static inline bool isFetchAndAddNative() noexcept { return isTestAndSetNative(); }
+ static inline constexpr bool isFetchAndAddWaitFree() noexcept { return false; }
template <typename T> static inline
T fetchAndAddRelaxed(std::atomic<T> &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd) noexcept
@@ -480,11 +457,7 @@ template <typename X> struct QAtomicOps
}
};
-#if defined(Q_COMPILER_CONSTEXPR)
# define Q_BASIC_ATOMIC_INITIALIZER(a) { a }
-#else
-# define Q_BASIC_ATOMIC_INITIALIZER(a) { ATOMIC_VAR_INIT(a) }
-#endif
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qatomic_msvc.h b/src/corelib/thread/qatomic_msvc.h
deleted file mode 100644
index 54b12f5bb2..0000000000
--- a/src/corelib/thread/qatomic_msvc.h
+++ /dev/null
@@ -1,485 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** 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$
-**
-****************************************************************************/
-
-#ifndef QATOMIC_MSVC_H
-#define QATOMIC_MSVC_H
-
-#include <QtCore/qgenericatomic.h>
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// use compiler intrinsics for all atomic functions
-# define QT_INTERLOCKED_PREFIX _
-# define QT_INTERLOCKED_PROTOTYPE
-# define QT_INTERLOCKED_DECLARE_PROTOTYPES
-# define QT_INTERLOCKED_INTRINSIC
-# define Q_ATOMIC_INT16_IS_SUPPORTED
-
-# ifdef _WIN64
-# define Q_ATOMIC_INT64_IS_SUPPORTED
-# endif
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Prototype declaration
-
-#define QT_INTERLOCKED_CONCAT_I(prefix, suffix) \
- prefix ## suffix
-#define QT_INTERLOCKED_CONCAT(prefix, suffix) \
- QT_INTERLOCKED_CONCAT_I(prefix, suffix)
-
-// MSVC intrinsics prefix function names with an underscore. Also, if platform
-// SDK headers have been included, the Interlocked names may be defined as
-// macros.
-// To avoid double underscores, we paste the prefix with Interlocked first and
-// then the remainder of the function name.
-#define QT_INTERLOCKED_FUNCTION(name) \
- QT_INTERLOCKED_CONCAT( \
- QT_INTERLOCKED_CONCAT(QT_INTERLOCKED_PREFIX, Interlocked), name)
-
-#ifndef QT_INTERLOCKED_VOLATILE
-# define QT_INTERLOCKED_VOLATILE volatile
-#endif
-
-#ifndef QT_INTERLOCKED_PREFIX
-#define QT_INTERLOCKED_PREFIX
-#endif
-
-#ifndef QT_INTERLOCKED_PROTOTYPE
-#define QT_INTERLOCKED_PROTOTYPE
-#endif
-
-#ifdef QT_INTERLOCKED_DECLARE_PROTOTYPES
-#undef QT_INTERLOCKED_DECLARE_PROTOTYPES
-
-extern "C" {
-
- long QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( Increment )(long QT_INTERLOCKED_VOLATILE *);
- long QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( Decrement )(long QT_INTERLOCKED_VOLATILE *);
- long QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( CompareExchange )(long QT_INTERLOCKED_VOLATILE *, long, long);
- long QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( Exchange )(long QT_INTERLOCKED_VOLATILE *, long);
- long QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( ExchangeAdd )(long QT_INTERLOCKED_VOLATILE *, long);
-
-# if !defined(__i386__) && !defined(_M_IX86)
- void * QT_INTERLOCKED_FUNCTION( CompareExchangePointer )(void * QT_INTERLOCKED_VOLATILE *, void *, void *);
- void * QT_INTERLOCKED_FUNCTION( ExchangePointer )(void * QT_INTERLOCKED_VOLATILE *, void *);
- __int64 QT_INTERLOCKED_FUNCTION( ExchangeAdd64 )(__int64 QT_INTERLOCKED_VOLATILE *, __int64);
-# endif
-
-# ifdef Q_ATOMIC_INT16_IS_SUPPORTED
- short QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( Increment16 )(short QT_INTERLOCKED_VOLATILE *);
- short QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( Decrement16 )(short QT_INTERLOCKED_VOLATILE *);
- short QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( CompareExchange16 )(short QT_INTERLOCKED_VOLATILE *, short, short);
- short QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( Exchange16 )(short QT_INTERLOCKED_VOLATILE *, short);
- short QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( ExchangeAdd16 )(short QT_INTERLOCKED_VOLATILE *, short);
-# endif
-# ifdef Q_ATOMIC_INT64_IS_SUPPORTED
- __int64 QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( Increment64 )(__int64 QT_INTERLOCKED_VOLATILE *);
- __int64 QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( Decrement64 )(__int64 QT_INTERLOCKED_VOLATILE *);
- __int64 QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( CompareExchange64 )(__int64 QT_INTERLOCKED_VOLATILE *, __int64, __int64);
- __int64 QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( Exchange64 )(__int64 QT_INTERLOCKED_VOLATILE *, __int64);
- //above already: qint64 QT_INTERLOCKED_PROTOTYPE QT_INTERLOCKED_FUNCTION( ExchangeAdd64 )(qint64 QT_INTERLOCKED_VOLATILE *, qint64);
-# endif
-}
-
-#endif // QT_INTERLOCKED_DECLARE_PROTOTYPES
-
-#undef QT_INTERLOCKED_PROTOTYPE
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-#ifdef QT_INTERLOCKED_INTRINSIC
-#undef QT_INTERLOCKED_INTRINSIC
-
-# pragma intrinsic (_InterlockedIncrement)
-# pragma intrinsic (_InterlockedDecrement)
-# pragma intrinsic (_InterlockedExchange)
-# pragma intrinsic (_InterlockedCompareExchange)
-# pragma intrinsic (_InterlockedExchangeAdd)
-
-# if !defined(_M_IX86)
-# pragma intrinsic (_InterlockedCompareExchangePointer)
-# pragma intrinsic (_InterlockedExchangePointer)
-# pragma intrinsic (_InterlockedExchangeAdd64)
-# endif
-
-#endif // QT_INTERLOCKED_INTRINSIC
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interlocked* replacement macros
-
-#if defined(__i386__) || defined(_M_IX86)
-
-# define QT_INTERLOCKED_COMPARE_EXCHANGE_POINTER(value, newValue, expectedValue) \
- reinterpret_cast<void *>( \
- QT_INTERLOCKED_FUNCTION(CompareExchange)( \
- reinterpret_cast<long QT_INTERLOCKED_VOLATILE *>(value), \
- long(newValue), \
- long(expectedValue)))
-
-# define QT_INTERLOCKED_EXCHANGE_POINTER(value, newValue) \
- QT_INTERLOCKED_FUNCTION(Exchange)( \
- reinterpret_cast<long QT_INTERLOCKED_VOLATILE *>(value), \
- long(newValue))
-
-# define QT_INTERLOCKED_EXCHANGE_ADD_POINTER(value, valueToAdd) \
- QT_INTERLOCKED_FUNCTION(ExchangeAdd)( \
- reinterpret_cast<long QT_INTERLOCKED_VOLATILE *>(value), \
- (valueToAdd))
-
-#else // !defined(__i386__) && !defined(_M_IX86)
-
-# define QT_INTERLOCKED_COMPARE_EXCHANGE_POINTER(value, newValue, expectedValue) \
- QT_INTERLOCKED_FUNCTION(CompareExchangePointer)( \
- (void * QT_INTERLOCKED_VOLATILE *)(value), \
- (void *) (newValue), \
- (void *) (expectedValue))
-
-# define QT_INTERLOCKED_EXCHANGE_POINTER(value, newValue) \
- QT_INTERLOCKED_FUNCTION(ExchangePointer)( \
- (void * QT_INTERLOCKED_VOLATILE *)(value), \
- (void *) (newValue))
-
-# define QT_INTERLOCKED_EXCHANGE_ADD_POINTER(value, valueToAdd) \
- QT_INTERLOCKED_FUNCTION(ExchangeAdd64)( \
- reinterpret_cast<qint64 QT_INTERLOCKED_VOLATILE *>(value), \
- (valueToAdd))
-
-#endif // !defined(__i386__) && !defined(_M_IX86)
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-QT_BEGIN_NAMESPACE
-
-#if 0
-// silence syncqt warnings
-QT_END_NAMESPACE
-#pragma qt_sync_skip_header_check
-#pragma qt_sync_stop_processing
-#endif
-
-#define Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
-#define Q_ATOMIC_INT_REFERENCE_COUNTING_IS_WAIT_FREE
-
-#define Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE
-#define Q_ATOMIC_INT_TEST_AND_SET_IS_WAIT_FREE
-
-#define Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE
-#define Q_ATOMIC_INT_FETCH_AND_STORE_IS_WAIT_FREE
-
-#define Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE
-#define Q_ATOMIC_INT_FETCH_AND_ADD_IS_WAIT_FREE
-
-#define Q_ATOMIC_INT32_IS_SUPPORTED
-
-#define Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
-#define Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_WAIT_FREE
-
-#define Q_ATOMIC_INT32_TEST_AND_SET_IS_ALWAYS_NATIVE
-#define Q_ATOMIC_INT32_TEST_AND_SET_IS_WAIT_FREE
-
-#define Q_ATOMIC_INT32_FETCH_AND_STORE_IS_ALWAYS_NATIVE
-#define Q_ATOMIC_INT32_FETCH_AND_STORE_IS_WAIT_FREE
-
-#define Q_ATOMIC_INT32_FETCH_AND_ADD_IS_ALWAYS_NATIVE
-#define Q_ATOMIC_INT32_FETCH_AND_ADD_IS_WAIT_FREE
-
-#define Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE
-#define Q_ATOMIC_POINTER_TEST_AND_SET_IS_WAIT_FREE
-
-#define Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE
-#define Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_WAIT_FREE
-
-#define Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_ALWAYS_NATIVE
-#define Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_WAIT_FREE
-
-#ifdef Q_ATOMIC_INT16_IS_SUPPORTED
-# define Q_ATOMIC_INT16_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
-# define Q_ATOMIC_INT16_REFERENCE_COUNTING_IS_WAIT_FREE
-
-# define Q_ATOMIC_INT16_TEST_AND_SET_IS_ALWAYS_NATIVE
-# define Q_ATOMIC_INT16_TEST_AND_SET_IS_WAIT_FREE
-
-# define Q_ATOMIC_INT16_FETCH_AND_STORE_IS_ALWAYS_NATIVE
-# define Q_ATOMIC_INT16_FETCH_AND_STORE_IS_WAIT_FREE
-
-# define Q_ATOMIC_INT16_FETCH_AND_ADD_IS_ALWAYS_NATIVE
-# define Q_ATOMIC_INT16_FETCH_AND_ADD_IS_WAIT_FREE
-
-template<> struct QAtomicOpsSupport<2> { enum { IsSupported = 1 }; };
-#endif
-
-#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
-# define Q_ATOMIC_INT64_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
-# define Q_ATOMIC_INT64_REFERENCE_COUNTING_IS_WAIT_FREE
-
-# define Q_ATOMIC_INT64_TEST_AND_SET_IS_ALWAYS_NATIVE
-# define Q_ATOMIC_INT64_TEST_AND_SET_IS_WAIT_FREE
-
-# define Q_ATOMIC_INT64_FETCH_AND_STORE_IS_ALWAYS_NATIVE
-# define Q_ATOMIC_INT64_FETCH_AND_STORE_IS_WAIT_FREE
-
-# define Q_ATOMIC_INT64_FETCH_AND_ADD_IS_ALWAYS_NATIVE
-# define Q_ATOMIC_INT64_FETCH_AND_ADD_IS_WAIT_FREE
-
-template<> struct QAtomicOpsSupport<8> { enum { IsSupported = 1 }; };
-#endif
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-template <int N> struct QAtomicWindowsType { typedef typename QIntegerForSize<N>::Signed Type; };
-template <> struct QAtomicWindowsType<4> { typedef long Type; };
-
-
-template <int N> struct QAtomicOpsBySize : QGenericAtomicOps<QAtomicOpsBySize<N> >
-{
- static inline Q_DECL_CONSTEXPR bool isReferenceCountingNative() noexcept { return true; }
- static inline Q_DECL_CONSTEXPR bool isReferenceCountingWaitFree() noexcept { return true; }
- template <typename T> static bool ref(T &_q_value) noexcept;
- template <typename T> static bool deref(T &_q_value) noexcept;
-
- static inline Q_DECL_CONSTEXPR bool isTestAndSetNative() noexcept { return true; }
- static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() noexcept { return true; }
- template <typename T> static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) noexcept;
- template <typename T>
- static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) noexcept;
-
- static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() noexcept { return true; }
- static inline Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() noexcept { return true; }
- template <typename T> static T fetchAndStoreRelaxed(T &_q_value, T newValue) noexcept;
-
- static inline Q_DECL_CONSTEXPR bool isFetchAndAddNative() noexcept { return true; }
- static inline Q_DECL_CONSTEXPR bool isFetchAndAddWaitFree() noexcept { return true; }
- template <typename T> static T fetchAndAddRelaxed(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd) noexcept;
-
-private:
- typedef typename QAtomicWindowsType<N>::Type Type;
- template <typename T> static inline Type *atomic(T *t)
- { static_assert(sizeof(T) == sizeof(Type)); return reinterpret_cast<Type *>(t); }
- template <typename T> static inline Type value(T t)
- { static_assert(sizeof(T) == sizeof(Type)); return Type(t); }
-};
-
-template <typename T>
-struct QAtomicOps : QAtomicOpsBySize<sizeof(T)>
-{
- typedef T Type;
-};
-
-template<> template<typename T>
-inline bool QAtomicOpsBySize<4>::ref(T &_q_value) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(Increment)(atomic(&_q_value)) != 0;
-}
-
-template<> template<typename T>
-inline bool QAtomicOpsBySize<4>::deref(T &_q_value) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(Decrement)(atomic(&_q_value)) != 0;
-}
-
-template<> template<typename T>
-inline bool QAtomicOpsBySize<4>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(CompareExchange)(atomic(&_q_value), value(newValue), value(expectedValue)) == value(expectedValue);
-}
-
-template<> template <typename T>
-inline bool QAtomicOpsBySize<4>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) noexcept
-{
- *currentValue = T(QT_INTERLOCKED_FUNCTION(CompareExchange)(atomic(&_q_value), newValue, expectedValue));
- return *currentValue == expectedValue;
-}
-
-template<> template<typename T>
-inline T QAtomicOpsBySize<4>::fetchAndStoreRelaxed(T &_q_value, T newValue) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(Exchange)(atomic(&_q_value), value(newValue));
-}
-
-template<> template<typename T>
-inline T QAtomicOpsBySize<4>::fetchAndAddRelaxed(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(ExchangeAdd)(atomic(&_q_value), value<T>(valueToAdd * QAtomicAdditiveType<T>::AddScale));
-}
-
-#ifdef Q_ATOMIC_INT16_IS_SUPPORTED
-template<> template<typename T>
-inline bool QAtomicOpsBySize<2>::ref(T &_q_value) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(Increment16)(atomic(&_q_value)) != 0;
-}
-
-template<> template<typename T>
-inline bool QAtomicOpsBySize<2>::deref(T &_q_value) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(Decrement16)(atomic(&_q_value)) != 0;
-}
-
-template<> template<typename T>
-inline bool QAtomicOpsBySize<2>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(CompareExchange16)(atomic(&_q_value), value(newValue), value(expectedValue)) == value(expectedValue);
-}
-
-template<> template <typename T>
-inline bool QAtomicOpsBySize<2>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) noexcept
-{
- *currentValue = T(QT_INTERLOCKED_FUNCTION(CompareExchange16)(atomic(&_q_value), newValue, expectedValue));
- return *currentValue == expectedValue;
-}
-
-template<> template<typename T>
-inline T QAtomicOpsBySize<2>::fetchAndStoreRelaxed(T &_q_value, T newValue) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(Exchange16)(atomic(&_q_value), value(newValue));
-}
-
-template<> template<typename T>
-inline T QAtomicOpsBySize<2>::fetchAndAddRelaxed(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(ExchangeAdd16)(atomic(&_q_value), value<T>(valueToAdd * QAtomicAdditiveType<T>::AddScale));
-}
-#endif
-
-#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
-template<> template<typename T>
-inline bool QAtomicOpsBySize<8>::ref(T &_q_value) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(Increment64)(atomic(&_q_value)) != 0;
-}
-
-template<> template<typename T>
-inline bool QAtomicOpsBySize<8>::deref(T &_q_value) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(Decrement64)(atomic(&_q_value)) != 0;
-}
-
-template<> template<typename T>
-inline bool QAtomicOpsBySize<8>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(CompareExchange64)(atomic(&_q_value), value(newValue), value(expectedValue)) == value(expectedValue);
-}
-
-template<> template <typename T>
-inline bool QAtomicOpsBySize<8>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) noexcept
-{
- *currentValue = T(QT_INTERLOCKED_FUNCTION(CompareExchange64)(atomic(&_q_value), newValue, expectedValue));
- return *currentValue == expectedValue;
-}
-
-template<> template<typename T>
-inline T QAtomicOpsBySize<8>::fetchAndStoreRelaxed(T &_q_value, T newValue) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(Exchange64)(atomic(&_q_value), value(newValue));
-}
-
-template<> template<typename T>
-inline T QAtomicOpsBySize<8>::fetchAndAddRelaxed(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd) noexcept
-{
- return QT_INTERLOCKED_FUNCTION(ExchangeAdd64)(atomic(&_q_value), value<T>(valueToAdd * QAtomicAdditiveType<T>::AddScale));
-}
-#endif
-
-// Specialization for pointer types, since we have Interlocked*Pointer() variants in some configurations
-template <typename T>
-struct QAtomicOps<T *> : QGenericAtomicOps<QAtomicOps<T *> >
-{
- typedef T *Type;
-
- static inline Q_DECL_CONSTEXPR bool isTestAndSetNative() noexcept { return true; }
- static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() noexcept { return true; }
- static bool testAndSetRelaxed(T *&_q_value, T *expectedValue, T *newValue) noexcept;
- static bool testAndSetRelaxed(T *&_q_value, T *expectedValue, T *newValue, T **currentValue) noexcept;
-
- static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() noexcept { return true; }
- static inline Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() noexcept { return true; }
- static T *fetchAndStoreRelaxed(T *&_q_value, T *newValue) noexcept;
-
- static inline Q_DECL_CONSTEXPR bool isFetchAndAddNative() noexcept { return true; }
- static inline Q_DECL_CONSTEXPR bool isFetchAndAddWaitFree() noexcept { return true; }
- static T *fetchAndAddRelaxed(T *&_q_value, qptrdiff valueToAdd) noexcept;
-};
-
-template <typename T>
-inline bool QAtomicOps<T *>::testAndSetRelaxed(T *&_q_value, T *expectedValue, T *newValue) noexcept
-{
- return QT_INTERLOCKED_COMPARE_EXCHANGE_POINTER(&_q_value, newValue, expectedValue) == expectedValue;
-}
-
-template <typename T>
-inline bool QAtomicOps<T *>::testAndSetRelaxed(T *&_q_value, T *expectedValue, T *newValue, T **currentValue) noexcept
-{
- *currentValue = reinterpret_cast<T *>(QT_INTERLOCKED_COMPARE_EXCHANGE_POINTER(&_q_value, newValue, expectedValue));
- return *currentValue == expectedValue;
-}
-
-template <typename T>
-inline T *QAtomicOps<T *>::fetchAndStoreRelaxed(T *&_q_value, T *newValue) noexcept
-{
- return reinterpret_cast<T *>(QT_INTERLOCKED_EXCHANGE_POINTER(&_q_value, newValue));
-}
-
-template <typename T>
-inline T *QAtomicOps<T *>::fetchAndAddRelaxed(T *&_q_value, qptrdiff valueToAdd) noexcept
-{
- return reinterpret_cast<T *>(QT_INTERLOCKED_EXCHANGE_ADD_POINTER(&_q_value, valueToAdd * sizeof(T)));
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Cleanup
-
-#undef QT_INTERLOCKED_CONCAT_I
-#undef QT_INTERLOCKED_CONCAT
-#undef QT_INTERLOCKED_FUNCTION
-#undef QT_INTERLOCKED_PREFIX
-
-#undef QT_INTERLOCKED_VOLATILE
-
-#undef QT_INTERLOCKED_INCREMENT
-#undef QT_INTERLOCKED_DECREMENT
-#undef QT_INTERLOCKED_COMPARE_EXCHANGE
-#undef QT_INTERLOCKED_EXCHANGE
-#undef QT_INTERLOCKED_EXCHANGE_ADD
-#undef QT_INTERLOCKED_COMPARE_EXCHANGE_POINTER
-#undef QT_INTERLOCKED_EXCHANGE_POINTER
-#undef QT_INTERLOCKED_EXCHANGE_ADD_POINTER
-
-QT_END_NAMESPACE
-#endif // QATOMIC_MSVC_H
diff --git a/src/corelib/thread/qbasicatomic.h b/src/corelib/thread/qbasicatomic.h
index 18da268270..6d061ea49a 100644
--- a/src/corelib/thread/qbasicatomic.h
+++ b/src/corelib/thread/qbasicatomic.h
@@ -1,65 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Thiago Macieira <thiago@kde.org>
-** Copyright (C) 2018 Intel Corporation.
-** 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$
-**
-****************************************************************************/
-
-#include <QtCore/qglobal.h>
+// Copyright (C) 2011 Thiago Macieira <thiago@kde.org>
+// Copyright (C) 2018 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QBASICATOMIC_H
#define QBASICATOMIC_H
-#if defined(QT_BOOTSTRAPPED)
-# include <QtCore/qatomic_bootstrap.h>
-
-// If C++11 atomics are supported, use them!
-// Note that constexpr support is sometimes disabled in QNX or INTEGRITY builds,
-// but their libraries have <atomic>.
-#elif defined(Q_COMPILER_ATOMICS) && (defined(Q_COMPILER_CONSTEXPR) || defined(Q_OS_QNX) || defined(Q_OS_INTEGRITY))
-# include <QtCore/qatomic_cxx11.h>
-
-// We only support one fallback: MSVC, because even on version 2015, it lacks full constexpr support
-#elif defined(Q_CC_MSVC)
-# include <QtCore/qatomic_msvc.h>
-
-// No fallback
-#else
-# error "Qt requires C++11 support"
-#endif
+#include <QtCore/qatomic_cxx11.h>
QT_WARNING_PUSH
QT_WARNING_DISABLE_MSVC(4522)
@@ -73,19 +19,6 @@ QT_END_NAMESPACE
#pragma qt_sync_stop_processing
#endif
-// New atomics
-
-#if defined(Q_COMPILER_CONSTEXPR)
-# if defined(Q_CC_CLANG) && Q_CC_CLANG < 303
- /*
- Do not define QT_BASIC_ATOMIC_HAS_CONSTRUCTORS for Clang before version 3.3.
- For details about the bug: see http://llvm.org/bugs/show_bug.cgi?id=12670
- */
-# else
-# define QT_BASIC_ATOMIC_HAS_CONSTRUCTORS
-# endif
-#endif
-
template <typename T>
class QBasicAtomicInteger
{
@@ -93,17 +26,13 @@ public:
typedef T Type;
typedef QAtomicOps<T> Ops;
// static check that this is a valid integer
- static_assert(QTypeInfo<T>::isIntegral, "template parameter is not an integral type");
+ static_assert(std::is_integral_v<T>, "template parameter is not an integral type");
static_assert(QAtomicOpsSupport<sizeof(T)>::IsSupported, "template parameter is an integral of a size not supported on this platform");
typename Ops::Type _q_value;
- // Everything below is either implemented in ../arch/qatomic_XXX.h or (as fallback) in qgenericatomic.h
-#if QT_DEPRECATED_SINCE(5, 14)
- QT_DEPRECATED_VERSION_X_5_14("Use loadRelaxed") T load() const noexcept { return loadRelaxed(); }
- QT_DEPRECATED_VERSION_X_5_14("Use storeRelaxed") void store(T newValue) noexcept { storeRelaxed(newValue); }
-#endif
-
+ // Everything below is either implemented in ../arch/qatomic_XXX.h or (as
+ // fallback) in qgenericatomic.h
T loadRelaxed() const noexcept { return Ops::loadRelaxed(_q_value); }
void storeRelaxed(T newValue) noexcept { Ops::storeRelaxed(_q_value, newValue); }
@@ -112,14 +41,14 @@ public:
operator T() const noexcept { return loadAcquire(); }
T operator=(T newValue) noexcept { storeRelease(newValue); return newValue; }
- static Q_DECL_CONSTEXPR bool isReferenceCountingNative() noexcept { return Ops::isReferenceCountingNative(); }
- static Q_DECL_CONSTEXPR bool isReferenceCountingWaitFree() noexcept { return Ops::isReferenceCountingWaitFree(); }
+ static constexpr bool isReferenceCountingNative() noexcept { return Ops::isReferenceCountingNative(); }
+ static constexpr bool isReferenceCountingWaitFree() noexcept { return Ops::isReferenceCountingWaitFree(); }
bool ref() noexcept { return Ops::ref(_q_value); }
bool deref() noexcept { return Ops::deref(_q_value); }
- static Q_DECL_CONSTEXPR bool isTestAndSetNative() noexcept { return Ops::isTestAndSetNative(); }
- static Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() noexcept { return Ops::isTestAndSetWaitFree(); }
+ static constexpr bool isTestAndSetNative() noexcept { return Ops::isTestAndSetNative(); }
+ static constexpr bool isTestAndSetWaitFree() noexcept { return Ops::isTestAndSetWaitFree(); }
bool testAndSetRelaxed(T expectedValue, T newValue) noexcept
{ return Ops::testAndSetRelaxed(_q_value, expectedValue, newValue); }
@@ -139,8 +68,8 @@ public:
bool testAndSetOrdered(T expectedValue, T newValue, T &currentValue) noexcept
{ return Ops::testAndSetOrdered(_q_value, expectedValue, newValue, &currentValue); }
- static Q_DECL_CONSTEXPR bool isFetchAndStoreNative() noexcept { return Ops::isFetchAndStoreNative(); }
- static Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() noexcept { return Ops::isFetchAndStoreWaitFree(); }
+ static constexpr bool isFetchAndStoreNative() noexcept { return Ops::isFetchAndStoreNative(); }
+ static constexpr bool isFetchAndStoreWaitFree() noexcept { return Ops::isFetchAndStoreWaitFree(); }
T fetchAndStoreRelaxed(T newValue) noexcept
{ return Ops::fetchAndStoreRelaxed(_q_value, newValue); }
@@ -151,8 +80,8 @@ public:
T fetchAndStoreOrdered(T newValue) noexcept
{ return Ops::fetchAndStoreOrdered(_q_value, newValue); }
- static Q_DECL_CONSTEXPR bool isFetchAndAddNative() noexcept { return Ops::isFetchAndAddNative(); }
- static Q_DECL_CONSTEXPR bool isFetchAndAddWaitFree() noexcept { return Ops::isFetchAndAddWaitFree(); }
+ static constexpr bool isFetchAndAddNative() noexcept { return Ops::isFetchAndAddNative(); }
+ static constexpr bool isFetchAndAddWaitFree() noexcept { return Ops::isFetchAndAddWaitFree(); }
T fetchAndAddRelaxed(T valueToAdd) noexcept
{ return Ops::fetchAndAddRelaxed(_q_value, valueToAdd); }
@@ -220,13 +149,11 @@ public:
{ return fetchAndXorOrdered(v) ^ v; }
-#ifdef QT_BASIC_ATOMIC_HAS_CONSTRUCTORS
QBasicAtomicInteger() = default;
constexpr QBasicAtomicInteger(T value) noexcept : _q_value(value) {}
QBasicAtomicInteger(const QBasicAtomicInteger &) = delete;
QBasicAtomicInteger &operator=(const QBasicAtomicInteger &) = delete;
QBasicAtomicInteger &operator=(const QBasicAtomicInteger &) volatile = delete;
-#endif
};
typedef QBasicAtomicInteger<int> QBasicAtomicInt;
@@ -240,11 +167,6 @@ public:
AtomicType _q_value;
-#if QT_DEPRECATED_SINCE(5, 14)
- QT_DEPRECATED_VERSION_X_5_14("Use loadRelaxed") Type load() const noexcept { return loadRelaxed(); }
- QT_DEPRECATED_VERSION_X_5_14("Use storeRelaxed") void store(Type newValue) noexcept { storeRelaxed(newValue); }
-#endif
-
Type loadRelaxed() const noexcept { return Ops::loadRelaxed(_q_value); }
void storeRelaxed(Type newValue) noexcept { Ops::storeRelaxed(_q_value, newValue); }
@@ -255,8 +177,8 @@ public:
Type loadAcquire() const noexcept { return Ops::loadAcquire(_q_value); }
void storeRelease(Type newValue) noexcept { Ops::storeRelease(_q_value, newValue); }
- static Q_DECL_CONSTEXPR bool isTestAndSetNative() noexcept { return Ops::isTestAndSetNative(); }
- static Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() noexcept { return Ops::isTestAndSetWaitFree(); }
+ static constexpr bool isTestAndSetNative() noexcept { return Ops::isTestAndSetNative(); }
+ static constexpr bool isTestAndSetWaitFree() noexcept { return Ops::isTestAndSetWaitFree(); }
bool testAndSetRelaxed(Type expectedValue, Type newValue) noexcept
{ return Ops::testAndSetRelaxed(_q_value, expectedValue, newValue); }
@@ -276,8 +198,8 @@ public:
bool testAndSetOrdered(Type expectedValue, Type newValue, Type &currentValue) noexcept
{ return Ops::testAndSetOrdered(_q_value, expectedValue, newValue, &currentValue); }
- static Q_DECL_CONSTEXPR bool isFetchAndStoreNative() noexcept { return Ops::isFetchAndStoreNative(); }
- static Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() noexcept { return Ops::isFetchAndStoreWaitFree(); }
+ static constexpr bool isFetchAndStoreNative() noexcept { return Ops::isFetchAndStoreNative(); }
+ static constexpr bool isFetchAndStoreWaitFree() noexcept { return Ops::isFetchAndStoreWaitFree(); }
Type fetchAndStoreRelaxed(Type newValue) noexcept
{ return Ops::fetchAndStoreRelaxed(_q_value, newValue); }
@@ -288,8 +210,8 @@ public:
Type fetchAndStoreOrdered(Type newValue) noexcept
{ return Ops::fetchAndStoreOrdered(_q_value, newValue); }
- static Q_DECL_CONSTEXPR bool isFetchAndAddNative() noexcept { return Ops::isFetchAndAddNative(); }
- static Q_DECL_CONSTEXPR bool isFetchAndAddWaitFree() noexcept { return Ops::isFetchAndAddWaitFree(); }
+ static constexpr bool isFetchAndAddNative() noexcept { return Ops::isFetchAndAddNative(); }
+ static constexpr bool isFetchAndAddWaitFree() noexcept { return Ops::isFetchAndAddWaitFree(); }
Type fetchAndAddRelaxed(qptrdiff valueToAdd) noexcept
{ return Ops::fetchAndAddRelaxed(_q_value, valueToAdd); }
@@ -322,13 +244,11 @@ public:
Type operator-=(qptrdiff valueToSub) noexcept
{ return fetchAndSubOrdered(valueToSub) - valueToSub; }
-#ifdef QT_BASIC_ATOMIC_HAS_CONSTRUCTORS
QBasicAtomicPointer() = default;
constexpr QBasicAtomicPointer(Type value) noexcept : _q_value(value) {}
QBasicAtomicPointer(const QBasicAtomicPointer &) = delete;
QBasicAtomicPointer &operator=(const QBasicAtomicPointer &) = delete;
QBasicAtomicPointer &operator=(const QBasicAtomicPointer &) volatile = delete;
-#endif
};
#ifndef Q_BASIC_ATOMIC_INITIALIZER
diff --git a/src/corelib/thread/qexception.cpp b/src/corelib/thread/qexception.cpp
index 2dc277523a..a623dc1c6e 100644
--- a/src/corelib/thread/qexception.cpp
+++ b/src/corelib/thread/qexception.cpp
@@ -1,46 +1,10 @@
-/****************************************************************************
-**
-** 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
#include "qexception.h"
#include "QtCore/qshareddata.h"
-#if !defined(QT_NO_EXCEPTIONS) || defined(Q_CLANG_QDOC)
+#if !defined(QT_NO_EXCEPTIONS) || defined(Q_QDOC)
QT_BEGIN_NAMESPACE
@@ -62,7 +26,7 @@ QT_BEGIN_NAMESPACE
\snippet code/src_corelib_thread_qexception.cpp 1
If you throw an exception that is not a subclass of QException,
- the Qt functions will throw a QUnhandledException
+ the \l{Qt Concurrent} functions will throw a QUnhandledException
in the receiver thread.
When using QFuture, transferred exceptions will be thrown when calling the following functions:
@@ -92,12 +56,18 @@ QT_BEGIN_NAMESPACE
\class QUnhandledException
\inmodule QtCore
- \brief The UnhandledException class represents an unhandled exception in a worker thread.
+ \brief The QUnhandledException class represents an unhandled exception in a
+ Qt Concurrent worker thread.
\since 5.0
If a worker thread throws an exception that is not a subclass of QException,
- the Qt functions will throw a QUnhandledException
- on the receiver thread side.
+ the \l{Qt Concurrent} functions will throw a QUnhandledException on the receiver
+ thread side. The information about the actual exception that has been thrown
+ will be saved in the QUnhandledException class and can be obtained using the
+ exception() method. For example, you can process the exception held by
+ QUnhandledException in the following way:
+
+ \snippet code/src_corelib_thread_qexception.cpp 4
Inheriting from this class is not supported.
*/
@@ -112,14 +82,8 @@ QT_BEGIN_NAMESPACE
\internal
*/
-QException::~QException()
-#ifdef Q_COMPILER_NOEXCEPT
- noexcept
-#else
- throw()
-#endif
+QException::~QException() noexcept
{
- // must stay empty until ### Qt 6
}
void QException::raise() const
@@ -133,14 +97,76 @@ QException *QException::clone() const
return new QException(*this);
}
-QUnhandledException::~QUnhandledException()
-#ifdef Q_COMPILER_NOEXCEPT
- noexcept
-#else
- throw()
-#endif
+class QUnhandledExceptionPrivate : public QSharedData
+{
+public:
+ QUnhandledExceptionPrivate(std::exception_ptr exception) noexcept : exceptionPtr(exception) { }
+ std::exception_ptr exceptionPtr;
+};
+
+/*!
+ \fn QUnhandledException::QUnhandledException(std::exception_ptr exception = nullptr) noexcept
+ \since 6.0
+
+ Constructs a new QUnhandledException object. Saves the pointer to the actual
+ exception object if \a exception is passed.
+
+ \sa exception()
+*/
+QUnhandledException::QUnhandledException(std::exception_ptr exception) noexcept
+ : d(new QUnhandledExceptionPrivate(exception))
+{
+}
+
+/*!
+ Move-constructs a QUnhandledException, making it point to the same
+ object as \a other was pointing to.
+*/
+QUnhandledException::QUnhandledException(QUnhandledException &&other) noexcept
+ : d(std::exchange(other.d, {}))
+{
+}
+
+/*!
+ Constructs a QUnhandledException object as a copy of \a other.
+*/
+QUnhandledException::QUnhandledException(const QUnhandledException &other) noexcept
+ : d(other.d)
+{
+}
+
+/*!
+ Assigns \a other to this QUnhandledException object and returns a reference
+ to this QUnhandledException object.
+*/
+QUnhandledException &QUnhandledException::operator=(const QUnhandledException &other) noexcept
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \fn void QUnhandledException::swap(QUnhandledException &other)
+ \since 6.0
+
+ Swaps this QUnhandledException with \a other. This function is very fast and
+ never fails.
+*/
+
+/*!
+ \since 6.0
+
+ Returns a \l{https://en.cppreference.com/w/cpp/error/exception_ptr}{pointer} to
+ the actual exception that has been saved in this QUnhandledException. Returns a
+ \c null pointer, if it does not point to an exception object.
+*/
+std::exception_ptr QUnhandledException::exception() const
+{
+ return d->exceptionPtr;
+}
+
+QUnhandledException::~QUnhandledException() noexcept
{
- // must stay empty until ### Qt 6
}
void QUnhandledException::raise() const
@@ -154,7 +180,7 @@ QUnhandledException *QUnhandledException::clone() const
return new QUnhandledException(*this);
}
-#if !defined(Q_CLANG_QDOC)
+#if !defined(Q_QDOC)
namespace QtPrivate {
@@ -190,9 +216,15 @@ void ExceptionStore::throwPossibleException()
std::rethrow_exception(exceptionHolder);
}
+void ExceptionStore::rethrowException() const
+{
+ Q_ASSERT(hasException());
+ std::rethrow_exception(exceptionHolder);
+}
+
} // namespace QtPrivate
-#endif //Q_CLANG_QDOC
+#endif //Q_QDOC
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qexception.h b/src/corelib/thread/qexception.h
index b117d90caf..62b9e70bea 100644
--- a/src/corelib/thread/qexception.h
+++ b/src/corelib/thread/qexception.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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 QTCORE_QEXCEPTION_H
#define QTCORE_QEXCEPTION_H
@@ -52,34 +16,38 @@ QT_REQUIRE_CONFIG(future);
QT_BEGIN_NAMESPACE
-#if !defined(QT_NO_EXCEPTIONS) || defined(Q_CLANG_QDOC)
+#if !defined(QT_NO_EXCEPTIONS) || defined(Q_QDOC)
class Q_CORE_EXPORT QException : public std::exception
{
public:
- ~QException()
-#ifdef Q_COMPILER_NOEXCEPT
- noexcept
-#else
- throw()
-#endif
- ;
+ ~QException() noexcept;
virtual void raise() const;
virtual QException *clone() const;
};
-class Q_CORE_EXPORT QUnhandledException : public QException
+class QUnhandledExceptionPrivate;
+class Q_CORE_EXPORT QUnhandledException final : public QException
{
public:
- ~QUnhandledException()
-#ifdef Q_COMPILER_NOEXCEPT
- noexcept
-#else
- throw()
-#endif
- ;
+ QUnhandledException(std::exception_ptr exception = nullptr) noexcept;
+ ~QUnhandledException() noexcept override;
+
+ QUnhandledException(QUnhandledException &&other) noexcept;
+ QUnhandledException(const QUnhandledException &other) noexcept;
+
+ void swap(QUnhandledException &other) noexcept { d.swap(other.d); }
+
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QUnhandledException)
+ QUnhandledException &operator=(const QUnhandledException &other) noexcept;
+
void raise() const override;
QUnhandledException *clone() const override;
+
+ std::exception_ptr exception() const;
+
+private:
+ QSharedDataPointer<QUnhandledExceptionPrivate> d;
};
namespace QtPrivate {
@@ -92,6 +60,7 @@ public:
bool hasException() const;
std::exception_ptr exception() const;
void throwPossibleException();
+ Q_NORETURN void rethrowException() const;
std::exception_ptr exceptionHolder;
};
@@ -106,6 +75,7 @@ class Q_CORE_EXPORT ExceptionStore
public:
ExceptionStore() { }
inline void throwPossibleException() {}
+ inline void rethrowException() const { }
};
} // namespace QtPrivate
diff --git a/src/corelib/thread/qfutex_freebsd_p.h b/src/corelib/thread/qfutex_freebsd_p.h
new file mode 100644
index 0000000000..b31774d28d
--- /dev/null
+++ b/src/corelib/thread/qfutex_freebsd_p.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2023 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFUTEX_FREEBSD_P_H
+#define QFUTEX_FREEBSD_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qcore_unix_p.h>
+#include <qdeadlinetimer.h>
+
+// https://man.freebsd.org/cgi/man.cgi?query=_umtx_op
+#include <sys/umtx.h>
+
+#define QT_ALWAYS_USE_FUTEX
+
+QT_BEGIN_NAMESPACE
+
+namespace QtFreeBSDFutex {
+constexpr inline bool futexAvailable() { return true; }
+
+template <typename Atomic>
+inline int do_wait(Atomic &futex, typename Atomic::Type expectedValue, _umtx_time *tmp = nullptr)
+{
+ // FreeBSD UMTX_OP_WAIT does not apply acquire or release memory barriers,
+ // so there are no QtTsan calls here.
+
+ int op = UMTX_OP_WAIT_UINT_PRIVATE;
+ if (sizeof(futex) > sizeof(quint32))
+ op = UMTX_OP_WAIT; // no _PRIVATE version
+
+ // The timeout is passed in uaddr2, with its size in uaddr
+ void *uaddr = reinterpret_cast<void *>(tmp ? sizeof(*tmp) : 0);
+ void *uaddr2 = tmp;
+ int ret = _umtx_op(&futex, op, u_long(expectedValue), uaddr, uaddr2);
+
+ return ret;
+}
+
+template <typename Atomic>
+inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
+{
+ do_wait(futex, expectedValue);
+}
+
+template <typename Atomic>
+inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, QDeadlineTimer timer)
+{
+ struct _umtx_time tm = {};
+ auto deadline = timer.deadline<std::chrono::steady_clock>();
+ tm._timeout = durationToTimespec(deadline.time_since_epoch());
+ tm._flags = UMTX_ABSTIME;
+ tm._clockid = CLOCK_MONOTONIC;
+ int r = do_wait(futex, expectedValue, &tm);
+ return r == 0 || errno != ETIMEDOUT;
+}
+
+template <typename Atomic> inline void futexWakeOne(Atomic &futex)
+{
+ _umtx_op(&futex, UMTX_OP_WAKE_PRIVATE, 1, nullptr, nullptr);
+}
+
+template <typename Atomic> inline void futexWakeAll(Atomic &futex)
+{
+ _umtx_op(&futex, UMTX_OP_WAKE_PRIVATE, INT_MAX, nullptr, nullptr);
+}
+} //namespace QtFreeBSDFutex
+
+namespace QtFutex = QtFreeBSDFutex;
+
+QT_END_NAMESPACE
+
+#endif // QFUTEX_FREEBSD_P_H
diff --git a/src/corelib/thread/qfutex_linux_p.h b/src/corelib/thread/qfutex_linux_p.h
new file mode 100644
index 0000000000..e114dfca72
--- /dev/null
+++ b/src/corelib/thread/qfutex_linux_p.h
@@ -0,0 +1,95 @@
+// Copyright (C) 2023 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFUTEX_LINUX_P_H
+#define QFUTEX_LINUX_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qcore_unix_p.h>
+#include <qdeadlinetimer.h>
+#include <qtsan_impl.h>
+
+#include <asm/unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <linux/futex.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+// RISC-V does not supply __NR_futex
+#ifndef __NR_futex
+# define __NR_futex __NR_futex_time64
+#endif
+
+#define QT_ALWAYS_USE_FUTEX
+
+QT_BEGIN_NAMESPACE
+
+namespace QtLinuxFutex {
+constexpr inline bool futexAvailable() { return true; }
+
+inline int _q_futex(int *addr, int op, int val, quintptr val2 = 0,
+ int *addr2 = nullptr, int val3 = 0) noexcept
+{
+ QtTsan::futexRelease(addr, addr2);
+
+ // we use __NR_futex because some libcs (like Android's bionic) don't
+ // provide SYS_futex etc.
+ int result = syscall(__NR_futex, addr, op | FUTEX_PRIVATE_FLAG, val, val2, addr2, val3);
+
+ QtTsan::futexAcquire(addr, addr2);
+
+ return result;
+}
+template <typename T> int *addr(T *ptr)
+{
+ int *int_addr = reinterpret_cast<int *>(ptr);
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ if (sizeof(T) > sizeof(int))
+ int_addr++; //We want a pointer to the least significant half
+#endif
+ return int_addr;
+}
+
+template <typename Atomic>
+inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
+{
+ _q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue));
+}
+template <typename Atomic>
+inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, QDeadlineTimer deadline)
+{
+ auto timeout = deadline.deadline<std::chrono::steady_clock>().time_since_epoch();
+ struct timespec ts = durationToTimespec(timeout);
+ int r = _q_futex(addr(&futex), FUTEX_WAIT_BITSET, qintptr(expectedValue), quintptr(&ts),
+ nullptr, FUTEX_BITSET_MATCH_ANY);
+ return r == 0 || errno != ETIMEDOUT;
+}
+template <typename Atomic> inline void futexWakeOne(Atomic &futex)
+{
+ _q_futex(addr(&futex), FUTEX_WAKE, 1);
+}
+template <typename Atomic> inline void futexWakeAll(Atomic &futex)
+{
+ _q_futex(addr(&futex), FUTEX_WAKE, INT_MAX);
+}
+template <typename Atomic> inline
+void futexWakeOp(Atomic &futex1, int wake1, int wake2, Atomic &futex2, quint32 op)
+{
+ _q_futex(addr(&futex1), FUTEX_WAKE_OP, wake1, wake2, addr(&futex2), op);
+}} // namespace QtLinuxFutex
+namespace QtFutex = QtLinuxFutex;
+
+QT_END_NAMESPACE
+
+#endif // QFUTEX_LINUX_P_H
diff --git a/src/corelib/thread/qfutex_mac_p.h b/src/corelib/thread/qfutex_mac_p.h
new file mode 100644
index 0000000000..0de08954ab
--- /dev/null
+++ b/src/corelib/thread/qfutex_mac_p.h
@@ -0,0 +1,140 @@
+// Copyright (C) 2023 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFUTEX_MAC_P_H
+#define QFUTEX_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qdeadlinetimer.h>
+#include <qtsan_impl.h>
+#include <private/qglobal_p.h>
+
+// The Darwin kernel exposes a set of __ulock_{wait,wait2,wake} APIs in
+// https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.81.2/bsd/sys/ulock.h,
+// but these APIs are marked as private, so we cannot rely on them being
+// stable, nor we can use these APIs in builds of Qt intended for
+// the Apple App Store. By wholesale disabling the use of the APIs
+// in App Store compliant builds, and runtime checking availability
+// of the APIs when we do build them in, we should be safe, unless
+// the semantics of the APIs change in ways we haven't accounted for,
+// but that's a risk we're willing to take.
+
+#if QT_CONFIG(appstore_compliant)
+QT_BEGIN_NAMESPACE
+namespace QtFutex = QtDummyFutex;
+QT_END_NAMESPACE
+#else
+
+extern "C" {
+// -------- BEGIN OS Declarations --------
+// Source: https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.81.2/bsd/sys/ulock.h
+// Modification: added __attribute((__weak__))
+// Copyright (c) 2015 Apple Inc. All rights reserved.
+
+__attribute((__weak__))
+extern int __ulock_wait2(uint32_t operation, void *addr, uint64_t value,
+ uint64_t timeout, uint64_t value2);
+__attribute((__weak__))
+extern int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
+
+/*
+ * operation bits [7, 0] contain the operation code.
+ */
+#define UL_COMPARE_AND_WAIT 1
+#define UL_COMPARE_AND_WAIT_SHARED 3
+#define UL_COMPARE_AND_WAIT64 5
+#define UL_COMPARE_AND_WAIT64_SHARED 6
+
+/*
+ * operation bits [15, 8] contain the flags for __ulock_wake
+ */
+#define ULF_WAKE_ALL 0x00000100
+#define ULF_WAKE_THREAD 0x00000200
+#define ULF_WAKE_ALLOW_NON_OWNER 0x00000400
+
+/*
+ * operation bits [15, 8] contain the flags for __ulock_wake
+ */
+#define ULF_WAKE_ALL 0x00000100
+#define ULF_WAKE_THREAD 0x00000200
+#define ULF_WAKE_ALLOW_NON_OWNER 0x00000400
+
+/*
+ * operation bits [31, 24] contain the generic flags
+ */
+#define ULF_NO_ERRNO 0x01000000
+
+// -------- END OS Declarations --------
+} // extern "C"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtDarwinFutex {
+
+/*not constexpr*/ inline bool futexAvailable() { return __ulock_wake && __ulock_wait2; }
+
+template <typename Atomic>
+inline uint32_t baseOperation(Atomic &)
+{
+ static_assert(sizeof(Atomic) >= sizeof(quint32), "Can only operate on 32- or 64-bit atomics");
+
+ uint32_t operation = ULF_NO_ERRNO;
+ if (sizeof(Atomic) == sizeof(quint32))
+ operation |= UL_COMPARE_AND_WAIT;
+ else
+ operation |= UL_COMPARE_AND_WAIT64;
+ return operation;
+}
+
+template <typename Atomic> inline int
+do_wait(Atomic &futex, typename Atomic::Type expectedValue, QDeadlineTimer timer)
+{
+ // source code inspection shows __ulock_wait2 uses nanoseconds for timeout
+ QtTsan::futexRelease(&futex);
+ int ret = __ulock_wait2(baseOperation(futex), &futex, uint64_t(expectedValue),
+ timer.remainingTimeNSecs(), 0);
+ QtTsan::futexAcquire(&futex);
+ return ret;
+}
+
+template <typename Atomic>
+inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
+{
+ do_wait(futex, expectedValue, {});
+}
+
+template <typename Atomic>
+inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, QDeadlineTimer timer)
+{
+ int r = do_wait(futex, expectedValue, timer);
+ return r == 0 || r != -ETIMEDOUT;
+}
+
+template <typename Atomic> inline void futexWakeAll(Atomic &futex)
+{
+ __ulock_wake(baseOperation(futex) | ULF_WAKE_ALL, &futex, 0);
+}
+
+template <typename Atomic> inline void futexWakeOne(Atomic &futex)
+{
+ __ulock_wake(baseOperation(futex), &futex, 0);
+}
+} //namespace QtDarwinMutex
+
+namespace QtFutex = QtDarwinFutex;
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(appstore_compliant)
+
+#endif // QFUTEX_MAC_P_H
diff --git a/src/corelib/thread/qfutex_p.h b/src/corelib/thread/qfutex_p.h
index f287b752d7..8ba798f920 100644
--- a/src/corelib/thread/qfutex_p.h
+++ b/src/corelib/thread/qfutex_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Intel Corporation.
-** 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) 2017 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFUTEX_P_H
#define QFUTEX_P_H
@@ -51,15 +15,16 @@
// We mean it.
//
-#include <qglobal.h>
+#include <qdeadlinetimer.h>
+#include <private/qglobal_p.h>
QT_BEGIN_NAMESPACE
namespace QtDummyFutex {
- Q_DECL_CONSTEXPR inline bool futexAvailable() { return false; }
+ constexpr inline bool futexAvailable() { return false; }
template <typename Atomic>
- inline bool futexWait(Atomic &, typename Atomic::Type, int = 0)
- { Q_UNREACHABLE(); return false; }
+ inline bool futexWait(Atomic &, typename Atomic::Type, QDeadlineTimer = {})
+ { Q_UNREACHABLE_RETURN(false); }
template <typename Atomic> inline void futexWakeOne(Atomic &)
{ Q_UNREACHABLE(); }
template <typename Atomic> inline void futexWakeAll(Atomic &)
@@ -68,100 +33,16 @@ namespace QtDummyFutex {
QT_END_NAMESPACE
-#if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
+#if defined(Q_OS_DARWIN)
+# include "qfutex_mac_p.h"
+#elif defined(Q_OS_FREEBSD)
+# include "qfutex_freebsd_p.h"
+#elif defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
// use Linux mutexes everywhere except for LSB builds
-# include <sys/syscall.h>
-# include <errno.h>
-# include <limits.h>
-# include <unistd.h>
-# include <asm/unistd.h>
-# include <linux/futex.h>
-# define QT_ALWAYS_USE_FUTEX
-
-// if not defined in linux/futex.h
-# define FUTEX_PRIVATE_FLAG 128 // added in v2.6.22
-
-# if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)
-# include <sanitizer/tsan_interface.h>
-inline void _q_tsan_acquire(void *addr, void *addr2)
-{
- __tsan_acquire(addr);
- if (addr2)
- __tsan_acquire(addr2);
-}
-inline void _q_tsan_release(void *addr, void *addr2)
-{
- if (addr2)
- __tsan_release(addr2);
- __tsan_release(addr);
-}
-# else
-inline void _q_tsan_acquire(void *, void *) {}
-inline void _q_tsan_release(void *, void *) {}
-# endif // __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)
-
-QT_BEGIN_NAMESPACE
-namespace QtLinuxFutex {
- constexpr inline bool futexAvailable() { return true; }
- inline int _q_futex(int *addr, int op, int val, quintptr val2 = 0,
- int *addr2 = nullptr, int val3 = 0) noexcept
- {
- // A futex call ensures total ordering on the futex words
- // (in either success or failure of the call). Instruct TSAN accordingly,
- // as TSAN does not understand the futex(2) syscall.
- _q_tsan_release(addr, addr2);
-
- // we use __NR_futex because some libcs (like Android's bionic) don't
- // provide SYS_futex etc.
- int result = syscall(__NR_futex, addr, op | FUTEX_PRIVATE_FLAG, val, val2, addr2, val3);
-
- _q_tsan_acquire(addr, addr2);
-
- return result;
- }
- template <typename T> int *addr(T *ptr)
- {
- int *int_addr = reinterpret_cast<int *>(ptr);
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- if (sizeof(T) > sizeof(int))
- int_addr++; //We want a pointer to the least significant half
-#endif
- return int_addr;
- }
-
- template <typename Atomic>
- inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
- {
- _q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue));
- }
- template <typename Atomic>
- inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, qint64 nstimeout)
- {
- struct timespec ts;
- ts.tv_sec = nstimeout / 1000 / 1000 / 1000;
- ts.tv_nsec = nstimeout % (1000 * 1000 * 1000);
- int r = _q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue), quintptr(&ts));
- return r == 0 || errno != ETIMEDOUT;
- }
- template <typename Atomic> inline void futexWakeOne(Atomic &futex)
- {
- _q_futex(addr(&futex), FUTEX_WAKE, 1);
- }
- template <typename Atomic> inline void futexWakeAll(Atomic &futex)
- {
- _q_futex(addr(&futex), FUTEX_WAKE, INT_MAX);
- }
- template <typename Atomic> inline
- void futexWakeOp(Atomic &futex1, int wake1, int wake2, Atomic &futex2, quint32 op)
- {
- _q_futex(addr(&futex1), FUTEX_WAKE_OP, wake1, wake2, addr(&futex2), op);
- }
-}
-namespace QtFutex = QtLinuxFutex;
-QT_END_NAMESPACE
-
+# include "qfutex_linux_p.h"
+#elif defined(Q_OS_WIN)
+# include "qfutex_win_p.h"
#else
-
QT_BEGIN_NAMESPACE
namespace QtFutex = QtDummyFutex;
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qfutex_win_p.h b/src/corelib/thread/qfutex_win_p.h
new file mode 100644
index 0000000000..75a12bd82c
--- /dev/null
+++ b/src/corelib/thread/qfutex_win_p.h
@@ -0,0 +1,58 @@
+// Copyright (C) 2023 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFUTEX_WIN_P_H
+#define QFUTEX_WIN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qglobal_p.h>
+#include <qdeadlinetimer.h>
+#include <qtsan_impl.h>
+
+#include <qt_windows.h>
+
+#define QT_ALWAYS_USE_FUTEX
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWindowsFutex {
+constexpr inline bool futexAvailable() { return true; }
+
+template <typename Atomic>
+inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
+{
+ QtTsan::futexRelease(&futex);
+ WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), INFINITE);
+ QtTsan::futexAcquire(&futex);
+}
+template <typename Atomic>
+inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, QDeadlineTimer deadline)
+{
+ using namespace std::chrono;
+ BOOL r = WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), DWORD(deadline.remainingTime()));
+ return r || GetLastError() != ERROR_TIMEOUT;
+}
+template <typename Atomic> inline void futexWakeAll(Atomic &futex)
+{
+ WakeByAddressAll(&futex);
+}
+template <typename Atomic> inline void futexWakeOne(Atomic &futex)
+{
+ WakeByAddressSingle(&futex);
+}
+} // namespace QtWindowsFutex
+namespace QtFutex = QtWindowsFutex;
+
+QT_END_NAMESPACE
+
+#endif // QFUTEX_WIN_P_H
diff --git a/src/corelib/thread/qfuture.h b/src/corelib/thread/qfuture.h
index dc8e0d1d2d..5939a93780 100644
--- a/src/corelib/thread/qfuture.h
+++ b/src/corelib/thread/qfuture.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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 QFUTURE_H
#define QFUTURE_H
@@ -43,12 +7,12 @@
#include <QtCore/qglobal.h>
#include <QtCore/qfutureinterface.h>
+#include <QtCore/qmetatype.h>
#include <QtCore/qstring.h>
#include <QtCore/qfuture_impl.h>
#include <type_traits>
-#include <vector>
QT_REQUIRE_CONFIG(future);
@@ -60,10 +24,9 @@ class QFutureWatcher;
template <typename T>
class QFuture
{
- static_assert (std::is_copy_constructible_v<T>
- || std::is_move_constructible_v<T>
+ static_assert (std::is_move_constructible_v<T>
|| std::is_same_v<T, void>,
- "Type with copy or move constructors or type void is required");
+ "A move-constructible type or type void is required");
public:
QFuture()
: d(QFutureInterface<T>::canceledResult())
@@ -80,9 +43,8 @@ public:
{
}
-#if !defined(Q_CC_XLC)
template<typename U, typename V = T, typename = QtPrivate::EnableForVoid<V>>
- QFuture(const QFuture<U> &other) : d(other.d)
+ explicit QFuture(const QFuture<U> &other) : d(other.d)
{
}
@@ -92,20 +54,13 @@ public:
d = other.d;
return *this;
}
-#endif
-#if defined(Q_CLANG_QDOC)
+#if defined(Q_QDOC)
~QFuture() { }
QFuture(const QFuture<T> &) { }
QFuture<T> & operator=(const QFuture<T> &) { }
-
- // This is required to allow QDoc to find the declaration of operator T().
- operator T() const;
#endif
- bool operator==(const QFuture &other) const { return (d == other.d); }
- bool operator!=(const QFuture &other) const { return (d != other.d); }
-
void cancel() { d.cancel(); }
bool isCanceled() const { return d.isCanceled(); }
@@ -155,18 +110,17 @@ QT_WARNING_POP
template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>>
bool isResultReadyAt(int resultIndex) const { return d.isResultReadyAt(resultIndex); }
- // operator T()
- template<typename U = T>
- operator typename std::enable_if_t<!std::is_same_v<U, void>, U>() const { return result(); }
-
template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>>
QList<T> results() const { return d.results(); }
template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>>
T takeResult() { return d.takeResult(); }
+#if 0
+ // TODO: Enable and make it return a QList, when QList is fixed to support move-only types
template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>>
std::vector<T> takeResults() { return d.takeResults(); }
+#endif
bool isValid() const { return d.isValid(); }
@@ -182,15 +136,33 @@ QT_WARNING_POP
template<class Function>
QFuture<ResultType<Function>> then(QThreadPool *pool, Function &&function);
+ template<class Function>
+ QFuture<ResultType<Function>> then(QObject *context, Function &&function);
+
#ifndef QT_NO_EXCEPTIONS
template<class Function,
typename = std::enable_if_t<!QtPrivate::ArgResolver<Function>::HasExtraArgs>>
QFuture<T> onFailed(Function &&handler);
+
+ template<class Function,
+ typename = std::enable_if_t<!QtPrivate::ArgResolver<Function>::HasExtraArgs>>
+ QFuture<T> onFailed(QObject *context, Function &&handler);
#endif
template<class Function, typename = std::enable_if_t<std::is_invocable_r_v<T, Function>>>
QFuture<T> onCanceled(Function &&handler);
+ template<class Function, typename = std::enable_if_t<std::is_invocable_r_v<T, Function>>>
+ QFuture<T> onCanceled(QObject *context, Function &&handler);
+
+#if !defined(Q_QDOC)
+ template<class U = T, typename = std::enable_if_t<QtPrivate::isQFutureV<U>>>
+ auto unwrap();
+#else
+ template<class U>
+ QFuture<U> unwrap();
+#endif
+
class const_iterator
{
public:
@@ -301,6 +273,8 @@ private:
template<class U>
friend class QFuture;
+ friend class QFutureInterfaceBase;
+
template<class Function, class ResultType, class ParentResultType>
friend class QtPrivate::Continuation;
@@ -312,6 +286,11 @@ private:
friend class QtPrivate::FailureHandler;
#endif
+ template<typename ResultType>
+ friend struct QtPrivate::WhenAnyContext;
+
+ friend struct QtPrivate::UnwrapHandler;
+
using QFuturePrivate =
std::conditional_t<std::is_same_v<T, void>, QFutureInterfaceBase, QFutureInterface<T>>;
@@ -356,7 +335,7 @@ QFuture<typename QFuture<T>::template ResultType<Function>>
QFuture<T>::then(QtFuture::Launch policy, Function &&function)
{
QFutureInterface<ResultType<Function>> promise(QFutureInterfaceBase::State::Pending);
- QtPrivate::Continuation<Function, ResultType<Function>, T>::create(
+ QtPrivate::Continuation<std::decay_t<Function>, ResultType<Function>, T>::create(
std::forward<Function>(function), this, promise, policy);
return promise.future();
}
@@ -367,19 +346,40 @@ QFuture<typename QFuture<T>::template ResultType<Function>> QFuture<T>::then(QTh
Function &&function)
{
QFutureInterface<ResultType<Function>> promise(QFutureInterfaceBase::State::Pending);
- QtPrivate::Continuation<Function, ResultType<Function>, T>::create(
+ QtPrivate::Continuation<std::decay_t<Function>, ResultType<Function>, T>::create(
std::forward<Function>(function), this, promise, pool);
return promise.future();
}
-#ifndef QT_NO_EXCEPTIONS
+template<class T>
+template<class Function>
+QFuture<typename QFuture<T>::template ResultType<Function>> QFuture<T>::then(QObject *context,
+ Function &&function)
+{
+ QFutureInterface<ResultType<Function>> promise(QFutureInterfaceBase::State::Pending);
+ QtPrivate::Continuation<std::decay_t<Function>, ResultType<Function>, T>::create(
+ std::forward<Function>(function), this, promise, context);
+ return promise.future();
+}
+#ifndef QT_NO_EXCEPTIONS
template<class T>
template<class Function, typename>
QFuture<T> QFuture<T>::onFailed(Function &&handler)
{
QFutureInterface<T> promise(QFutureInterfaceBase::State::Pending);
- QtPrivate::FailureHandler<Function, T>::create(std::forward<Function>(handler), this, promise);
+ QtPrivate::FailureHandler<std::decay_t<Function>, T>::create(std::forward<Function>(handler),
+ this, promise);
+ return promise.future();
+}
+
+template<class T>
+template<class Function, typename>
+QFuture<T> QFuture<T>::onFailed(QObject *context, Function &&handler)
+{
+ QFutureInterface<T> promise(QFutureInterfaceBase::State::Pending);
+ QtPrivate::FailureHandler<std::decay_t<Function>, T>::create(std::forward<Function>(handler),
+ this, promise, context);
return promise.future();
}
@@ -390,23 +390,157 @@ template<class Function, typename>
QFuture<T> QFuture<T>::onCanceled(Function &&handler)
{
QFutureInterface<T> promise(QFutureInterfaceBase::State::Pending);
- QtPrivate::CanceledHandler<Function, T>::create(std::forward<Function>(handler), this, promise);
+ QtPrivate::CanceledHandler<std::decay_t<Function>, T>::create(std::forward<Function>(handler),
+ this, promise);
return promise.future();
}
+template<class T>
+template<class Function, typename>
+QFuture<T> QFuture<T>::onCanceled(QObject *context, Function &&handler)
+{
+ QFutureInterface<T> promise(QFutureInterfaceBase::State::Pending);
+ QtPrivate::CanceledHandler<std::decay_t<Function>, T>::create(std::forward<Function>(handler),
+ this, promise, context);
+ return promise.future();
+}
+
+template<class T>
+template<class U, typename>
+auto QFuture<T>::unwrap()
+{
+ if constexpr (QtPrivate::isQFutureV<typename QtPrivate::Future<T>::type>)
+ return QtPrivate::UnwrapHandler::unwrapImpl(this).unwrap();
+ else
+ return QtPrivate::UnwrapHandler::unwrapImpl(this);
+}
+
inline QFuture<void> QFutureInterface<void>::future()
{
return QFuture<void>(this);
}
-template <typename T>
-QFuture<void> qToVoidFuture(const QFuture<T> &future)
+template<typename T>
+QFutureInterfaceBase QFutureInterfaceBase::get(const QFuture<T> &future)
+{
+ return future.d;
+}
+
+namespace QtPrivate
+{
+
+template<typename T>
+struct MetaTypeQFutureHelper<QFuture<T>>
+{
+ static bool registerConverter() {
+ if constexpr (std::is_same_v<T, void>)
+ return false;
+
+ return QMetaType::registerConverter<QFuture<T>, QFuture<void>>(
+ [](const QFuture<T> &future) { return QFuture<void>(future); });
+ }
+};
+
+} // namespace QtPrivate
+
+namespace QtFuture {
+
+#ifndef Q_QDOC
+
+template<typename OutputSequence, typename InputIt,
+ typename ValueType = typename std::iterator_traits<InputIt>::value_type,
+ std::enable_if_t<std::conjunction_v<QtPrivate::IsForwardIterable<InputIt>,
+ QtPrivate::IsRandomAccessible<OutputSequence>,
+ QtPrivate::isQFuture<ValueType>>,
+ int> = 0>
+QFuture<OutputSequence> whenAll(InputIt first, InputIt last)
+{
+ return QtPrivate::whenAllImpl<OutputSequence, InputIt, ValueType>(first, last);
+}
+
+template<typename InputIt, typename ValueType = typename std::iterator_traits<InputIt>::value_type,
+ std::enable_if_t<std::conjunction_v<QtPrivate::IsForwardIterable<InputIt>,
+ QtPrivate::isQFuture<ValueType>>,
+ int> = 0>
+QFuture<QList<ValueType>> whenAll(InputIt first, InputIt last)
+{
+ return QtPrivate::whenAllImpl<QList<ValueType>, InputIt, ValueType>(first, last);
+}
+
+template<typename OutputSequence, typename... Futures,
+ std::enable_if_t<std::conjunction_v<QtPrivate::IsRandomAccessible<OutputSequence>,
+ QtPrivate::NotEmpty<Futures...>,
+ QtPrivate::isQFuture<std::decay_t<Futures>>...>,
+ int> = 0>
+QFuture<OutputSequence> whenAll(Futures &&... futures)
+{
+ return QtPrivate::whenAllImpl<OutputSequence, Futures...>(std::forward<Futures>(futures)...);
+}
+
+template<typename... Futures,
+ std::enable_if_t<std::conjunction_v<QtPrivate::NotEmpty<Futures...>,
+ QtPrivate::isQFuture<std::decay_t<Futures>>...>,
+ int> = 0>
+QFuture<QList<std::variant<std::decay_t<Futures>...>>> whenAll(Futures &&... futures)
+{
+ return QtPrivate::whenAllImpl<QList<std::variant<std::decay_t<Futures>...>>, Futures...>(
+ std::forward<Futures>(futures)...);
+}
+
+template<typename InputIt, typename ValueType = typename std::iterator_traits<InputIt>::value_type,
+ std::enable_if_t<std::conjunction_v<QtPrivate::IsForwardIterable<InputIt>,
+ QtPrivate::isQFuture<ValueType>>,
+ int> = 0>
+QFuture<WhenAnyResult<typename QtPrivate::Future<ValueType>::type>> whenAny(InputIt first,
+ InputIt last)
+{
+ return QtPrivate::whenAnyImpl<InputIt, ValueType>(first, last);
+}
+
+template<typename... Futures,
+ std::enable_if_t<std::conjunction_v<QtPrivate::NotEmpty<Futures...>,
+ QtPrivate::isQFuture<std::decay_t<Futures>>...>,
+ int> = 0>
+QFuture<std::variant<std::decay_t<Futures>...>> whenAny(Futures &&... futures)
+{
+ return QtPrivate::whenAnyImpl(std::forward<Futures>(futures)...);
+}
+
+#else
+
+template<typename OutputSequence, typename InputIt>
+QFuture<OutputSequence> whenAll(InputIt first, InputIt last);
+
+template<typename OutputSequence, typename... Futures>
+QFuture<OutputSequence> whenAll(Futures &&... futures);
+
+template<typename T, typename InputIt>
+QFuture<QtFuture::WhenAnyResult<T>> whenAny(InputIt first, InputIt last);
+
+template<typename... Futures>
+QFuture<std::variant<std::decay_t<Futures>...>> whenAny(Futures &&... futures);
+
+#endif // Q_QDOC
+
+#if QT_DEPRECATED_SINCE(6, 10)
+#if defined(Q_QDOC)
+static QFuture<void> makeReadyFuture()
+#else
+template<typename T = void>
+QT_DEPRECATED_VERSION_X(6, 10, "Use makeReadyVoidFuture() instead.")
+static QFuture<T> makeReadyFuture()
+#endif
{
- return QFuture<void>(future.d);
+ return makeReadyVoidFuture();
}
+#endif // QT_DEPRECATED_SINCE(6, 10)
+
+} // namespace QtFuture
Q_DECLARE_SEQUENTIAL_ITERATOR(Future)
QT_END_NAMESPACE
+Q_DECLARE_METATYPE_TEMPLATE_1ARG(QFuture)
+
#endif // QFUTURE_H
diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc
index b20875c423..9eda766968 100644
--- a/src/corelib/thread/qfuture.qdoc
+++ b/src/corelib/thread/qfuture.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** 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 Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*! \class QFuture
\inmodule QtCore
@@ -33,21 +9,18 @@
\ingroup thread
- To start a computation, use one of the APIs in the \l {Qt Concurrent} framework.
-
QFuture allows threads to be synchronized against one or more results
which will be ready at a later point in time. The result can be of any type
that has default, copy and possibly move constructors. If
a result is not available at the time of calling the result(), resultAt(),
- results(), takeResult(), or takeResults() functions, QFuture
- will wait until the result becomes available. You can use the isResultReadyAt()
- function to determine if a result is ready or not. For QFuture objects that
- report more than one result, the resultCount() function returns the number
- of continuous results. This means that it is always safe to iterate through
- the results from 0 to resultCount(). takeResult() and takeResults()
- invalidate a future and any subsequent attempt to access result or results
- from the future leads to undefined behavior. isValid() tells you if
- results can be accessed.
+ results() and takeResult() functions, QFuture will wait until the result
+ becomes available. You can use the isResultReadyAt() function to determine
+ if a result is ready or not. For QFuture objects that report more than one
+ result, the resultCount() function returns the number of continuous results.
+ This means that it is always safe to iterate through the results from 0 to
+ resultCount(). takeResult() invalidates a future, and any subsequent attempt
+ to access result or results from the future leads to undefined behavior.
+ isValid() tells you if results can be accessed.
QFuture provides a \l{Java-style iterators}{Java-style iterator}
(QFutureIterator) and an \l{STL-style iterators}{STL-style iterator}
@@ -68,7 +41,7 @@
using exceptions. Let's say we want to send a network request to obtain a large
file from a network location. Then we want to write it to the file system and
return its location in case of a success. Both of these operations may fail
- with different errors. So, we use std::variant to keep the result
+ with different errors. So, we use \c std::variant to keep the result
or error:
\snippet code/src_corelib_thread_qfuture.cpp 3
@@ -78,13 +51,13 @@
\snippet code/src_corelib_thread_qfuture.cpp 4
It's possible to chain multiple continuations and handlers in any order.
- The first handler that can handle the state of its parent is invoked first.
- If there's no proper handler, the state is propagated to the next continuation
- or handler. For example:
+ For example:
\snippet code/src_corelib_thread_qfuture.cpp 15
- If \c testFuture is successfully fulfilled \c {Block 1} will be called. If
+ Depending on the state of \c testFuture (canceled, has exception or has a
+ result), the next onCanceled(), onFailed() or then() will be called. So
+ if \c testFuture is successfully fulfilled, \c {Block 1} will be called. If
it succeeds as well, the next then() (\c {Block 4}) is called. If \c testFuture
gets canceled or fails with an exception, either \c {Block 2} or \c {Block 3}
will be called respectively. The next then() will be called afterwards, and the
@@ -102,7 +75,16 @@
If \c testFuture gets canceled, its state is propagated to the next then(),
which will be also canceled. So in this case \c {Block 6} will be called.
- QFuture also offers ways to interact with a runnning computation. For
+ The future can have only one continuation. Consider the following example:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 31
+
+ In this case \c f1 and \c f2 are effectively the same QFuture object, as
+ they share the same internal state. As a result, calling
+ \l {QFuture::}{then} on \c f2 will overwrite the continuation specified for
+ \c {f1}. So, only \c {"second"} will be printed when this code is executed.
+
+ QFuture also offers ways to interact with a running computation. For
instance, the computation can be canceled with the cancel() function. To
suspend or resume the computation, use the setSuspended() function or one of
the suspend(), resume(), or toggleSuspended() convenience functions. Be aware
@@ -119,9 +101,6 @@
the isCanceled(), isStarted(), isFinished(), isRunning(), isSuspending()
or isSuspended() functions.
- QFuture is a lightweight reference counted class that can be passed by
- value.
-
QFuture<void> is specialized to not contain any of the result fetching
functions. Any QFuture<T> can be assigned or copied into a QFuture<void>
as well. This is useful if only status or progress information is needed
@@ -129,13 +108,32 @@
To interact with running tasks using signals and slots, use QFutureWatcher.
- You can also use QtFuture::connect to connect signals to a QFuture object
+ You can also use QtFuture::connect() to connect signals to a QFuture object
which will be resolved when a signal is emitted. This allows working with
signals like with QFuture objects. For example, if you combine it with then(),
you can attach multiple continuations to a signal, which are invoked in the
same thread or a new thread.
- \sa QtFuture::connect(), QFutureWatcher, {Qt Concurrent}
+ The QtFuture::whenAll() and QtFuture::whenAny() functions can be used to
+ combine several futures and track when the last or first of them completes.
+
+ A ready QFuture object with a value or a QFuture object holding exception can
+ be created using convenience functions QtFuture::makeReadyVoidFuture(),
+ QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(), and
+ QtFuture::makeExceptionalFuture().
+
+ \note Some APIs (see \l {QFuture::then()} or various QtConcurrent method
+ overloads) allow scheduling the computation to a specific thread pool.
+ However, QFuture implements a work-stealing algorithm to prevent deadlocks
+ and optimize thread usage. As a result, computations can be executed
+ directly in the thread which requests the QFuture's result.
+
+ \note To start a computation and store results in a QFuture, use QPromise or
+ one of the APIs in the \l {Qt Concurrent} framework.
+
+ \sa QPromise, QtFuture::connect(), QtFuture::makeReadyVoidFuture(),
+ QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(),
+ QtFuture::makeExceptionalFuture(), QFutureWatcher, {Qt Concurrent}
*/
/*! \fn template <typename T> QFuture<T>::QFuture()
@@ -168,22 +166,11 @@
Assigns \a other to this future and returns a reference to this future.
*/
-/*! \fn template <typename T> bool QFuture<T>::operator==(const QFuture &other) const
-
- Returns \c true if \a other is a copy of this future; otherwise returns \c false.
-*/
-
-/*! \fn template <typename T> bool QFuture<T>::operator!=(const QFuture &other) const
-
- Returns \c true if \a other is \e not a copy of this future; otherwise returns
- false.
-*/
-
/*! \fn template <typename T> void QFuture<T>::cancel()
Cancels the asynchronous computation represented by this future. Note that
- the cancelation is asynchronous. Use waitForFinished() after calling
- cancel() when you need synchronous cancelation.
+ the cancellation is asynchronous. Use waitForFinished() after calling
+ cancel() when you need synchronous cancellation.
Results currently available may still be accessed on a canceled future,
but new results will \e not become available after calling this function.
@@ -208,8 +195,7 @@
#if QT_DEPRECATED_SINCE(6, 0)
/*! \fn template <typename T> void QFuture<T>::setPaused(bool paused)
- \obsolete
- Use setSuspended() instead.
+ \deprecated [6.0] Use setSuspended() instead.
If \a paused is true, this function pauses the asynchronous computation
represented by the future. If the computation is already paused, this
@@ -225,13 +211,12 @@
returned by QtConcurrent::run() cannot be paused; but the future returned
by QtConcurrent::mappedReduced() can.
- \sa pause(), resume(), togglePaused()
+ \sa suspend(), resume(), toggleSuspended()
*/
/*! \fn template <typename T> bool QFuture<T>::isPaused() const
- \obsolete
- Use isSuspending() or isSuspended() instead.
+ \deprecated [6.0] Use isSuspending() or isSuspended() instead.
Returns \c true if the asynchronous computation has been paused with the
pause() function; otherwise returns \c false.
@@ -240,13 +225,12 @@
function returns \c true. See setPaused() for more details. To check
if pause actually took effect, use isSuspended() instead.
- \sa setPaused(), togglePaused(), isSuspended()
+ \sa toggleSuspended(), isSuspended()
*/
/*! \fn template <typename T> void QFuture<T>::pause()
- \obsolete
- Use suspend() instead.
+ \deprecated [6.0] Use suspend() instead.
Pauses the asynchronous computation represented by this future. This is a
convenience method that simply calls setPaused(true).
@@ -256,15 +240,14 @@
/*! \fn template <typename T> void QFuture<T>::togglePaused()
- \obsolete
- Use toggleSuspended() instead.
+ \deprecated [6.0] Use toggleSuspended() instead.
Toggles the paused state of the asynchronous computation. In other words,
if the computation is currently paused, calling this function resumes it;
if the computation is running, it is paused. This is a convenience method
for calling setPaused(!isPaused()).
- \sa setPaused(), pause(), resume()
+ \sa setSuspended(), suspend(), resume()
*/
#endif // QT_DEPRECATED_SINCE(6, 0)
@@ -367,7 +350,7 @@
number of results stored might be different from this value, due to gaps
in the result set. It is always safe to iterate through the results from 0
to resultCount().
- \sa result(), resultAt(), results(), takeResult(), takeResults()
+ \sa result(), resultAt(), results(), takeResult()
*/
/*! \fn template <typename T> int QFuture<T>::progressValue() const
@@ -404,22 +387,25 @@
/*! \fn template <typename T> void QFuture<T>::waitForFinished()
Waits for the asynchronous computation to finish (including cancel()ed
- computations).
+ computations), i.e. until isFinished() returns \c true.
*/
-/*! \fn template <typename T> T QFuture<T>::result() const
+/*! \fn template <typename T> template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>> T QFuture<T>::result() const
Returns the first result in the future. If the result is not immediately
available, this function will block and wait for the result to become
- available. This is a convenience method for calling resultAt(0).
+ available. This is a convenience method for calling resultAt(0). Note
+ that \c result() returns a copy of the internally stored result. If \c T is
+ a move-only type, or you don't want to copy the result, use takeResult()
+ instead.
- \note Calling result() leads to undefined behavior if isValid()
+ \note Calling \c result() leads to undefined behavior if isValid()
returns \c false for this QFuture.
- \sa resultAt(), results(), takeResult(), takeResults()
+ \sa resultAt(), results(), takeResult()
*/
-/*! \fn template <typename T> T QFuture<T>::resultAt(int index) const
+/*! \fn template <typename T> template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>> T QFuture<T>::resultAt(int index) const
Returns the result at \a index in the future. If the result is not
immediately available, this function will block and wait for the result to
@@ -428,10 +414,10 @@
\note Calling resultAt() leads to undefined behavior if isValid()
returns \c false for this QFuture.
- \sa result(), results(), takeResult(), takeResults(), resultCount()
+ \sa result(), results(), takeResult(), resultCount()
*/
-/*! \fn template <typename T> bool QFuture<T>::isResultReadyAt(int index) const
+/*! \fn template <typename T> template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>> bool QFuture<T>::isResultReadyAt(int index) const
Returns \c true if the result at \a index is immediately available; otherwise
returns \c false.
@@ -439,34 +425,26 @@
\note Calling isResultReadyAt() leads to undefined behavior if isValid()
returns \c false for this QFuture.
- \sa resultAt(), resultCount(), takeResult(), takeResults()
-*/
-
-/*! \fn template <typename T> QFuture<T>::operator T() const
-
- Returns the first result in the future. If the result is not immediately
- available, this function will block and wait for the result to become
- available. This is a convenience method for calling result() or
- resultAt(0).
-
- \note Calling this function leads to undefined behavior if isValid()
- returns \c false for this QFuture.
-
- \sa result(), resultAt(), results(), takeResult(), takeResults(), isValid()
+ \sa resultAt(), resultCount(), takeResult()
*/
-/*! \fn template <typename T> QList<T> QFuture<T>::results() const
+/*! \fn template <typename T> template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>> QList<T> QFuture<T>::results() const
Returns all results from the future. If the results are not immediately available,
- this function will block and wait for them to become available.
+ this function will block and wait for them to become available. Note that
+ \c results() returns a copy of the internally stored results. Getting all
+ results of a move-only type \c T is not supported at the moment. However you can
+ still iterate through the list of move-only results by using \l{STL-style iterators}
+ or read-only \l{Java-style iterators}.
- \note Calling results() leads to undefined behavior if isValid()
+ \note Calling \c results() leads to undefined behavior if isValid()
returns \c false for this QFuture.
- \sa result(), resultAt(), takeResult(), takeResults(), resultCount(), isValid()
+ \sa result(), resultAt(), takeResult(), resultCount(), isValid()
*/
-/*! \fn template <typename T> std::vector<T> QFuture<T>::takeResults()
+#if 0
+/*! \fn template <typename T> template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>> std::vector<T> QFuture<T>::takeResults()
If isValid() returns \c false, calling this function leads to undefined behavior.
takeResults() takes all results from the QFuture object and invalidates it
@@ -483,14 +461,16 @@
\sa takeResult(), result(), resultAt(), results(), resultCount(), isValid()
*/
+#endif
-/*! \fn template <typename T> std::vector<T> QFuture<T>::takeResult()
+/*! \fn template <typename T> template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>> std::vector<T> QFuture<T>::takeResult()
+
+ \since 6.0
Call this function only if isValid() returns \c true, otherwise
- the behavior is undefined. This function takes the first result from
- the QFuture object, for convenience when only one result is expected.
- If there are any other results, they are discarded after taking the
- first one (if such behavior is undesired, use takeResults() instead).
+ the behavior is undefined. This function takes (moves) the first result from
+ the QFuture object, when only one result is expected. If there are any other
+ results, they are discarded after taking the first one.
If the result is not immediately available, this function will block and
wait for the result to become available. The QFuture will try to use move
semantics if possible, and will fall back to copy construction if the type
@@ -501,20 +481,24 @@
objects (and potentially between different threads). takeResult() was introduced
to make QFuture also work with move-only types (like std::unique_ptr), so it
assumes that only one thread can move the results out of the future, and
- do it only once.
+ do it only once. Also note that taking the list of all results is not supported
+ at the moment. However you can still iterate through the list of move-only
+ results by using \l{STL-style iterators} or read-only \l{Java-style iterators}.
- \sa takeResults(), result(), results(), resultAt(), isValid()
+ \sa result(), results(), resultAt(), isValid()
*/
/*! \fn template <typename T> bool QFuture<T>::isValid() const
+ \since 6.0
+
Returns \c true if a result or results can be accessed or taken from this
QFuture object. Returns false after the result was taken from the future.
- \sa takeResults(), takeResult(), result(), results(), resultAt()
+ \sa takeResult(), result(), results(), resultAt()
*/
-/*! \fn template <typename T> QFuture<T>::const_iterator QFuture<T>::begin() const
+/*! \fn template<typename T> template<class U = T, typename = QtPrivate::EnableForNonVoid<U>> QFuture<T>::const_iterator QFuture<T>::begin() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first result in the
future.
@@ -522,7 +506,7 @@
\sa constBegin(), end()
*/
-/*! \fn template <typename T> QFuture<T>::const_iterator QFuture<T>::end() const
+/*! \fn template<typename T> template<class U = T, typename = QtPrivate::EnableForNonVoid<U>> QFuture<T>::const_iterator QFuture<T>::end() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary result
after the last result in the future.
@@ -530,7 +514,7 @@
\sa begin(), constEnd()
*/
-/*! \fn template <typename T> QFuture<T>::const_iterator QFuture<T>::constBegin() const
+/*! \fn template<typename T> template<class U = T, typename = QtPrivate::EnableForNonVoid<U>> QFuture<T>::const_iterator QFuture<T>::constBegin() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first result in the
future.
@@ -538,7 +522,7 @@
\sa begin(), constEnd()
*/
-/*! \fn template <typename T> QFuture<T>::const_iterator QFuture<T>::constEnd() const
+/*! \fn template<typename T> template<class U = T, typename = QtPrivate::EnableForNonVoid<U>> QFuture<T>::const_iterator QFuture<T>::constEnd() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary result
after the last result in the future.
@@ -647,7 +631,7 @@
/*! \fn template <typename T> QFuture<T>::const_iterator &QFuture<T>::const_iterator::operator++()
- The prefix ++ operator (\c{++it}) advances the iterator to the next result
+ The prefix \c{++} operator (\c{++it}) advances the iterator to the next result
in the future and returns an iterator to the new current result.
Calling this function on QFuture<T>::constEnd() leads to undefined results.
@@ -659,14 +643,14 @@
\overload
- The postfix ++ operator (\c{it++}) advances the iterator to the next
+ The postfix \c{++} operator (\c{it++}) advances the iterator to the next
result in the future and returns an iterator to the previously current
result.
*/
/*! \fn template <typename T> QFuture<T>::const_iterator &QFuture<T>::const_iterator::operator--()
- The prefix -- operator (\c{--it}) makes the preceding result current and
+ The prefix \c{--} operator (\c{--it}) makes the preceding result current and
returns an iterator to the new current result.
Calling this function on QFuture<T>::constBegin() leads to undefined results.
@@ -678,7 +662,7 @@
\overload
- The postfix -- operator (\c{it--}) makes the preceding result current and
+ The postfix \c{--} operator (\c{it--}) makes the preceding result current and
returns an iterator to the previously current result.
*/
@@ -888,6 +872,7 @@
/*!
\namespace QtFuture
+ \inheaderfile QFuture
\inmodule QtCore
\brief Contains miscellaneous identifiers used by the QFuture class.
@@ -901,20 +886,57 @@
Represents execution policies for running a QFuture continuation.
- \value Sync The continuation will be launched in the same thread in
- which the parent has been executing.
+ \value Sync The continuation will be launched in the same thread that
+ fulfills the promise associated with the future to which the
+ continuation was attached, or if it has already finished, the
+ continuation will be invoked immediately, in the thread that
+ executes \c then().
- \value Async The continuation will be launched in in a separate thread taken from
+ \value Async The continuation will be launched in a separate thread taken from
the global QThreadPool.
- \value Inherit The continuation will inherit the launch policy of the parent or its
- thread pool, if it was using a custom one.
+ \value Inherit The continuation will inherit the launch policy or thread pool of
+ the future to which it is attached.
+
+ \c Sync is used as a default launch policy.
\sa QFuture::then(), QThreadPool::globalInstance()
*/
-/*! \fn template<class Sender, class Signal> static QFuture<ArgsType<Signal>> QtFuture::connect(Sender *sender, Signal signal)
+/*!
+ \class QtFuture::WhenAnyResult
+ \inmodule QtCore
+ \ingroup thread
+ \brief QtFuture::WhenAnyResult is used to represent the result of QtFuture::whenAny().
+ \since 6.3
+
+ The \c {QtFuture::WhenAnyResult<T>} struct is used for packaging the copy and
+ the index of the first completed \c QFuture<T> in the sequence of futures
+ packaging type \c T that are passed to QtFuture::whenAny().
+
+ \sa QFuture, QtFuture::whenAny()
+*/
+
+/*!
+ \variable QtFuture::WhenAnyResult::index
+
+ The field contains the index of the first completed QFuture in the sequence
+ of futures passed to whenAny(). It has type \c qsizetype.
+
+ \sa QtFuture::whenAny()
+*/
+
+/*!
+ \variable QtFuture::WhenAnyResult::future
+
+ The field contains the copy of the first completed QFuture that packages type
+ \c T, where \c T is the type packaged by the futures passed to whenAny().
+
+ \sa QtFuture::whenAny()
+*/
+
+/*! \fn template<class Sender, class Signal, typename = QtPrivate::EnableIfInvocable<Sender, Signal>> static QFuture<ArgsType<Signal>> QtFuture::connect(Sender *sender, Signal signal)
Creates and returns a QFuture which will become available when the \a sender emits
the \a signal. If the \a signal takes no arguments, a QFuture<void> is returned. If
@@ -953,23 +975,212 @@
\sa QFuture, QFuture::then()
*/
+/*! \fn template<typename T, typename = QtPrivate::EnableForNonVoid<T>> static QFuture<std::decay_t<T>> QtFuture::makeReadyFuture(T &&value)
+
+ \since 6.1
+ \overload
+ \deprecated [6.6] Use makeReadyValueFuture() instead.
+
+ Creates and returns a QFuture which already has a result \a value.
+ The returned QFuture has a type of std::decay_t<T>, where T is not void.
+
+ \code
+ auto f = QtFuture::makeReadyFuture(std::make_unique<int>(42));
+ ...
+ const int result = *f.takeResult(); // result == 42
+ \endcode
+
+ The method should be avoided because
+ it has an inconsistent set of overloads. From Qt 6.10 onwards, using it
+ in code will result in compiler warnings.
+
+ \sa QFuture, QtFuture::makeReadyVoidFuture(),
+ QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(),
+ QtFuture::makeExceptionalFuture()
+*/
+
+/*! \fn QFuture<void> QtFuture::makeReadyFuture()
+
+ \since 6.1
+ \overload
+ \deprecated [6.6] Use makeReadyVoidFuture() instead.
+
+ Creates and returns a void QFuture. Such QFuture can't store any result.
+ One can use it to query the state of the computation.
+ The returned QFuture will always be in the finished state.
+
+ \code
+ auto f = QtFuture::makeReadyFuture();
+ ...
+ const bool started = f.isStarted(); // started == true
+ const bool running = f.isRunning(); // running == false
+ const bool finished = f.isFinished(); // finished == true
+ \endcode
+
+ The method should be avoided because
+ it has an inconsistent set of overloads. From Qt 6.10 onwards, using it
+ in code will result in compiler warnings.
+
+ \sa QFuture, QFuture::isStarted(), QFuture::isRunning(),
+ QFuture::isFinished(), QtFuture::makeReadyVoidFuture(),
+ QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(),
+ QtFuture::makeExceptionalFuture()
+*/
+
+/*! \fn template<typename T> static QFuture<T> QtFuture::makeReadyFuture(const QList<T> &values)
+
+ \since 6.1
+ \overload
+ \deprecated [6.6] Use makeReadyRangeFuture() instead.
+
+ Creates and returns a QFuture which already has multiple results set from \a values.
+
+ \code
+ const QList<int> values { 1, 2, 3 };
+ auto f = QtFuture::makeReadyFuture(values);
+ ...
+ const int count = f.resultCount(); // count == 3
+ const auto results = f.results(); // results == { 1, 2, 3 }
+ \endcode
+
+ The method should be avoided because
+ it has an inconsistent set of overloads. From Qt 6.10 onwards, using it
+ in code will result in compiler warnings.
+
+ \sa QFuture, QtFuture::makeReadyVoidFuture(),
+ QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(),
+ QtFuture::makeExceptionalFuture()
+*/
+
+/*! \fn template<typename T> static QFuture<std::decay_t<T>> QtFuture::makeReadyValueFuture(T &&value)
+
+ \since 6.6
+
+ Creates and returns a QFuture which already has a result \a value.
+ The returned QFuture has a type of std::decay_t<T>, where T is not void.
+ The returned QFuture will already be in the finished state.
+
+ \snippet code/src_corelib_thread_qfuture.cpp 35
+
+ \sa QFuture, QtFuture::makeReadyRangeFuture(),
+ QtFuture::makeReadyVoidFuture(), QtFuture::makeExceptionalFuture()
+*/
+
+/*! \fn QFuture<void> QtFuture::makeReadyVoidFuture()
+
+ \since 6.6
+
+ Creates and returns a void QFuture. Such QFuture can't store any result.
+ One can use it to query the state of the computation.
+ The returned QFuture will already be in the finished state.
+
+ \snippet code/src_corelib_thread_qfuture.cpp 36
+
+ \sa QFuture, QFuture::isStarted(), QFuture::isRunning(),
+ QFuture::isFinished(), QtFuture::makeReadyValueFuture(),
+ QtFuture::makeReadyRangeFuture(), QtFuture::makeExceptionalFuture()
+*/
+
+/*! \fn template<typename T> static QFuture<T> QtFuture::makeExceptionalFuture(const QException &exception)
+
+ \since 6.1
+
+ Creates and returns a QFuture which already has an exception \a exception.
+
+ \code
+ QException e;
+ auto f = QtFuture::makeExceptionalFuture<int>(e);
+ ...
+ try {
+ f.result(); // throws QException
+ } catch (QException &) {
+ // handle exception here
+ }
+ \endcode
+
+ \sa QFuture, QException, QtFuture::makeReadyVoidFuture(),
+ QtFuture::makeReadyValueFuture()
+*/
+
+/*! \fn template<typename T> static QFuture<T> QtFuture::makeExceptionalFuture(std::exception_ptr exception)
+
+ \since 6.1
+ \overload
+
+ Creates and returns a QFuture which already has an exception \a exception.
+
+ \code
+ struct TestException
+ {
+ };
+ ...
+ auto exception = std::make_exception_ptr(TestException());
+ auto f = QtFuture::makeExceptionalFuture<int>(exception);
+ ...
+ try {
+ f.result(); // throws TestException
+ } catch (TestException &) {
+ // handle exception here
+ }
+ \endcode
+
+ \sa QFuture, QException, QtFuture::makeReadyVoidFuture(),
+ QtFuture::makeReadyValueFuture()
+*/
+
+/*! \fn template<typename Container, QtFuture::if_container_with_input_iterators<Container>> static QFuture<QtFuture::ContainedType<Container>> QtFuture::makeReadyRangeFuture(Container &&container)
+
+ \since 6.6
+ \overload
+
+ Takes an input container \a container and returns a QFuture with multiple
+ results of type \c ContainedType initialized from the values of the
+ \a container.
+
+ \note This overload only participates in overload resolution if the
+ \c Container has input iterators.
+
+ \snippet code/src_corelib_thread_qfuture.cpp 32
+ \dots
+ \snippet code/src_corelib_thread_qfuture.cpp 34
+
+ \sa QFuture, QtFuture::makeReadyVoidFuture(),
+ QtFuture::makeReadyValueFuture(), QtFuture::makeExceptionalFuture()
+*/
+
+/*! \fn template<typename ValueType> static QFuture<ValueType> QtFuture::makeReadyRangeFuture(std::initializer_list<ValueType> values)
+
+ \since 6.6
+ \overload
+
+ Returns a QFuture with multiple results of type \c ValueType initialized
+ from the input initializer list \a values.
+
+ \snippet code/src_corelib_thread_qfuture.cpp 33
+ \dots
+ \snippet code/src_corelib_thread_qfuture.cpp 34
+
+ \sa QFuture, QtFuture::makeReadyVoidFuture(),
+ QtFuture::makeReadyValueFuture(), QtFuture::makeExceptionalFuture()
+*/
+
/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(Function &&function)
\since 6.0
\overload
Attaches a continuation to this future, allowing to chain multiple asynchronous
- computations if desired. When the asynchronous computation represented by this
- future finishes, \a function will be invoked in the same thread in which this
- future has been running. A new QFuture representing the result of the continuation
- is returned.
+ computations if desired, using the \l {QtFuture::Launch}{Sync} policy.
+ \a function is a callable that takes an argument of the type packaged by this
+ future if this has a result (is not a QFuture<void>). Otherwise it takes no
+ arguments. This method returns a new QFuture that packages a value of the type
+ returned by \a function. The returned future will be in an uninitialized state
+ until the attached continuation is invoked, or until this future fails or is
+ canceled.
\note Use other overloads of this method if you need to launch the continuation in
a separate thread.
- If this future has a result (is not a QFuture<void>), \a function takes the result
- of this future as its argument.
-
You can chain multiple operations like this:
\code
@@ -999,7 +1210,7 @@
In this case the whole chain of continuations will be interrupted.
- \note If the parent future gets canceled, its continuations will
+ \note If this future gets canceled, the continuations attached to it will
also be canceled.
\sa onFailed(), onCanceled()
@@ -1015,10 +1226,12 @@
finishes, \a function will be invoked according to the given launch \a policy.
A new QFuture representing the result of the continuation is returned.
- Depending on the \a policy, continuation will run in the same thread as the parent,
- run in a new thread, or inherit the launch policy and thread pool of the parent.
+ Depending on the \a policy, continuation will be invoked in the same thread as
+ this future, in a new thread, or will inherit the launch policy and thread pool of
+ this future. If no launch policy is specified (see the overload taking only a callable),
+ the \c Sync policy will be used.
- In the following example both continuations will run in a new thread (but in
+ In the following example both continuations will be invoked in a new thread (but in
the same one).
\code
@@ -1026,8 +1239,8 @@
future.then(QtFuture::Launch::Async, [](int res){ ... }).then([](int res2){ ... });
\endcode
- In the following example both continuations will run in new threads using the same
- thread pool.
+ In the following example both continuations will be invoked in new threads using the
+ same thread pool.
\code
QFuture<int> future = ...;
@@ -1035,6 +1248,8 @@
.then(QtFuture::Launch::Inherit, [](int res2){ ... });
\endcode
+ See the documentation of the other overload for more details about \a function.
+
\sa onFailed(), onCanceled()
*/
@@ -1045,24 +1260,86 @@
Attaches a continuation to this future, allowing to chain multiple asynchronous
computations if desired. When the asynchronous computation represented by this
- future finishes, \a function will be invoked in a separate thread taken from the
- QThreadPool \a pool.
+ future finishes, \a function will be scheduled on \a pool.
\sa onFailed(), onCanceled()
*/
-/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onFailed(Function &&handler)
+/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QObject *context, Function &&function)
+
+ \since 6.1
+ \overload
+
+ Attaches a continuation to this future, allowing to chain multiple asynchronous
+ computations if desired. When the asynchronous computation represented by this
+ future finishes, \a function will be invoked in the thread of the \a context object.
+ This can be useful if the continuation needs to be invoked in a specific thread.
+ For example:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 17
+
+ The continuation attached into QtConcurrent::run updates the UI elements and cannot
+ be invoked from a non-gui thread. So \c this is provided as a context to \c .then(),
+ to make sure that it will be invoked in the main thread.
+
+ The following continuations will be also invoked from the same context,
+ unless a different context or launch policy is specified:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 18
+
+ This is because by default \c .then() is invoked from the same thread as the
+ previous one.
+
+ But note that if the continuation is attached after this future has already finished,
+ it will be invoked immediately, in the thread that executes \c then():
+
+ \snippet code/src_corelib_thread_qfuture.cpp 20
+
+ In the above example if \c cachedResultsReady is \c true, and a ready future is
+ returned, it is possible that the first \c .then() finishes before the second one
+ is attached. In this case it will be resolved in the current thread. Therefore, when
+ in doubt, pass the context explicitly.
+
+ \target context_lifetime
+ If the \a context is destroyed before the chain has finished, the future is canceled.
+ This implies that a cancellation handler might be invoked when the \a context is not valid
+ anymore. To guard against this, capture the \a context as a QPointer:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 37
+
+ When the context object is destroyed, cancellation happens immediately. Previous futures in the
+ chain are \e {not} cancelled and keep running until they are finished.
+
+ \note When calling this method, it should be guaranteed that the \a context stays alive
+ during setup of the chain.
+
+ \sa onFailed(), onCanceled()
+*/
+
+/*! \fn template<class T> template<class Function, typename = std::enable_if_t<!QtPrivate::ArgResolver<Function>::HasExtraArgs>> QFuture<T> QFuture<T>::onFailed(Function &&handler)
\since 6.0
- Attaches a failure handler to this future, to handle any exceptions that may
- have been generated. Returns a QFuture of the parent type. The handler will
- be invoked only in case of an exception, in the same thread as the parent
- future has been running. \a handler is a callable which takes either no argument
- or one argument, to filter by specific error types similar to
+ Attaches a failure handler to this future, to handle any exceptions. The
+ returned future behaves exactly as this future (has the same state and result)
+ unless this future fails with an exception.
+
+ The \a handler is a callable which takes either no argument or one argument, to
+ filter by specific error types, similar to the
\l {https://en.cppreference.com/w/cpp/language/try_catch} {catch} statement.
+ It returns a value of the type packaged by this future. After the failure, the
+ returned future packages the value returned by \a handler.
- For example:
+ The handler will only be invoked if an exception is raised. If the exception
+ is raised after this handler is attached, the handler is executed in the thread
+ that reports the future as finished as a result of the exception. If the handler
+ is attached after this future has already failed, it will be invoked immediately,
+ in the thread that executes \c onFailed(). Therefore, the handler cannot always
+ make assumptions about which thread it will be run on. Use the overload that
+ takes a context object if you want to control which thread the handler is
+ invoked on.
+
+ The example below demonstrates how to attach a failure handler:
\snippet code/src_corelib_thread_qfuture.cpp 7
@@ -1082,13 +1359,247 @@
\sa then(), onCanceled()
*/
-/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onCanceled(Function &&handler)
+/*! \fn template<class T> template<class Function, typename = std::enable_if_t<!QtPrivate::ArgResolver<Function>::HasExtraArgs>> QFuture<T> QFuture<T>::onFailed(QObject *context, Function &&handler)
+
+ \since 6.1
+ \overload
+
+ Attaches a failure handler to this future, to handle any exceptions that the future
+ raises, or that it has already raised. Returns a QFuture of the same type as this
+ future. The handler will be invoked only in case of an exception, in the thread of
+ the \a context object. This can be useful if the failure needs to be handled in a
+ specific thread. For example:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 19
+
+ The failure handler attached into QtConcurrent::run updates the UI elements and cannot
+ be invoked from a non-gui thread. So \c this is provided as a context to \c .onFailed(),
+ to make sure that it will be invoked in the main thread.
+
+ If the \a context is destroyed before the chain has finished, the future is canceled.
+ See \l {context_lifetime}{then()} for details.
+
+ \note When calling this method, it should be guaranteed that the \a context stays alive
+ during setup of the chain.
+
+ See the documentation of the other overload for more details about \a handler.
+
+ \sa then(), onCanceled()
+*/
+
+/*! \fn template<class T> template<class Function, typename = std::enable_if_t<std::is_invocable_r_v<T, Function>>> QFuture<T> QFuture<T>::onCanceled(Function &&handler)
\since 6.0
+ Attaches a cancellation \a handler to this future. The returned future
+ behaves exactly as this future (has the same state and result) unless
+ this future is cancelled. The \a handler is a callable which takes no
+ arguments and returns a value of the type packaged by this future. After
+ cancellation, the returned future packages the value returned by \a handler.
+
+ If attached before the cancellation, \a handler will be invoked in the same
+ thread that reports the future as finished after the cancellation. If the
+ handler is attached after this future has already been canceled, it will be
+ invoked immediately in the thread that executes \c onCanceled(). Therefore,
+ the handler cannot always make assumptions about which thread it will be run
+ on. Use the overload that takes a context object if you want to control
+ which thread the handler is invoked on.
+
+ The example below demonstrates how to attach a cancellation handler:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 21
+
+ If \c testFuture is canceled, \c {Block 3} will be called and the
+ \c resultFuture will have \c -1 as its result. Unlike \c testFuture, it won't
+ be in a \c Canceled state. This means that you can get its result, attach
+ countinuations to it, and so on.
+
+ Also note that you can cancel the chain of continuations while they are
+ executing via the future that started the chain. Let's say \c testFuture.cancel()
+ was called while \c {Block 1} is already executing. The next continuation will
+ detect that cancellation was requested, so \c {Block 2} will be skipped, and
+ the cancellation handler (\c {Block 3}) will be called.
+
+ \note This method returns a new \c QFuture representing the result of the
+ continuation chain. Canceling the resulting \c QFuture itself won't invoke the
+ cancellation handler in the chain that lead to it. This means that if you call
+ \c resultFuture.cancel(), \c {Block 3} won't be called: because \c resultFuture is
+ the future that results from attaching the cancellation handler to \c testFuture,
+ no cancellation handlers have been attached to \c resultFuture itself. Only
+ cancellation of \c testFuture or the futures returned by continuations attached
+ before the \c onCancelled() call can trigger \c{Block 3}.
+
+ \sa then(), onFailed()
+*/
+
+/*! \fn template<class T> template<class Function, typename = std::enable_if_t<std::is_invocable_r_v<T, Function>>> QFuture<T> QFuture<T>::onCanceled(QObject *context, Function &&handler)
+
+ \since 6.1
+ \overload
+
Attaches a cancellation \a handler to this future, to be called when the future is
canceled. The \a handler is a callable which doesn't take any arguments. It will be
- invoked in the same thread in which this future has been running.
+ invoked in the thread of the \a context object. This can be useful if the cancellation
+ needs to be handled in a specific thread.
+
+ If the \a context is destroyed before the chain has finished, the future is canceled.
+ See \l {context_lifetime}{then()} for details.
+
+ \note When calling this method, it should be guaranteed that the \a context stays alive
+ during setup of the chain.
+
+ See the documentation of the other overload for more details about \a handler.
\sa then(), onFailed()
*/
+
+/*! \fn template<class T> template<class U> QFuture<U> QFuture<T>::unwrap()
+
+ \since 6.4
+
+ Unwraps the inner future from this \c QFuture<T>, where \c T is a future
+ of type \c QFuture<U>, i.e. this future has type of \c QFuture<QFuture<U>>.
+ For example:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 28
+
+ \c unwrappedFuture will be fulfilled as soon as the inner future nested
+ inside the \c outerFuture is fulfilled, with the same result or exception
+ and in the same thread that reports the inner future as finished. If the
+ inner future is canceled, \c unwrappedFuture will also be canceled.
+
+ This is especially useful when chaining multiple computations, and one of
+ them returns a \c QFuture as its result type. For example, let's say we
+ want to download multiple images from an URL, scale the images, and reduce
+ them to a single image using QtConcurrent::mappedReduced(). We could write
+ something like:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 29
+
+ Here \c QtConcurrent::mappedReduced() returns a \c QFuture<QImage>, so
+ \c .then(processImages) returns a \c QFuture<QFuture<QImage>>. Since
+ \c show() takes a \c QImage as argument, the result of \c .then(processImages)
+ can't be passed to it directly. We need to call \c .unwrap(), that will
+ get the result of the inner future when it's ready and pass it to the next
+ continuation.
+
+ In case of multiple nesting, \c .unwrap() goes down to the innermost level:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 30
+*/
+
+/*! \fn template<typename OutputSequence, typename InputIt> QFuture<OutputSequence> QtFuture::whenAll(InputIt first, InputIt last)
+
+ \since 6.3
+
+ Returns a new QFuture that succeeds when all futures from \a first to \a last
+ complete. \a first and \a last are iterators to a sequence of futures packaging
+ type \c T. \c OutputSequence is a sequence containing all completed futures
+ from \a first to \a last, appearing in the same order as in the input. If the
+ type of \c OutputSequence is not specified, the resulting futures will be
+ returned in a \c QList of \c QFuture<T>. For example:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 22
+
+ \note The output sequence must support random access and the \c resize()
+ operation.
+
+ If \c first equals \c last, this function returns a ready QFuture that
+ contains an empty \c OutputSequence.
+
+//! [whenAll]
+ The returned future always completes successfully after all the specified
+ futures complete. It doesn't matter if any of these futures completes with
+ error or is canceled. You can use \c .then() to process the completed futures
+ after the future returned by \c whenAll() succeeds:
+//! [whenAll]
+
+ \snippet code/src_corelib_thread_qfuture.cpp 23
+
+//! [whenAll-note]
+ \note If the input futures complete on different threads, the future returned
+ by this method will complete in the thread that the last future completes in.
+ Therefore, the continuations attached to the future returned by \c whenAll()
+ cannot always make assumptions about which thread they will be run on. Use the
+ overload of \c .then() that takes a context object if you want to control which
+ thread the continuations are invoked on.
+//! [whenAll-note]
+*/
+
+/*! \fn template<typename OutputSequence, typename... Futures> QFuture<OutputSequence> QtFuture::whenAll(Futures &&... futures)
+
+ \since 6.3
+
+ Returns a new QFuture that succeeds when all \a futures packaging arbitrary
+ types complete. \c OutputSequence is a sequence of completed futures. The type
+ of its entries is \c std::variant<Futures...>. For each \c QFuture<T> passed to
+ \c whenAll(), the entry at the corresponding position in \c OutputSequence
+ will be a \c std::variant holding that \c QFuture<T>, in its completed state.
+ If the type of \c OutputSequence is not specified, the resulting futures will
+ be returned in a QList of \c std::variant<Futures...>. For example:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 24
+
+ \note The output sequence should support random access and the \c resize()
+ operation.
+
+ \include qfuture.qdoc whenAll
+
+ \snippet code/src_corelib_thread_qfuture.cpp 25
+
+ \include qfuture.qdoc whenAll-note
+*/
+
+/*! \fn template<typename T, typename InputIt> QFuture<QtFuture::WhenAnyResult<T>> QtFuture::whenAny(InputIt first, InputIt last)
+
+ \since 6.3
+
+ Returns a new QFuture that succeeds when any of the futures from \a first to
+ \a last completes. \a first and \a last are iterators to a sequence of futures
+ packaging type \c T. The returned future packages a value of type
+ \c {QtFuture::WhenAnyResult<T>} which in turn packages the index of the
+ first completed \c QFuture and the \c QFuture itself. If \a first equals \a last,
+ this function returns a ready \c QFuture that has \c -1 for the \c index field in
+ the QtFuture::WhenAnyResult struct and a default-constructed \c QFuture<T> for
+ the \c future field. Note that a default-constructed QFuture is a completed
+ future in a cancelled state.
+
+//! [whenAny]
+ The returned future always completes successfully after the first future
+ from the specified futures completes. It doesn't matter if the first future
+ completes with error or is canceled. You can use \c .then() to process the
+ result after the future returned by \c whenAny() succeeds:
+//! [whenAny]
+
+ \snippet code/src_corelib_thread_qfuture.cpp 26
+
+//! [whenAny-note]
+ \note If the input futures complete on different threads, the future returned
+ by this method will complete in the thread that the first future completes in.
+ Therefore, the continuations attached to the future returned by \c whenAny()
+ cannot always make assumptions about which thread they will be run on. Use the
+ overload of \c .then() that takes a context object if you want to control which
+ thread the continuations are invoked on.
+//! [whenAny-note]
+
+ \sa QtFuture::WhenAnyResult
+*/
+
+/*! \fn template<typename... Futures> QFuture<std::variant<std::decay_t<Futures>...>> QtFuture::whenAny(Futures &&... futures)
+
+ \since 6.3
+
+ Returns a new QFuture that succeeds when any of the \a futures completes.
+ \a futures can package arbitrary types. The returned future packages the
+ value of type \c std::variant<Futures...> which in turn packages the first
+ completed QFuture from \a futures. You can use
+ \l {https://en.cppreference.com/w/cpp/utility/variant/index} {std::variant::index()}
+ to find out the index of the future in the sequence of \a futures that
+ finished first.
+
+ \include qfuture.qdoc whenAny
+
+ \snippet code/src_corelib_thread_qfuture.cpp 27
+
+ \include qfuture.qdoc whenAny-note
+*/
diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h
index 883f8d4f09..62de5c0660 100644
--- a/src/corelib/thread/qfuture_impl.h
+++ b/src/corelib/thread/qfuture_impl.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 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) 2020 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 QFUTURE_H
#error Do not include qfuture_impl.h directly
@@ -49,6 +13,10 @@
#include <QtCore/qglobal.h>
#include <QtCore/qfutureinterface.h>
#include <QtCore/qthreadpool.h>
+#include <QtCore/qexception.h>
+#include <QtCore/qpromise.h>
+
+#include <memory>
QT_BEGIN_NAMESPACE
@@ -59,17 +27,27 @@ template<class T>
class QFuture;
template<class T>
class QFutureInterface;
+template<class T>
+class QPromise;
namespace QtFuture {
+
enum class Launch { Sync, Async, Inherit };
+
+template<class T>
+struct WhenAnyResult
+{
+ qsizetype index = -1;
+ QFuture<T> future;
+};
+
+// Deduction guide
+template<class T>
+WhenAnyResult(qsizetype, const QFuture<T> &) -> WhenAnyResult<T>;
}
namespace QtPrivate {
-template<class T, class U>
-using EnableIfSameOrConvertible = std::enable_if_t<std::is_same_v<T, U>
- || std::is_convertible_v<T, U>>;
-
template<class T>
using EnableForVoid = std::enable_if_t<std::is_same_v<T, void>>;
@@ -113,7 +91,48 @@ struct ResultTypeHelper<
using ResultType = std::invoke_result_t<std::decay_t<F>>;
};
+// Helpers to remove QPrivateSignal argument from the list of arguments
+
+template<class T, class Enable = void>
+inline constexpr bool IsPrivateSignalArg = false;
+
+template<class T>
+inline constexpr bool IsPrivateSignalArg<T, typename std::enable_if_t<
+ // finds injected-class-name, the 'class' avoids falling into the rules of [class.qual]/2:
+ std::is_class_v<class T::QPrivateSignal>
+ >> = true;
+
+template<class Tuple, std::size_t... I>
+auto cutTuple(Tuple &&t, std::index_sequence<I...>)
+{
+ return std::make_tuple(std::get<I>(t)...);
+}
+
+template<class Arg, class... Args>
+auto createTuple(Arg &&arg, Args &&... args)
+{
+ using TupleType = std::tuple<std::decay_t<Arg>, std::decay_t<Args>...>;
+ constexpr auto Size = sizeof...(Args); // One less than the size of all arguments
+ if constexpr (QtPrivate::IsPrivateSignalArg<std::tuple_element_t<Size, TupleType>>) {
+ if constexpr (Size == 1) {
+ return std::forward<Arg>(arg);
+ } else {
+ return cutTuple(std::make_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...),
+ std::make_index_sequence<Size>());
+ }
+ } else {
+ return std::make_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+}
+
// Helpers to resolve argument types of callables.
+
+template<class Arg, class... Args>
+using FilterLastPrivateSignalArg =
+ std::conditional_t<(sizeof...(Args) > 0),
+ std::invoke_result_t<decltype(createTuple<Arg, Args...>), Arg, Args...>,
+ std::conditional_t<IsPrivateSignalArg<Arg>, void, Arg>>;
+
template<typename...>
struct ArgsType;
@@ -121,19 +140,34 @@ template<typename Arg, typename... Args>
struct ArgsType<Arg, Args...>
{
using First = Arg;
+ using PromiseType = void;
+ using IsPromise = std::false_type;
static const bool HasExtraArgs = (sizeof...(Args) > 0);
- using AllArgs =
- std::conditional_t<HasExtraArgs, std::tuple<std::decay_t<Arg>, std::decay_t<Args>...>,
- std::decay_t<Arg>>;
+ using AllArgs = FilterLastPrivateSignalArg<std::decay_t<Arg>, std::decay_t<Args>...>;
template<class Class, class Callable>
static const bool CanInvokeWithArgs = std::is_invocable_v<Callable, Class, Arg, Args...>;
};
+template<typename Arg, typename... Args>
+struct ArgsType<QPromise<Arg> &, Args...>
+{
+ using First = QPromise<Arg> &;
+ using PromiseType = Arg;
+ using IsPromise = std::true_type;
+ static const bool HasExtraArgs = (sizeof...(Args) > 0);
+ using AllArgs = FilterLastPrivateSignalArg<QPromise<Arg>, std::decay_t<Args>...>;
+
+ template<class Class, class Callable>
+ static const bool CanInvokeWithArgs = std::is_invocable_v<Callable, Class, QPromise<Arg> &, Args...>;
+};
+
template<>
struct ArgsType<>
{
using First = void;
+ using PromiseType = void;
+ using IsPromise = std::false_type;
static const bool HasExtraArgs = false;
using AllArgs = void;
@@ -146,6 +180,11 @@ struct ArgResolver : ArgResolver<decltype(&std::decay_t<F>::operator())>
{
};
+template<typename F>
+struct ArgResolver<std::reference_wrapper<F>> : ArgResolver<decltype(&std::decay_t<F>::operator())>
+{
+};
+
template<typename R, typename... Args>
struct ArgResolver<R(Args...)> : public ArgsType<Args...>
{
@@ -157,6 +196,16 @@ struct ArgResolver<R (*)(Args...)> : public ArgsType<Args...>
};
template<typename R, typename... Args>
+struct ArgResolver<R (*&)(Args...)> : public ArgsType<Args...>
+{
+};
+
+template<typename R, typename... Args>
+struct ArgResolver<R (* const)(Args...)> : public ArgsType<Args...>
+{
+};
+
+template<typename R, typename... Args>
struct ArgResolver<R (&)(Args...)> : public ArgsType<Args...>
{
};
@@ -181,39 +230,84 @@ struct ArgResolver<R (Class::*)(Args...) const noexcept> : public ArgsType<Args.
{
};
+template<typename Class, typename R, typename... Args>
+struct ArgResolver<R (Class::* const)(Args...) const> : public ArgsType<Args...>
+{
+};
+
+template<typename Class, typename R, typename... Args>
+struct ArgResolver<R (Class::* const)(Args...) const noexcept> : public ArgsType<Args...>
+{
+};
+
template<class Class, class Callable>
using EnableIfInvocable = std::enable_if_t<
QtPrivate::ArgResolver<Callable>::template CanInvokeWithArgs<Class, Callable>>;
-template<class>
-struct isTuple : std::false_type
+template<class T>
+inline constexpr bool isQFutureV = false;
+
+template<class T>
+inline constexpr bool isQFutureV<QFuture<T>> = true;
+
+template<class T>
+using isQFuture = std::bool_constant<isQFutureV<T>>;
+
+template<class T>
+struct Future
{
};
-template<class... T>
-struct isTuple<std::tuple<T...>> : std::true_type
+
+template<class T>
+struct Future<QFuture<T>>
{
+ using type = T;
};
-template<class T>
-inline constexpr bool isTupleV = isTuple<T>::value;
+
+template<class... Args>
+using NotEmpty = std::bool_constant<(sizeof...(Args) > 0)>;
+
+template<class Sequence>
+using IsRandomAccessible =
+ std::is_convertible<typename std::iterator_traits<std::decay_t<decltype(
+ std::begin(std::declval<Sequence>()))>>::iterator_category,
+ std::random_access_iterator_tag>;
+
+template<class Sequence>
+using HasInputIterator =
+ std::is_convertible<typename std::iterator_traits<std::decay_t<decltype(
+ std::begin(std::declval<Sequence>()))>>::iterator_category,
+ std::input_iterator_tag>;
+
+template<class Iterator>
+using IsForwardIterable =
+ std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category,
+ std::forward_iterator_tag>;
template<typename Function, typename ResultType, typename ParentResultType>
class Continuation
{
public:
- Continuation(Function &&func, const QFuture<ParentResultType> &f,
- const QFutureInterface<ResultType> &p)
- : promise(p), parentFuture(f), function(std::forward<Function>(func))
+ template<typename F = Function>
+ Continuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p)
+ : promise(std::move(p)), parentFuture(f), function(std::forward<F>(func))
{
}
virtual ~Continuation() = default;
bool execute();
- static void create(Function &&func, QFuture<ParentResultType> *f,
- QFutureInterface<ResultType> &p, QtFuture::Launch policy);
+ template<typename F = Function>
+ static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi,
+ QtFuture::Launch policy);
+
+ template<typename F = Function>
+ static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi,
+ QThreadPool *pool);
- static void create(Function &&func, QFuture<ParentResultType> *f,
- QFutureInterface<ResultType> &p, QThreadPool *pool);
+ template<typename F = Function>
+ static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi,
+ QObject *context);
private:
void fulfillPromiseWithResult();
@@ -229,7 +323,7 @@ protected:
void runFunction();
protected:
- QFutureInterface<ResultType> promise;
+ QPromise<ResultType> promise;
QFuture<ParentResultType> parentFuture;
Function function;
};
@@ -238,9 +332,10 @@ template<typename Function, typename ResultType, typename ParentResultType>
class SyncContinuation final : public Continuation<Function, ResultType, ParentResultType>
{
public:
- SyncContinuation(Function &&func, const QFuture<ParentResultType> &f,
- const QFutureInterface<ResultType> &p)
- : Continuation<Function, ResultType, ParentResultType>(std::forward<Function>(func), f, p)
+ template<typename F = Function>
+ SyncContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p)
+ : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f,
+ std::move(p))
{
}
@@ -255,12 +350,13 @@ class AsyncContinuation final : public QRunnable,
public Continuation<Function, ResultType, ParentResultType>
{
public:
- AsyncContinuation(Function &&func, const QFuture<ParentResultType> &f,
- const QFutureInterface<ResultType> &p, QThreadPool *pool = nullptr)
- : Continuation<Function, ResultType, ParentResultType>(std::forward<Function>(func), f, p),
+ template<typename F = Function>
+ AsyncContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p,
+ QThreadPool *pool = nullptr)
+ : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f,
+ std::move(p)),
threadPool(pool)
{
- this->promise.setRunnable(this);
}
~AsyncContinuation() override = default;
@@ -287,12 +383,17 @@ template<class Function, class ResultType>
class FailureHandler
{
public:
- static void create(Function &&function, QFuture<ResultType> *future,
- const QFutureInterface<ResultType> &promise);
+ template<typename F = Function>
+ static void create(F &&function, QFuture<ResultType> *future,
+ const QFutureInterface<ResultType> &fi);
- FailureHandler(Function &&func, const QFuture<ResultType> &f,
- const QFutureInterface<ResultType> &p)
- : promise(p), parentFuture(f), handler(std::forward<Function>(func))
+ template<typename F = Function>
+ static void create(F &&function, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi,
+ QObject *context);
+
+ template<typename F = Function>
+ FailureHandler(F &&func, const QFuture<ResultType> &f, QPromise<ResultType> &&p)
+ : promise(std::move(p)), parentFuture(f), handler(std::forward<F>(func))
{
}
@@ -305,7 +406,7 @@ private:
void handleAllExceptions();
private:
- QFutureInterface<ResultType> promise;
+ QPromise<ResultType> promise;
QFuture<ResultType> parentFuture;
Function handler;
};
@@ -315,7 +416,7 @@ private:
template<typename Function, typename ResultType, typename ParentResultType>
void Continuation<Function, ResultType, ParentResultType>::runFunction()
{
- promise.reportStarted();
+ promise.start();
Q_ASSERT(parentFuture.isFinished());
@@ -329,7 +430,7 @@ void Continuation<Function, ResultType, ParentResultType>::runFunction()
fulfillPromiseWithResult();
} else {
// This assert normally should never fail, this is to make sure
- // that nothing unexpected happend.
+ // that nothing unexpected happened.
static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>,
"The continuation is not invocable with the provided arguments");
fulfillPromise(parentFuture);
@@ -344,7 +445,7 @@ void Continuation<Function, ResultType, ParentResultType>::runFunction()
fulfillVoidPromise();
} else {
// This assert normally should never fail, this is to make sure
- // that nothing unexpected happend.
+ // that nothing unexpected happened.
static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>,
"The continuation is not invocable with the provided arguments");
function(parentFuture);
@@ -352,10 +453,10 @@ void Continuation<Function, ResultType, ParentResultType>::runFunction()
}
#ifndef QT_NO_EXCEPTIONS
} catch (...) {
- promise.reportException(std::current_exception());
+ promise.setException(std::current_exception());
}
#endif
- promise.reportFinished();
+ promise.finish();
}
template<typename Function, typename ResultType, typename ParentResultType>
@@ -363,25 +464,25 @@ bool Continuation<Function, ResultType, ParentResultType>::execute()
{
Q_ASSERT(parentFuture.isFinished());
- if (parentFuture.isCanceled()) {
+ if (parentFuture.d.isChainCanceled()) {
#ifndef QT_NO_EXCEPTIONS
- if (parentFuture.d.exceptionStore().hasException()) {
+ if (parentFuture.d.hasException()) {
// If the continuation doesn't take a QFuture argument, propagate the exception
// to the caller, by reporting it. If the continuation takes a QFuture argument,
// the user may want to catch the exception inside the continuation, to not
// interrupt the continuation chain, so don't report anything yet.
if constexpr (!std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) {
- promise.reportStarted();
- promise.reportException(parentFuture.d.exceptionStore().exception());
- promise.reportFinished();
+ promise.start();
+ promise.setException(parentFuture.d.exceptionStore().exception());
+ promise.finish();
return false;
}
} else
#endif
{
- promise.reportStarted();
- promise.reportCanceled();
- promise.reportFinished();
+ promise.start();
+ promise.future().cancel();
+ promise.finish();
return false;
}
}
@@ -390,10 +491,30 @@ bool Continuation<Function, ResultType, ParentResultType>::execute()
return true;
}
+// Workaround for keeping move-only lambdas inside std::function
+template<class Function>
+struct ContinuationWrapper
+{
+ ContinuationWrapper(Function &&f) : function(std::move(f)) { }
+ ContinuationWrapper(const ContinuationWrapper &other)
+ : function(std::move(const_cast<ContinuationWrapper &>(other).function))
+ {
+ Q_ASSERT_X(false, "QFuture", "Continuation shouldn't be copied");
+ }
+ ContinuationWrapper(ContinuationWrapper &&other) = default;
+ ContinuationWrapper &operator=(ContinuationWrapper &&) = default;
+
+ void operator()(const QFutureInterfaceBase &parentData) { function(parentData); }
+
+private:
+ Function function;
+};
+
template<typename Function, typename ResultType, typename ParentResultType>
-void Continuation<Function, ResultType, ParentResultType>::create(Function &&func,
+template<typename F>
+void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
QFuture<ParentResultType> *f,
- QFutureInterface<ResultType> &p,
+ QFutureInterface<ResultType> &fi,
QtFuture::Launch policy)
{
Q_ASSERT(f);
@@ -407,22 +528,26 @@ void Continuation<Function, ResultType, ParentResultType>::create(Function &&fun
// If the parent future was using a custom thread pool, inherit it as well.
if (launchAsync && f->d.threadPool()) {
pool = f->d.threadPool();
- p.setThreadPool(pool);
+ fi.setThreadPool(pool);
}
}
- Continuation<Function, ResultType, ParentResultType> *continuationJob = nullptr;
- if (launchAsync) {
- continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
- std::forward<Function>(func), *f, p, pool);
- } else {
- continuationJob = new SyncContinuation<Function, ResultType, ParentResultType>(
- std::forward<Function>(func), *f, p);
- }
-
- p.setLaunchAsync(launchAsync);
+ fi.setLaunchAsync(launchAsync);
+
+ auto continuation = [func = std::forward<F>(func), fi, promise_ = QPromise(fi), pool,
+ launchAsync](const QFutureInterfaceBase &parentData) mutable {
+ const auto parent = QFutureInterface<ParentResultType>(parentData).future();
+ Continuation<Function, ResultType, ParentResultType> *continuationJob = nullptr;
+ if (launchAsync) {
+ auto asyncJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
+ std::forward<Function>(func), parent, std::move(promise_), pool);
+ fi.setRunnable(asyncJob);
+ continuationJob = asyncJob;
+ } else {
+ continuationJob = new SyncContinuation<Function, ResultType, ParentResultType>(
+ std::forward<Function>(func), parent, std::move(promise_));
+ }
- auto continuation = [continuationJob, launchAsync]() mutable {
bool isLaunched = continuationJob->execute();
// If continuation is successfully launched, AsyncContinuation will be deleted
// by the QThreadPool which has started it. Synchronous continuation will be
@@ -432,24 +557,26 @@ void Continuation<Function, ResultType, ParentResultType>::create(Function &&fun
continuationJob = nullptr;
}
};
-
- f->d.setContinuation(std::move(continuation));
+ f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
}
template<typename Function, typename ResultType, typename ParentResultType>
-void Continuation<Function, ResultType, ParentResultType>::create(Function &&func,
+template<typename F>
+void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
QFuture<ParentResultType> *f,
- QFutureInterface<ResultType> &p,
+ QFutureInterface<ResultType> &fi,
QThreadPool *pool)
{
Q_ASSERT(f);
- auto continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
- std::forward<Function>(func), *f, p, pool);
- p.setLaunchAsync(true);
- p.setThreadPool(pool);
+ fi.setLaunchAsync(true);
+ fi.setThreadPool(pool);
- auto continuation = [continuationJob]() mutable {
+ auto continuation = [func = std::forward<F>(func), promise_ = QPromise(fi),
+ pool](const QFutureInterfaceBase &parentData) mutable {
+ const auto parent = QFutureInterface<ParentResultType>(parentData).future();
+ auto continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
+ std::forward<Function>(func), parent, std::move(promise_), pool);
bool isLaunched = continuationJob->execute();
// If continuation is successfully launched, AsyncContinuation will be deleted
// by the QThreadPool which has started it.
@@ -458,8 +585,39 @@ void Continuation<Function, ResultType, ParentResultType>::create(Function &&fun
continuationJob = nullptr;
}
};
+ f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
+}
+
+template <typename Continuation>
+void watchContinuation(const QObject *context, Continuation &&c, QFutureInterfaceBase &fi)
+{
+ using Prototype = typename QtPrivate::Callable<Continuation>::Function;
+ watchContinuationImpl(context,
+ QtPrivate::makeCallableObject<Prototype>(std::forward<Continuation>(c)),
+ fi);
+}
+
+template<typename Function, typename ResultType, typename ParentResultType>
+template<typename F>
+void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
+ QFuture<ParentResultType> *f,
+ QFutureInterface<ResultType> &fi,
+ QObject *context)
+{
+ Q_ASSERT(f);
+ Q_ASSERT(context);
+
+ // When the context object is destroyed, the signal-slot connection is broken and the
+ // continuation callback is destroyed. The promise that is created in the capture list is
+ // destroyed and, if it is not yet finished, cancelled.
+ auto continuation = [func = std::forward<F>(func), parent = *f,
+ promise_ = QPromise(fi)]() mutable {
+ SyncContinuation<Function, ResultType, ParentResultType> continuationJob(
+ std::forward<Function>(func), parent, std::move(promise_));
+ continuationJob.execute();
+ };
- f->d.setContinuation(continuation);
+ QtPrivate::watchContinuation(context, std::move(continuation), f->d);
}
template<typename Function, typename ResultType, typename ParentResultType>
@@ -493,51 +651,65 @@ template<typename Function, typename ResultType, typename ParentResultType>
template<class... Args>
void Continuation<Function, ResultType, ParentResultType>::fulfillPromise(Args &&... args)
{
- if constexpr (std::is_copy_constructible_v<ResultType>)
- promise.reportResult(std::invoke(function, std::forward<Args>(args)...));
- else
- promise.reportAndMoveResult(std::invoke(function, std::forward<Args>(args)...));
+ promise.addResult(std::invoke(function, std::forward<Args>(args)...));
}
template<class T>
-void fulfillPromise(QFutureInterface<T> &promise, QFuture<T> &future)
+void fulfillPromise(QPromise<T> &promise, QFuture<T> &future)
{
if constexpr (!std::is_void_v<T>) {
if constexpr (std::is_copy_constructible_v<T>)
- promise.reportResult(future.result());
+ promise.addResult(future.result());
else
- promise.reportAndMoveResult(future.takeResult());
+ promise.addResult(future.takeResult());
}
}
template<class T, class Function>
-void fulfillPromise(QFutureInterface<T> &promise, Function &&handler)
+void fulfillPromise(QPromise<T> &promise, Function &&handler)
{
if constexpr (std::is_void_v<T>)
handler();
- else if constexpr (std::is_copy_constructible_v<T>)
- promise.reportResult(handler());
else
- promise.reportAndMoveResult(handler());
+ promise.addResult(handler());
}
#ifndef QT_NO_EXCEPTIONS
template<class Function, class ResultType>
-void FailureHandler<Function, ResultType>::create(Function &&function, QFuture<ResultType> *future,
- const QFutureInterface<ResultType> &promise)
+template<class F>
+void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future,
+ const QFutureInterface<ResultType> &fi)
{
Q_ASSERT(future);
- FailureHandler<Function, ResultType> *failureHandler = new FailureHandler<Function, ResultType>(
- std::forward<Function>(function), *future, promise);
+ auto failureContinuation = [function = std::forward<F>(function), promise_ = QPromise(fi)](
+ const QFutureInterfaceBase &parentData) mutable {
+ const auto parent = QFutureInterface<ResultType>(parentData).future();
+ FailureHandler<Function, ResultType> failureHandler(std::forward<Function>(function),
+ parent, std::move(promise_));
+ failureHandler.run();
+ };
- auto failureContinuation = [failureHandler]() mutable {
- failureHandler->run();
- delete failureHandler;
+ future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation)));
+}
+
+template<class Function, class ResultType>
+template<class F>
+void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future,
+ QFutureInterface<ResultType> &fi,
+ QObject *context)
+{
+ Q_ASSERT(future);
+ Q_ASSERT(context);
+ auto failureContinuation = [function = std::forward<F>(function),
+ parent = *future, promise_ = QPromise(fi)]() mutable {
+ FailureHandler<Function, ResultType> failureHandler(
+ std::forward<Function>(function), parent, std::move(promise_));
+ failureHandler.run();
};
- future->d.setContinuation(std::move(failureContinuation));
+ QtPrivate::watchContinuation(context, std::move(failureContinuation), future->d);
}
template<class Function, class ResultType>
@@ -545,19 +717,21 @@ void FailureHandler<Function, ResultType>::run()
{
Q_ASSERT(parentFuture.isFinished());
- promise.reportStarted();
+ promise.start();
- if (parentFuture.d.exceptionStore().hasException()) {
+ if (parentFuture.d.hasException()) {
using ArgType = typename QtPrivate::ArgResolver<Function>::First;
if constexpr (std::is_void_v<ArgType>) {
handleAllExceptions();
} else {
handleException<ArgType>();
}
+ } else if (parentFuture.d.isChainCanceled()) {
+ promise.future().cancel();
} else {
QtPrivate::fulfillPromise(promise, parentFuture);
}
- promise.reportFinished();
+ promise.finish();
}
template<class Function, class ResultType>
@@ -565,25 +739,22 @@ template<class ArgType>
void FailureHandler<Function, ResultType>::handleException()
{
try {
- parentFuture.d.exceptionStore().throwPossibleException();
+ Q_ASSERT(parentFuture.d.hasException());
+ parentFuture.d.exceptionStore().rethrowException();
} catch (const ArgType &e) {
try {
// Handle exceptions matching with the handler's argument type
- if constexpr (std::is_void_v<ResultType>) {
+ if constexpr (std::is_void_v<ResultType>)
handler(e);
- } else {
- if constexpr (std::is_copy_constructible_v<ResultType>)
- promise.reportResult(handler(e));
- else
- promise.reportAndMoveResult(handler(e));
- }
+ else
+ promise.addResult(handler(e));
} catch (...) {
- promise.reportException(std::current_exception());
+ promise.setException(std::current_exception());
}
} catch (...) {
// Exception doesn't match with handler's argument type, propagate
// the exception to be handled later.
- promise.reportException(std::current_exception());
+ promise.setException(std::current_exception());
}
}
@@ -591,12 +762,13 @@ template<class Function, class ResultType>
void FailureHandler<Function, ResultType>::handleAllExceptions()
{
try {
- parentFuture.d.exceptionStore().throwPossibleException();
+ Q_ASSERT(parentFuture.d.hasException());
+ parentFuture.d.exceptionStore().rethrowException();
} catch (...) {
try {
QtPrivate::fulfillPromise(promise, std::forward<Function>(handler));
} catch (...) {
- promise.reportException(std::current_exception());
+ promise.setException(std::current_exception());
}
}
}
@@ -607,41 +779,124 @@ template<class Function, class ResultType>
class CanceledHandler
{
public:
- static QFuture<ResultType> create(Function &&handler, QFuture<ResultType> *future,
- QFutureInterface<ResultType> promise)
+ template<class F = Function>
+ static void create(F &&handler, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi)
{
Q_ASSERT(future);
- auto canceledContinuation = [parentFuture = *future, promise,
- handler = std::move(handler)]() mutable {
- promise.reportStarted();
+ auto canceledContinuation = [promise = QPromise(fi), handler = std::forward<F>(handler)](
+ const QFutureInterfaceBase &parentData) mutable {
+ auto parentFuture = QFutureInterface<ResultType>(parentData).future();
+ run(std::forward<F>(handler), parentFuture, std::move(promise));
+ };
+ future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation)));
+ }
- if (parentFuture.isCanceled()) {
+ template<class F = Function>
+ static void create(F &&handler, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi,
+ QObject *context)
+ {
+ Q_ASSERT(future);
+ Q_ASSERT(context);
+ auto canceledContinuation = [handler = std::forward<F>(handler),
+ parentFuture = *future, promise = QPromise(fi)]() mutable {
+ run(std::forward<F>(handler), parentFuture, std::move(promise));
+ };
+
+ QtPrivate::watchContinuation(context, std::move(canceledContinuation), future->d);
+ }
+
+ template<class F = Function>
+ static void run(F &&handler, QFuture<ResultType> &parentFuture, QPromise<ResultType> &&promise)
+ {
+ promise.start();
+
+ if (parentFuture.isCanceled()) {
#ifndef QT_NO_EXCEPTIONS
- if (parentFuture.d.exceptionStore().hasException()) {
- // Propagate the exception to the result future
- promise.reportException(parentFuture.d.exceptionStore().exception());
- } else {
- try {
+ if (parentFuture.d.hasException()) {
+ // Propagate the exception to the result future
+ promise.setException(parentFuture.d.exceptionStore().exception());
+ } else {
+ try {
#endif
- QtPrivate::fulfillPromise(promise, std::forward<Function>(handler));
+ QtPrivate::fulfillPromise(promise, std::forward<F>(handler));
#ifndef QT_NO_EXCEPTIONS
- } catch (...) {
- promise.reportException(std::current_exception());
- }
+ } catch (...) {
+ promise.setException(std::current_exception());
}
+ }
#endif
- } else {
- QtPrivate::fulfillPromise(promise, parentFuture);
+ } else {
+ QtPrivate::fulfillPromise(promise, parentFuture);
+ }
+
+ promise.finish();
+ }
+};
+
+struct UnwrapHandler
+{
+ template<class T>
+ static auto unwrapImpl(T *outer)
+ {
+ Q_ASSERT(outer);
+
+ using ResultType = typename QtPrivate::Future<std::decay_t<T>>::type;
+ using NestedType = typename QtPrivate::Future<ResultType>::type;
+ QFutureInterface<NestedType> promise(QFutureInterfaceBase::State::Pending);
+
+ outer->then([promise](const QFuture<ResultType> &outerFuture) mutable {
+ // We use the .then([](QFuture<ResultType> outerFuture) {...}) version
+ // (where outerFuture == *outer), to propagate the exception if the
+ // outer future has failed.
+ Q_ASSERT(outerFuture.isFinished());
+#ifndef QT_NO_EXCEPTIONS
+ if (outerFuture.d.hasException()) {
+ promise.reportStarted();
+ promise.reportException(outerFuture.d.exceptionStore().exception());
+ promise.reportFinished();
+ return;
}
+#endif
+
+ promise.reportStarted();
+ ResultType nestedFuture = outerFuture.result();
+ nestedFuture.then([promise] (const QFuture<NestedType> &nested) mutable {
+#ifndef QT_NO_EXCEPTIONS
+ if (nested.d.hasException()) {
+ promise.reportException(nested.d.exceptionStore().exception());
+ } else
+#endif
+ {
+ if constexpr (!std::is_void_v<NestedType>)
+ promise.reportResults(nested.results());
+ }
+ promise.reportFinished();
+ }).onCanceled([promise] () mutable {
+ promise.reportCanceled();
+ promise.reportFinished();
+ });
+ }).onCanceled([promise]() mutable {
+ // propagate the cancellation of the outer future
+ promise.reportStarted();
+ promise.reportCanceled();
promise.reportFinished();
- };
- future->d.setContinuation(std::move(canceledContinuation));
+ });
return promise.future();
}
};
+template<typename ValueType>
+QFuture<ValueType> makeReadyRangeFutureImpl(const QList<ValueType> &values)
+{
+ QFutureInterface<ValueType> promise;
+ promise.reportStarted();
+ promise.reportResults(values);
+ promise.reportFinished();
+ return promise.future();
+}
+
} // namespace QtPrivate
namespace QtFuture {
@@ -655,6 +910,11 @@ static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal)
using ArgsType = ArgsType<Signal>;
QFutureInterface<ArgsType> promise;
promise.reportStarted();
+ if (!sender) {
+ promise.reportCanceled();
+ promise.reportFinished();
+ return promise.future();
+ }
using Connections = std::pair<QMetaObject::Connection, QMetaObject::Connection>;
auto connections = std::make_shared<Connections>();
@@ -662,39 +922,280 @@ static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal)
if constexpr (std::is_void_v<ArgsType>) {
connections->first =
QObject::connect(sender, signal, sender, [promise, connections]() mutable {
- promise.reportFinished();
QObject::disconnect(connections->first);
QObject::disconnect(connections->second);
+ promise.reportFinished();
});
- } else if constexpr (QtPrivate::isTupleV<ArgsType>) {
+ } else if constexpr (QtPrivate::ArgResolver<Signal>::HasExtraArgs) {
connections->first = QObject::connect(sender, signal, sender,
[promise, connections](auto... values) mutable {
- promise.reportResult(std::make_tuple(values...));
- promise.reportFinished();
QObject::disconnect(connections->first);
QObject::disconnect(connections->second);
+ promise.reportResult(QtPrivate::createTuple(
+ std::move(values)...));
+ promise.reportFinished();
});
} else {
connections->first = QObject::connect(sender, signal, sender,
[promise, connections](ArgsType value) mutable {
- promise.reportResult(value);
- promise.reportFinished();
QObject::disconnect(connections->first);
QObject::disconnect(connections->second);
+ promise.reportResult(value);
+ promise.reportFinished();
});
}
+ if (!connections->first) {
+ promise.reportCanceled();
+ promise.reportFinished();
+ return promise.future();
+ }
+
connections->second =
QObject::connect(sender, &QObject::destroyed, sender, [promise, connections]() mutable {
- promise.reportCanceled();
- promise.reportFinished();
QObject::disconnect(connections->first);
QObject::disconnect(connections->second);
+ promise.reportCanceled();
+ promise.reportFinished();
});
return promise.future();
}
+template<typename Container>
+using if_container_with_input_iterators =
+ std::enable_if_t<QtPrivate::HasInputIterator<Container>::value, bool>;
+
+template<typename Container>
+using ContainedType =
+ typename std::iterator_traits<decltype(
+ std::cbegin(std::declval<Container&>()))>::value_type;
+
+template<typename Container, if_container_with_input_iterators<Container> = true>
+static QFuture<ContainedType<Container>> makeReadyRangeFuture(Container &&container)
+{
+ // handle QList<T> separately, because reportResults() takes a QList
+ // as an input
+ using ValueType = ContainedType<Container>;
+ if constexpr (std::is_convertible_v<q20::remove_cvref_t<Container>, QList<ValueType>>) {
+ return QtPrivate::makeReadyRangeFutureImpl(container);
+ } else {
+ return QtPrivate::makeReadyRangeFutureImpl(QList<ValueType>{std::cbegin(container),
+ std::cend(container)});
+ }
+}
+
+template<typename ValueType>
+static QFuture<ValueType> makeReadyRangeFuture(std::initializer_list<ValueType> values)
+{
+ return QtPrivate::makeReadyRangeFutureImpl(QList<ValueType>{values});
+}
+
+template<typename T>
+static QFuture<std::decay_t<T>> makeReadyValueFuture(T &&value)
+{
+ QFutureInterface<std::decay_t<T>> promise;
+ promise.reportStarted();
+ promise.reportResult(std::forward<T>(value));
+ promise.reportFinished();
+
+ return promise.future();
+}
+
+Q_CORE_EXPORT QFuture<void> makeReadyVoidFuture(); // implemented in qfutureinterface.cpp
+
+#if QT_DEPRECATED_SINCE(6, 10)
+template<typename T, typename = QtPrivate::EnableForNonVoid<T>>
+QT_DEPRECATED_VERSION_X(6, 10, "Use makeReadyValueFuture() instead.")
+static QFuture<std::decay_t<T>> makeReadyFuture(T &&value)
+{
+ return makeReadyValueFuture(std::forward<T>(value));
+}
+
+// the void specialization is moved to the end of qfuture.h, because it now
+// uses makeReadyVoidFuture() and required QFuture<void> to be defined.
+
+template<typename T>
+QT_DEPRECATED_VERSION_X(6, 10, "Use makeReadyRangeFuture() instead.")
+static QFuture<T> makeReadyFuture(const QList<T> &values)
+{
+ return makeReadyRangeFuture(values);
+}
+#endif // QT_DEPRECATED_SINCE(6, 10)
+
+#ifndef QT_NO_EXCEPTIONS
+
+template<typename T = void>
+static QFuture<T> makeExceptionalFuture(std::exception_ptr exception)
+{
+ QFutureInterface<T> promise;
+ promise.reportStarted();
+ promise.reportException(exception);
+ promise.reportFinished();
+
+ return promise.future();
+}
+
+template<typename T = void>
+static QFuture<T> makeExceptionalFuture(const QException &exception)
+{
+ try {
+ exception.raise();
+ } catch (...) {
+ return makeExceptionalFuture<T>(std::current_exception());
+ }
+ Q_UNREACHABLE();
+}
+
+#endif // QT_NO_EXCEPTIONS
+
} // namespace QtFuture
+namespace QtPrivate {
+
+template<typename ResultFutures>
+struct WhenAllContext
+{
+ using ValueType = typename ResultFutures::value_type;
+
+ explicit WhenAllContext(qsizetype size) : remaining(size) {}
+
+ template<typename T = ValueType>
+ void checkForCompletion(qsizetype index, T &&future)
+ {
+ futures[index] = std::forward<T>(future);
+ const auto oldRemaining = remaining.fetchAndSubRelaxed(1);
+ Q_ASSERT(oldRemaining > 0);
+ if (oldRemaining <= 1) { // that was the last one
+ promise.addResult(futures);
+ promise.finish();
+ }
+ }
+
+ QAtomicInteger<qsizetype> remaining;
+ QPromise<ResultFutures> promise;
+ ResultFutures futures;
+};
+
+template<typename ResultType>
+struct WhenAnyContext
+{
+ using ValueType = ResultType;
+
+ template<typename T = ResultType, typename = EnableForNonVoid<T>>
+ void checkForCompletion(qsizetype, T &&result)
+ {
+ if (!ready.fetchAndStoreRelaxed(true)) {
+ promise.addResult(std::forward<T>(result));
+ promise.finish();
+ }
+ }
+
+ QAtomicInt ready = false;
+ QPromise<ResultType> promise;
+};
+
+template<qsizetype Index, typename ContextType, typename... Ts>
+void addCompletionHandlersImpl(const std::shared_ptr<ContextType> &context,
+ const std::tuple<Ts...> &t)
+{
+ auto future = std::get<Index>(t);
+ using ResultType = typename ContextType::ValueType;
+ // Need context=context so that the compiler does not infer the captured variable's type as 'const'
+ future.then([context=context](const std::tuple_element_t<Index, std::tuple<Ts...>> &f) {
+ context->checkForCompletion(Index, ResultType { std::in_place_index<Index>, f });
+ }).onCanceled([context=context, future]() {
+ context->checkForCompletion(Index, ResultType { std::in_place_index<Index>, future });
+ });
+
+ if constexpr (Index != 0)
+ addCompletionHandlersImpl<Index - 1, ContextType, Ts...>(context, t);
+}
+
+template<typename ContextType, typename... Ts>
+void addCompletionHandlers(const std::shared_ptr<ContextType> &context, const std::tuple<Ts...> &t)
+{
+ constexpr qsizetype size = std::tuple_size<std::tuple<Ts...>>::value;
+ addCompletionHandlersImpl<size - 1, ContextType, Ts...>(context, t);
+}
+
+template<typename OutputSequence, typename InputIt, typename ValueType>
+QFuture<OutputSequence> whenAllImpl(InputIt first, InputIt last)
+{
+ const qsizetype size = std::distance(first, last);
+ if (size == 0)
+ return QtFuture::makeReadyValueFuture(OutputSequence());
+
+ const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(size);
+ context->futures.resize(size);
+ context->promise.start();
+
+ qsizetype idx = 0;
+ for (auto it = first; it != last; ++it, ++idx) {
+ // Need context=context so that the compiler does not infer the captured variable's type as 'const'
+ it->then([context=context, idx](const ValueType &f) {
+ context->checkForCompletion(idx, f);
+ }).onCanceled([context=context, idx, f = *it] {
+ context->checkForCompletion(idx, f);
+ });
+ }
+ return context->promise.future();
+}
+
+template<typename OutputSequence, typename... Futures>
+QFuture<OutputSequence> whenAllImpl(Futures &&... futures)
+{
+ constexpr qsizetype size = sizeof...(Futures);
+ const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(size);
+ context->futures.resize(size);
+ context->promise.start();
+
+ QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward<Futures>(futures)...));
+
+ return context->promise.future();
+}
+
+template<typename InputIt, typename ValueType>
+QFuture<QtFuture::WhenAnyResult<typename Future<ValueType>::type>> whenAnyImpl(InputIt first,
+ InputIt last)
+{
+ using PackagedType = typename Future<ValueType>::type;
+ using ResultType = QtFuture::WhenAnyResult<PackagedType>;
+
+ const qsizetype size = std::distance(first, last);
+ if (size == 0) {
+ return QtFuture::makeReadyValueFuture(
+ QtFuture::WhenAnyResult { qsizetype(-1), QFuture<PackagedType>() });
+ }
+
+ const auto context = std::make_shared<QtPrivate::WhenAnyContext<ResultType>>();
+ context->promise.start();
+
+ qsizetype idx = 0;
+ for (auto it = first; it != last; ++it, ++idx) {
+ // Need context=context so that the compiler does not infer the captured variable's type as 'const'
+ it->then([context=context, idx](const ValueType &f) {
+ context->checkForCompletion(idx, QtFuture::WhenAnyResult { idx, f });
+ }).onCanceled([context=context, idx, f = *it] {
+ context->checkForCompletion(idx, QtFuture::WhenAnyResult { idx, f });
+ });
+ }
+ return context->promise.future();
+}
+
+template<typename... Futures>
+QFuture<std::variant<std::decay_t<Futures>...>> whenAnyImpl(Futures &&... futures)
+{
+ using ResultType = std::variant<std::decay_t<Futures>...>;
+
+ const auto context = std::make_shared<QtPrivate::WhenAnyContext<ResultType>>();
+ context->promise.start();
+
+ QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward<Futures>(futures)...));
+
+ return context->promise.future();
+}
+
+} // namespace QtPrivate
+
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp
index a625e05aac..f83306af00 100644
--- a/src/corelib/thread/qfutureinterface.cpp
+++ b/src/corelib/thread/qfutureinterface.cpp
@@ -1,53 +1,21 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 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) 2020 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
// qfutureinterface.h included from qfuture.h
#include "qfuture.h"
#include "qfutureinterface_p.h"
#include <QtCore/qatomic.h>
+#include <QtCore/qcoreapplication.h>
#include <QtCore/qthread.h>
+#include <QtCore/qvarlengtharray.h>
#include <private/qthreadpool_p.h>
+#include <private/qobject_p.h>
-#ifdef interface
-# undef interface
-#endif
+// GCC 12 gets confused about QFutureInterfaceBase::state, for some non-obvious
+// reason
+// warning: ‘unsigned int __atomic_or_fetch_4(volatile void*, unsigned int, int)’ writing 4 bytes into a region of size 0 overflows the destination [-Wstringop-overflow=]
+QT_WARNING_DISABLE_GCC("-Wstringop-overflow")
QT_BEGIN_NAMESPACE
@@ -59,6 +27,7 @@ namespace {
class ThreadPoolThreadReleaser {
QThreadPool *m_pool;
public:
+ Q_NODISCARD_CTOR
explicit ThreadPoolThreadReleaser(QThreadPool *pool)
: m_pool(pool)
{ if (pool) pool->releaseThread(); }
@@ -71,6 +40,67 @@ const auto suspendingOrSuspended =
} // unnamed namespace
+class QObjectContinuationWrapper : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QObjectContinuationWrapper(QObject *parent = nullptr)
+ : QObject(parent)
+ {
+ }
+
+signals:
+ void run();
+};
+
+void QtPrivate::watchContinuationImpl(const QObject *context, QSlotObjectBase *slotObj,
+ QFutureInterfaceBase &fi)
+{
+ Q_ASSERT(context);
+ Q_ASSERT(slotObj);
+
+ auto slot = SlotObjUniquePtr(slotObj);
+
+ auto *watcher = new QObjectContinuationWrapper;
+ watcher->moveToThread(context->thread());
+
+ // We need to protect acccess to the watcher. The context object (and in turn, the watcher)
+ // could be destroyed while the continuation that emits the signal is running. We have to
+ // prevent that.
+ // The mutex has to be recursive, because the continuation itself could delete the context
+ // object (and thus the watcher), which will try to lock the mutex from the same thread twice.
+ auto watcherMutex = std::make_shared<QRecursiveMutex>();
+ const auto destroyWatcher = [watcherMutex, watcher]() mutable {
+ QMutexLocker lock(watcherMutex.get());
+ delete watcher;
+ };
+
+ // ### we're missing a convenient way to `QObject::connect()` to a `QSlotObjectBase`...
+ QObject::connect(watcher, &QObjectContinuationWrapper::run,
+ // for the following, cf. QMetaObject::invokeMethodImpl():
+ // we know `slot` is a lambda returning `void`, so we can just
+ // `call()` with `obj` and `args[0]` set to `nullptr`:
+ context, [slot = std::move(slot)] {
+ void *args[] = { nullptr }; // for `void` return value
+ slot->call(nullptr, args);
+ });
+ QObject::connect(watcher, &QObjectContinuationWrapper::run, watcher, &QObject::deleteLater);
+ QObject::connect(context, &QObject::destroyed, watcher, destroyWatcher);
+
+ fi.setContinuation([watcherMutex, watcher = QPointer(watcher)]
+ (const QFutureInterfaceBase &parentData)
+ {
+ Q_UNUSED(parentData);
+ QMutexLocker lock(watcherMutex.get());
+ if (watcher)
+ emit watcher->run();
+ });
+}
+
+QFutureCallOutInterface::~QFutureCallOutInterface()
+ = default;
+
+Q_IMPL_EVENT_COMMON(QFutureCallOutEvent)
QFutureInterfaceBase::QFutureInterfaceBase(State initialState)
: d(new QFutureInterfaceBasePrivate(initialState))
@@ -84,7 +114,7 @@ QFutureInterfaceBase::QFutureInterfaceBase(const QFutureInterfaceBase &other)
QFutureInterfaceBase::~QFutureInterfaceBase()
{
- if (!d->refCount.deref())
+ if (d && !d->refCount.deref())
delete d;
}
@@ -100,24 +130,52 @@ static inline int switch_off(QAtomicInt &a, int which)
static inline int switch_from_to(QAtomicInt &a, int from, int to)
{
- int newValue;
- int expected = a.loadRelaxed();
- do {
- newValue = (expected & ~from) | to;
- } while (!a.testAndSetRelaxed(expected, newValue, expected));
- return newValue;
+ const auto adjusted = [&](int old) { return (old & ~from) | to; };
+ int value = a.loadRelaxed();
+ while (!a.testAndSetRelaxed(value, adjusted(value), value))
+ qYieldCpu();
+ return value;
}
void QFutureInterfaceBase::cancel()
{
+ cancel(CancelMode::CancelOnly);
+}
+
+void QFutureInterfaceBase::cancel(QFutureInterfaceBase::CancelMode mode)
+{
QMutexLocker locker(&d->m_mutex);
- if (d->state.loadRelaxed() & Canceled)
- return;
- switch_from_to(d->state, suspendingOrSuspended, Canceled);
+ const auto oldState = d->state.loadRelaxed();
+
+ switch (mode) {
+ case CancelMode::CancelAndFinish:
+ if ((oldState & Finished) && (oldState & Canceled))
+ return;
+ switch_from_to(d->state, suspendingOrSuspended | Running, Canceled | Finished);
+ break;
+ case CancelMode::CancelOnly:
+ if (oldState & Canceled)
+ return;
+ switch_from_to(d->state, suspendingOrSuspended, Canceled);
+ break;
+ }
+
+ // Cancel the continuations chain
+ QFutureInterfaceBasePrivate *next = d->continuationData;
+ while (next) {
+ next->continuationState = QFutureInterfaceBasePrivate::Canceled;
+ next = next->continuationData;
+ }
+
d->waitCondition.wakeAll();
d->pausedWaitCondition.wakeAll();
- d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Canceled));
+
+ if (!(oldState & Canceled))
+ d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Canceled));
+ if (mode == CancelMode::CancelAndFinish && !(oldState & Finished))
+ d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Finished));
+
d->isValid = false;
}
@@ -153,7 +211,7 @@ void QFutureInterfaceBase::reportSuspended() const
// i.e. no more events will be reported.
QMutexLocker locker(&d->m_mutex);
- const int state = d->state;
+ const int state = d->state.loadRelaxed();
if (!(state & Suspending) || (state & Suspended))
return;
@@ -299,13 +357,13 @@ int QFutureInterfaceBase::progressValue() const
int QFutureInterfaceBase::progressMinimum() const
{
const QMutexLocker lock(&d->m_mutex);
- return d->m_progressMinimum;
+ return d->m_progress ? d->m_progress->minimum : 0;
}
int QFutureInterfaceBase::progressMaximum() const
{
const QMutexLocker lock(&d->m_mutex);
- return d->m_progressMaximum;
+ return d->m_progress ? d->m_progress->maximum : 0;
}
int QFutureInterfaceBase::resultCount() const
@@ -317,7 +375,7 @@ int QFutureInterfaceBase::resultCount() const
QString QFutureInterfaceBase::progressText() const
{
QMutexLocker locker(&d->m_mutex);
- return d->m_progressText;
+ return d->m_progress ? d->m_progress->text : QString();
}
bool QFutureInterfaceBase::isProgressUpdateNeeded() const
@@ -351,13 +409,18 @@ void QFutureInterfaceBase::reportException(const QException &exception)
}
}
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
void QFutureInterfaceBase::reportException(std::exception_ptr exception)
+#else
+void QFutureInterfaceBase::reportException(const std::exception_ptr &exception)
+#endif
{
QMutexLocker locker(&d->m_mutex);
if (d->state.loadRelaxed() & (Canceled|Finished))
return;
- d->m_exceptionStore.setException(exception);
+ d->hasException = true;
+ d->data.setException(exception);
switch_on(d->state, Canceled);
d->waitCondition.wakeAll();
d->pausedWaitCondition.wakeAll();
@@ -377,7 +440,7 @@ void QFutureInterfaceBase::reportFinished()
void QFutureInterfaceBase::setExpectedResultCount(int resultCount)
{
- if (d->manualProgress == false)
+ if (d->m_progress)
setProgressRange(0, resultCount);
d->m_expectedResultCount = resultCount;
}
@@ -394,12 +457,16 @@ bool QFutureInterfaceBase::queryState(State state) const
int QFutureInterfaceBase::loadState() const
{
+ // Used from ~QPromise, so this check is needed
+ if (!d)
+ return QFutureInterfaceBase::State::NoState;
return d->state.loadRelaxed();
}
void QFutureInterfaceBase::waitForResult(int resultIndex)
{
- d->m_exceptionStore.throwPossibleException();
+ if (d->hasException)
+ d->data.m_exceptionStore.rethrowException();
QMutexLocker lock(&d->m_mutex);
if (!isRunningOrPending())
@@ -416,13 +483,14 @@ void QFutureInterfaceBase::waitForResult(int resultIndex)
while (isRunningOrPending() && !d->internal_isResultReadyAt(waitIndex))
d->waitCondition.wait(&d->m_mutex);
- d->m_exceptionStore.throwPossibleException();
+ if (d->hasException)
+ d->data.m_exceptionStore.rethrowException();
}
void QFutureInterfaceBase::waitForFinished()
{
QMutexLocker lock(&d->m_mutex);
- const bool alreadyFinished = !isRunningOrPending();
+ const bool alreadyFinished = isFinished();
lock.unlock();
if (!alreadyFinished) {
@@ -430,11 +498,12 @@ void QFutureInterfaceBase::waitForFinished()
lock.relock();
- while (isRunningOrPending())
+ while (!isFinished())
d->waitCondition.wait(&d->m_mutex);
}
- d->m_exceptionStore.throwPossibleException();
+ if (d->hasException)
+ d->data.m_exceptionStore.rethrowException();
}
void QFutureInterfaceBase::reportResultsReady(int beginIndex, int endIndex)
@@ -444,8 +513,8 @@ void QFutureInterfaceBase::reportResultsReady(int beginIndex, int endIndex)
d->waitCondition.wakeAll();
- if (d->manualProgress == false) {
- if (d->internal_updateProgress(d->m_progressValue + endIndex - beginIndex) == false) {
+ if (!d->m_progress) {
+ if (d->internal_updateProgressValue(d->m_progressValue + endIndex - beginIndex) == false) {
d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady,
beginIndex,
endIndex));
@@ -454,7 +523,7 @@ void QFutureInterfaceBase::reportResultsReady(int beginIndex, int endIndex)
d->sendCallOuts(QFutureCallOutEvent(QFutureCallOutEvent::Progress,
d->m_progressValue,
- d->m_progressText),
+ QString()),
QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady,
beginIndex,
endIndex));
@@ -481,15 +550,35 @@ QThreadPool *QFutureInterfaceBase::threadPool() const
void QFutureInterfaceBase::setFilterMode(bool enable)
{
QMutexLocker locker(&d->m_mutex);
- resultStoreBase().setFilterMode(enable);
+ if (!hasException())
+ resultStoreBase().setFilterMode(enable);
}
+/*!
+ \internal
+ Sets the progress range's minimum and maximum values to \a minimum and
+ \a maximum respectively.
+
+ If \a maximum is smaller than \a minimum, \a minimum becomes the only
+ legal value.
+
+ The progress value is reset to be \a minimum.
+
+ The progress range usage can be disabled by using setProgressRange(0, 0).
+ In this case progress value is also reset to 0.
+
+ The behavior of this method is mostly inspired by
+ \l QProgressBar::setRange.
+*/
void QFutureInterfaceBase::setProgressRange(int minimum, int maximum)
{
QMutexLocker locker(&d->m_mutex);
- d->m_progressMinimum = minimum;
- d->m_progressMaximum = maximum;
+ if (!d->m_progress)
+ d->m_progress.reset(new QFutureInterfaceBasePrivate::ProgressData());
+ d->m_progress->minimum = minimum;
+ d->m_progress->maximum = qMax(minimum, maximum);
d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, minimum, maximum));
+ d->m_progressValue = minimum;
}
void QFutureInterfaceBase::setProgressValue(int progressValue)
@@ -497,12 +586,25 @@ void QFutureInterfaceBase::setProgressValue(int progressValue)
setProgressValueAndText(progressValue, QString());
}
+/*!
+ \internal
+ In case of the \a progressValue falling out of the progress range,
+ this method has no effect.
+ Such behavior is inspired by \l QProgressBar::setValue.
+*/
void QFutureInterfaceBase::setProgressValueAndText(int progressValue,
const QString &progressText)
{
QMutexLocker locker(&d->m_mutex);
- if (d->manualProgress == false)
- d->manualProgress = true;
+ if (!d->m_progress)
+ d->m_progress.reset(new QFutureInterfaceBasePrivate::ProgressData());
+
+ const bool useProgressRange = (d->m_progress->maximum != 0) || (d->m_progress->minimum != 0);
+ if (useProgressRange
+ && ((progressValue < d->m_progress->minimum) || (progressValue > d->m_progress->maximum))) {
+ return;
+ }
+
if (d->m_progressValue >= progressValue)
return;
@@ -512,7 +614,7 @@ void QFutureInterfaceBase::setProgressValueAndText(int progressValue,
if (d->internal_updateProgress(progressValue, progressText)) {
d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Progress,
d->m_progressValue,
- d->m_progressText));
+ d->m_progress->text));
}
}
@@ -521,82 +623,121 @@ QMutex &QFutureInterfaceBase::mutex() const
return d->m_mutex;
}
+bool QFutureInterfaceBase::hasException() const
+{
+ return d->hasException;
+}
+
QtPrivate::ExceptionStore &QFutureInterfaceBase::exceptionStore()
{
- return d->m_exceptionStore;
+ Q_ASSERT(d->hasException);
+ return d->data.m_exceptionStore;
}
QtPrivate::ResultStoreBase &QFutureInterfaceBase::resultStoreBase()
{
- return d->m_results;
+ Q_ASSERT(!d->hasException);
+ return d->data.m_results;
}
const QtPrivate::ResultStoreBase &QFutureInterfaceBase::resultStoreBase() const
{
- return d->m_results;
+ Q_ASSERT(!d->hasException);
+ return d->data.m_results;
}
QFutureInterfaceBase &QFutureInterfaceBase::operator=(const QFutureInterfaceBase &other)
{
- other.d->refCount.ref();
- if (!d->refCount.deref())
- delete d;
- d = other.d;
+ QFutureInterfaceBase copy(other);
+ swap(copy);
return *this;
}
+// ### Qt 7: inline
void QFutureInterfaceBase::swap(QFutureInterfaceBase &other) noexcept
{
qSwap(d, other.d);
}
-bool QFutureInterfaceBase::refT() const
+bool QFutureInterfaceBase::refT() const noexcept
{
return d->refCount.refT();
}
-bool QFutureInterfaceBase::derefT() const
+bool QFutureInterfaceBase::derefT() const noexcept
{
- return d->refCount.derefT();
+ // Called from ~QFutureInterface
+ return !d || d->refCount.derefT();
}
void QFutureInterfaceBase::reset()
{
d->m_progressValue = 0;
- d->m_progressMinimum = 0;
- d->m_progressMaximum = 0;
- d->setState(QFutureInterfaceBase::NoState);
+ d->m_progress.reset();
d->progressTime.invalidate();
d->isValid = false;
}
+void QFutureInterfaceBase::rethrowPossibleException()
+{
+ if (hasException())
+ exceptionStore().rethrowException();
+}
+
QFutureInterfaceBasePrivate::QFutureInterfaceBasePrivate(QFutureInterfaceBase::State initialState)
- : refCount(1), m_progressValue(0), m_progressMinimum(0), m_progressMaximum(0),
- state(initialState),
- manualProgress(false), m_expectedResultCount(0), runnable(nullptr), m_pool(nullptr)
+ : state(initialState)
{
progressTime.invalidate();
}
+QFutureInterfaceBasePrivate::~QFutureInterfaceBasePrivate()
+{
+ if (hasException)
+ data.m_exceptionStore.~ExceptionStore();
+ else
+ data.m_results.~ResultStoreBase();
+}
+
int QFutureInterfaceBasePrivate::internal_resultCount() const
{
- return m_results.count(); // ### subtract canceled results.
+ return hasException ? 0 : data.m_results.count(); // ### subtract canceled results.
}
bool QFutureInterfaceBasePrivate::internal_isResultReadyAt(int index) const
{
- return (m_results.contains(index));
+ return hasException ? false : (data.m_results.contains(index));
}
bool QFutureInterfaceBasePrivate::internal_waitForNextResult()
{
- if (m_results.hasNextResult())
+ if (hasException)
+ return false;
+
+ if (data.m_results.hasNextResult())
return true;
- while ((state.loadRelaxed() & QFutureInterfaceBase::Running) && m_results.hasNextResult() == false)
+ while ((state.loadRelaxed() & QFutureInterfaceBase::Running)
+ && data.m_results.hasNextResult() == false)
waitCondition.wait(&m_mutex);
- return !(state.loadRelaxed() & QFutureInterfaceBase::Canceled) && m_results.hasNextResult();
+ return !(state.loadRelaxed() & QFutureInterfaceBase::Canceled)
+ && data.m_results.hasNextResult();
+}
+
+bool QFutureInterfaceBasePrivate::internal_updateProgressValue(int progress)
+{
+ if (m_progressValue >= progress)
+ return false;
+
+ m_progressValue = progress;
+
+ if (progressTime.isValid() && m_progressValue != 0) // make sure the first and last steps are emitted.
+ if (progressTime.elapsed() < (1000 / MaxProgressEmitsPerSecond))
+ return false;
+
+ progressTime.start();
+ return true;
+
}
bool QFutureInterfaceBasePrivate::internal_updateProgress(int progress,
@@ -605,10 +746,12 @@ bool QFutureInterfaceBasePrivate::internal_updateProgress(int progress,
if (m_progressValue >= progress)
return false;
+ Q_ASSERT(m_progress);
+
m_progressValue = progress;
- m_progressText = progressText;
+ m_progress->text = progressText;
- if (progressTime.isValid() && m_progressValue != m_progressMaximum) // make sure the first and last steps are emitted.
+ if (progressTime.isValid() && m_progressValue != m_progress->maximum) // make sure the first and last steps are emitted.
if (progressTime.elapsed() < (1000 / MaxProgressEmitsPerSecond))
return false;
@@ -638,7 +781,7 @@ void QFutureInterfaceBasePrivate::sendCallOut(const QFutureCallOutEvent &callOut
if (outputConnections.isEmpty())
return;
- for (int i = 0; i < outputConnections.count(); ++i)
+ for (int i = 0; i < outputConnections.size(); ++i)
outputConnections.at(i)->postCallOutEvent(callOutEvent);
}
@@ -648,65 +791,75 @@ void QFutureInterfaceBasePrivate::sendCallOuts(const QFutureCallOutEvent &callOu
if (outputConnections.isEmpty())
return;
- for (int i = 0; i < outputConnections.count(); ++i) {
- QFutureCallOutInterface *interface = outputConnections.at(i);
- interface->postCallOutEvent(callOutEvent1);
- interface->postCallOutEvent(callOutEvent2);
+ for (int i = 0; i < outputConnections.size(); ++i) {
+ QFutureCallOutInterface *iface = outputConnections.at(i);
+ iface->postCallOutEvent(callOutEvent1);
+ iface->postCallOutEvent(callOutEvent2);
}
}
// This function connects an output interface (for example a QFutureWatcher)
// to this future. While holding the lock we check the state and ready results
-// and add the appropriate callouts to the queue. In order to avoid deadlocks,
-// the actual callouts are made at the end while not holding the lock.
-void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface *interface)
+// and add the appropriate callouts to the queue.
+void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface *iface)
{
QMutexLocker locker(&m_mutex);
const auto currentState = state.loadRelaxed();
if (currentState & QFutureInterfaceBase::Started) {
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Started));
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange,
- m_progressMinimum,
- m_progressMaximum));
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress,
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Started));
+ if (m_progress) {
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange,
+ m_progress->minimum,
+ m_progress->maximum));
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress,
+ m_progressValue,
+ m_progress->text));
+ } else {
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange,
+ 0,
+ 0));
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress,
m_progressValue,
- m_progressText));
+ QString()));
+ }
}
- QtPrivate::ResultIteratorBase it = m_results.begin();
- while (it != m_results.end()) {
- const int begin = it.resultIndex();
- const int end = begin + it.batchSize();
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady,
+ if (!hasException) {
+ QtPrivate::ResultIteratorBase it = data.m_results.begin();
+ while (it != data.m_results.end()) {
+ const int begin = it.resultIndex();
+ const int end = begin + it.batchSize();
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady,
begin,
end));
- it.batchedAdvance();
+ it.batchedAdvance();
+ }
}
if (currentState & QFutureInterfaceBase::Suspended)
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Suspended));
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Suspended));
else if (currentState & QFutureInterfaceBase::Suspending)
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Suspending));
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Suspending));
if (currentState & QFutureInterfaceBase::Canceled)
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Canceled));
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Canceled));
if (currentState & QFutureInterfaceBase::Finished)
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Finished));
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Finished));
- outputConnections.append(interface);
+ outputConnections.append(iface);
}
-void QFutureInterfaceBasePrivate::disconnectOutputInterface(QFutureCallOutInterface *interface)
+void QFutureInterfaceBasePrivate::disconnectOutputInterface(QFutureCallOutInterface *iface)
{
QMutexLocker lock(&m_mutex);
- const int index = outputConnections.indexOf(interface);
+ const qsizetype index = outputConnections.indexOf(iface);
if (index == -1)
return;
outputConnections.removeAt(index);
- interface->callOutInterfaceDisconnected();
+ iface->callOutInterfaceDisconnected();
}
void QFutureInterfaceBasePrivate::setState(QFutureInterfaceBase::State newState)
@@ -714,28 +867,72 @@ void QFutureInterfaceBasePrivate::setState(QFutureInterfaceBase::State newState)
state.storeRelaxed(newState);
}
-void QFutureInterfaceBase::setContinuation(std::function<void()> func)
+void QFutureInterfaceBase::setContinuation(std::function<void(const QFutureInterfaceBase &)> func)
+{
+ setContinuation(std::move(func), nullptr);
+}
+
+void QFutureInterfaceBase::setContinuation(std::function<void(const QFutureInterfaceBase &)> func,
+ QFutureInterfaceBasePrivate *continuationFutureData)
{
QMutexLocker lock(&d->continuationMutex);
+
// If the state is ready, run continuation immediately,
// otherwise save it for later.
if (isFinished()) {
lock.unlock();
- func();
- } else {
+ func(*this);
+ lock.relock();
+ }
+ // Unless the continuation has been cleaned earlier, we have to
+ // store the move-only continuation, to guarantee that the associated
+ // future's data stays alive.
+ if (d->continuationState != QFutureInterfaceBasePrivate::Cleaned) {
+ if (d->continuation) {
+ qWarning() << "Adding a continuation to a future which already has a continuation. "
+ "The existing continuation is overwritten.";
+ }
d->continuation = std::move(func);
+ d->continuationData = continuationFutureData;
}
}
+void QFutureInterfaceBase::cleanContinuation()
+{
+ if (!d)
+ return;
+
+ QMutexLocker lock(&d->continuationMutex);
+ d->continuation = nullptr;
+ d->continuationState = QFutureInterfaceBasePrivate::Cleaned;
+ d->continuationData = nullptr;
+}
+
void QFutureInterfaceBase::runContinuation() const
{
QMutexLocker lock(&d->continuationMutex);
if (d->continuation) {
+ // Save the continuation in a local function, to avoid calling
+ // a null std::function below, in case cleanContinuation() is
+ // called from some other thread right after unlock() below.
+ auto fn = std::move(d->continuation);
lock.unlock();
- d->continuation();
+ fn(*this);
+
+ lock.relock();
+ // Unless the continuation has been cleaned earlier, we have to
+ // store the move-only continuation, to guarantee that the associated
+ // future's data stays alive.
+ if (d->continuationState != QFutureInterfaceBasePrivate::Cleaned)
+ d->continuation = std::move(fn);
}
}
+bool QFutureInterfaceBase::isChainCanceled() const
+{
+ return isCanceled() || d->continuationState == QFutureInterfaceBasePrivate::Canceled;
+}
+
void QFutureInterfaceBase::setLaunchAsync(bool value)
{
d->launchAsync = value;
@@ -746,4 +943,19 @@ bool QFutureInterfaceBase::launchAsync() const
return d->launchAsync;
}
+namespace QtFuture {
+
+QFuture<void> makeReadyVoidFuture()
+{
+ QFutureInterface<void> promise;
+ promise.reportStarted();
+ promise.reportFinished();
+
+ return promise.future();
+}
+
+} // namespace QtFuture
+
QT_END_NAMESPACE
+
+#include "qfutureinterface.moc"
diff --git a/src/corelib/thread/qfutureinterface.h b/src/corelib/thread/qfutureinterface.h
index 047d2eb2d3..180a59a94e 100644
--- a/src/corelib/thread/qfutureinterface.h
+++ b/src/corelib/thread/qfutureinterface.h
@@ -1,61 +1,27 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 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) 2020 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 QFUTUREINTERFACE_H
#define QFUTUREINTERFACE_H
-#include <QtCore/qrunnable.h>
#include <QtCore/qmutex.h>
-#include <QtCore/qexception.h>
#include <QtCore/qresultstore.h>
+#ifndef QT_NO_EXCEPTIONS
+#include <exception>
+#endif
#include <utility>
-#include <vector>
-#include <mutex>
QT_REQUIRE_CONFIG(future);
+QT_FORWARD_DECLARE_CLASS(QRunnable)
+QT_FORWARD_DECLARE_CLASS(QException)
QT_BEGIN_NAMESPACE
template <typename T> class QFuture;
class QThreadPool;
+class QFutureInterfaceBase;
class QFutureInterfaceBasePrivate;
class QFutureWatcherBase;
class QFutureWatcherBasePrivate;
@@ -64,6 +30,8 @@ namespace QtPrivate {
template<typename Function, typename ResultType, typename ParentResultType>
class Continuation;
+class ExceptionStore;
+
template<class Function, class ResultType>
class CanceledHandler;
@@ -71,6 +39,10 @@ class CanceledHandler;
template<class Function, class ResultType>
class FailureHandler;
#endif
+
+void Q_CORE_EXPORT watchContinuationImpl(const QObject *context,
+ QtPrivate::QSlotObjectBase *slotObj,
+ QFutureInterfaceBase &fi);
}
class Q_CORE_EXPORT QFutureInterfaceBase
@@ -91,6 +63,10 @@ public:
QFutureInterfaceBase(State initialState = NoState);
QFutureInterfaceBase(const QFutureInterfaceBase &other);
+ QFutureInterfaceBase(QFutureInterfaceBase &&other) noexcept
+ : d(std::exchange(other.d, nullptr)) {}
+ QFutureInterfaceBase &operator=(const QFutureInterfaceBase &other);
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QFutureInterfaceBase)
virtual ~QFutureInterfaceBase();
// reporting functions available to the engine author:
@@ -99,7 +75,11 @@ public:
void reportCanceled();
#ifndef QT_NO_EXCEPTIONS
void reportException(const QException &e);
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
void reportException(std::exception_ptr e);
+#else
+ void reportException(const std::exception_ptr &e);
+#endif
#endif
void reportResultsReady(int beginIndex, int endIndex);
@@ -143,6 +123,8 @@ public:
int loadState() const;
void cancel();
+ void cancelAndFinish() { cancel(CancelMode::CancelAndFinish); }
+
void setSuspended(bool suspend);
void toggleSuspended();
void reportSuspended() const;
@@ -155,20 +137,28 @@ public:
void suspendIfRequested();
QMutex &mutex() const;
+ bool hasException() const;
QtPrivate::ExceptionStore &exceptionStore();
QtPrivate::ResultStoreBase &resultStoreBase();
const QtPrivate::ResultStoreBase &resultStoreBase() const;
inline bool operator==(const QFutureInterfaceBase &other) const { return d == other.d; }
inline bool operator!=(const QFutureInterfaceBase &other) const { return d != other.d; }
- QFutureInterfaceBase &operator=(const QFutureInterfaceBase &other);
+ // ### Qt 7: inline
void swap(QFutureInterfaceBase &other) noexcept;
+ template<typename T>
+ static QFutureInterfaceBase get(const QFuture<T> &future); // implemented in qfuture.h
+
+ bool isChainCanceled() const;
+
protected:
- bool refT() const;
- bool derefT() const;
+ // ### Qt 7: remove const from refT/derefT
+ bool refT() const noexcept;
+ bool derefT() const noexcept;
void reset();
+ void rethrowPossibleException();
public:
#ifndef QFUTURE_TEST
@@ -191,16 +181,33 @@ private:
friend class QtPrivate::FailureHandler;
#endif
+ friend Q_CORE_EXPORT void QtPrivate::watchContinuationImpl(
+ const QObject *context, QtPrivate::QSlotObjectBase *slotObj, QFutureInterfaceBase &fi);
+
+ template<class T>
+ friend class QPromise;
+
protected:
- void setContinuation(std::function<void()> func);
+ void setContinuation(std::function<void(const QFutureInterfaceBase &)> func);
+ void setContinuation(std::function<void(const QFutureInterfaceBase &)> func,
+ QFutureInterfaceBasePrivate *continuationFutureData);
+ void cleanContinuation();
void runContinuation() const;
void setLaunchAsync(bool value);
bool launchAsync() const;
bool isRunningOrPending() const;
+
+ enum class CancelMode { CancelOnly, CancelAndFinish };
+ void cancel(CancelMode mode);
};
+inline void swap(QFutureInterfaceBase &lhs, QFutureInterfaceBase &rhs) noexcept
+{
+ lhs.swap(rhs);
+}
+
template <typename T>
class QFutureInterface : public QFutureInterfaceBase
{
@@ -215,32 +222,36 @@ public:
{
refT();
}
+ QFutureInterface(const QFutureInterfaceBase &dd) : QFutureInterfaceBase(dd) { refT(); }
+ QFutureInterface(QFutureInterfaceBase &&dd) noexcept : QFutureInterfaceBase(std::move(dd)) { refT(); }
+ QFutureInterface &operator=(const QFutureInterface &other)
+ {
+ QFutureInterface copy(other);
+ swap(copy);
+ return *this;
+ }
+ QFutureInterface(QFutureInterface &&other) = default;
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QFutureInterface)
+
~QFutureInterface()
{
- if (!derefT())
+ if (!derefT() && !hasException())
resultStoreBase().template clear<T>();
}
static QFutureInterface canceledResult()
{ return QFutureInterface(State(Started | Finished | Canceled)); }
- QFutureInterface &operator=(const QFutureInterface &other)
- {
- other.refT();
- if (!derefT())
- resultStoreBase().template clear<T>();
- QFutureInterfaceBase::operator=(other);
- return *this;
- }
-
inline QFuture<T> future(); // implemented in qfuture.h
- inline void reportResult(const T *result, int index = -1);
- inline void reportAndMoveResult(T &&result, int index = -1);
- inline void reportResult(T &&result, int index = -1);
- inline void reportResult(const T &result, int index = -1);
- inline void reportResults(const QList<T> &results, int beginIndex = -1, int count = -1);
- inline void reportFinished(const T *result);
+ template <typename...Args, std::enable_if_t<std::is_constructible_v<T, Args...>, bool> = true>
+ inline bool reportAndEmplaceResult(int index, Args&&...args);
+ inline bool reportResult(const T *result, int index = -1);
+ inline bool reportAndMoveResult(T &&result, int index = -1);
+ inline bool reportResult(T &&result, int index = -1);
+ inline bool reportResult(const T &result, int index = -1);
+ inline bool reportResults(const QList<T> &results, int beginIndex = -1, int count = -1);
+ inline bool reportFinished(const T *result);
void reportFinished()
{
QFutureInterfaceBase::reportFinished();
@@ -252,95 +263,137 @@ public:
inline QList<T> results();
T takeResult();
+#if 0
+ // TODO: Enable and make it return a QList, when QList is fixed to support move-only types
std::vector<T> takeResults();
+#endif
+
+#ifndef QT_NO_EXCEPTIONS
+ void reportException(const std::exception_ptr &e)
+ {
+ if (hasException())
+ return;
+
+ resultStoreBase().template clear<T>();
+ QFutureInterfaceBase::reportException(e);
+ }
+ void reportException(const QException &e)
+ {
+ if (hasException())
+ return;
+
+ resultStoreBase().template clear<T>();
+ QFutureInterfaceBase::reportException(e);
+ }
+#endif
};
template <typename T>
-inline void QFutureInterface<T>::reportResult(const T *result, int index)
+inline bool QFutureInterface<T>::reportResult(const T *result, int index)
{
- std::lock_guard<QMutex> locker{mutex()};
- if (this->queryState(Canceled) || this->queryState(Finished)) {
- return;
- }
+ QMutexLocker<QMutex> locker{&mutex()};
+ if (this->queryState(Canceled) || this->queryState(Finished))
+ return false;
+ Q_ASSERT(!hasException());
QtPrivate::ResultStoreBase &store = resultStoreBase();
+ const int resultCountBefore = store.count();
+ const int insertIndex = store.addResult<T>(index, result);
+ if (insertIndex == -1)
+ return false;
if (store.filterMode()) {
- const int resultCountBefore = store.count();
- store.addResult<T>(index, result);
this->reportResultsReady(resultCountBefore, store.count());
} else {
- const int insertIndex = store.addResult<T>(index, result);
this->reportResultsReady(insertIndex, insertIndex + 1);
}
+ return true;
}
template<typename T>
-void QFutureInterface<T>::reportAndMoveResult(T &&result, int index)
+template<typename...Args, std::enable_if_t<std::is_constructible_v<T, Args...>, bool>>
+bool QFutureInterface<T>::reportAndEmplaceResult(int index, Args&&...args)
{
- std::lock_guard<QMutex> locker{mutex()};
+ QMutexLocker<QMutex> locker{&mutex()};
if (queryState(Canceled) || queryState(Finished))
- return;
+ return false;
+ Q_ASSERT(!hasException());
QtPrivate::ResultStoreBase &store = resultStoreBase();
const int oldResultCount = store.count();
- const int insertIndex = store.moveResult(index, std::forward<T>(result));
- if (!store.filterMode() || oldResultCount < store.count()) // Let's make sure it's not in pending results.
+ const int insertIndex = store.emplaceResult<T>(index, std::forward<Args>(args)...);
+ // Let's make sure it's not in pending results.
+ if (insertIndex != -1 && (!store.filterMode() || oldResultCount < store.count()))
reportResultsReady(insertIndex, store.count());
+ return insertIndex != -1;
+}
+
+template<typename T>
+bool QFutureInterface<T>::reportAndMoveResult(T &&result, int index)
+{
+ return reportAndEmplaceResult(index, std::move(result));
}
template<typename T>
-void QFutureInterface<T>::reportResult(T &&result, int index)
+bool QFutureInterface<T>::reportResult(T &&result, int index)
{
- reportAndMoveResult(std::move(result), index);
+ return reportAndMoveResult(std::move(result), index);
}
template <typename T>
-inline void QFutureInterface<T>::reportResult(const T &result, int index)
+inline bool QFutureInterface<T>::reportResult(const T &result, int index)
{
- reportResult(&result, index);
+ return reportResult(&result, index);
}
template<typename T>
-inline void QFutureInterface<T>::reportResults(const QList<T> &_results, int beginIndex, int count)
+inline bool QFutureInterface<T>::reportResults(const QList<T> &_results, int beginIndex, int count)
{
- std::lock_guard<QMutex> locker{mutex()};
- if (this->queryState(Canceled) || this->queryState(Finished)) {
- return;
- }
+ QMutexLocker<QMutex> locker{&mutex()};
+ if (this->queryState(Canceled) || this->queryState(Finished))
+ return false;
+ Q_ASSERT(!hasException());
auto &store = resultStoreBase();
+ const int resultCountBefore = store.count();
+ const int insertIndex = store.addResults(beginIndex, &_results, count);
+ if (insertIndex == -1)
+ return false;
if (store.filterMode()) {
- const int resultCountBefore = store.count();
- store.addResults(beginIndex, &_results, count);
this->reportResultsReady(resultCountBefore, store.count());
} else {
- const int insertIndex = store.addResults(beginIndex, &_results, count);
- this->reportResultsReady(insertIndex, insertIndex + _results.count());
+ this->reportResultsReady(insertIndex, insertIndex + _results.size());
}
+ return true;
}
template <typename T>
-inline void QFutureInterface<T>::reportFinished(const T *result)
+inline bool QFutureInterface<T>::reportFinished(const T *result)
{
+ bool resultReported = false;
if (result)
- reportResult(result);
+ resultReported = reportResult(result);
reportFinished();
+ return resultReported;
}
template <typename T>
inline const T &QFutureInterface<T>::resultReference(int index) const
{
- std::lock_guard<QMutex> locker{mutex()};
+ Q_ASSERT(!hasException());
+
+ QMutexLocker<QMutex> locker{&mutex()};
return resultStoreBase().resultAt(index).template value<T>();
}
template <typename T>
inline const T *QFutureInterface<T>::resultPointer(int index) const
{
- std::lock_guard<QMutex> locker{mutex()};
+ Q_ASSERT(!hasException());
+
+ QMutexLocker<QMutex> locker{&mutex()};
return resultStoreBase().resultAt(index).template pointer<T>();
}
@@ -348,14 +401,14 @@ template <typename T>
inline QList<T> QFutureInterface<T>::results()
{
if (this->isCanceled()) {
- exceptionStore().throwPossibleException();
+ rethrowPossibleException();
return QList<T>();
}
QFutureInterfaceBase::waitForResult(-1);
QList<T> res;
- std::lock_guard<QMutex> locker{mutex()};
+ QMutexLocker<QMutex> locker{&mutex()};
QtPrivate::ResultIteratorBase it = resultStoreBase().begin();
while (it != resultStoreBase().end()) {
@@ -375,7 +428,9 @@ T QFutureInterface<T>::takeResult()
// not to mess with other unready results.
waitForResult(-1);
- const std::lock_guard<QMutex> locker{mutex()};
+ Q_ASSERT(!hasException());
+
+ const QMutexLocker<QMutex> locker{&mutex()};
QtPrivate::ResultIteratorBase position = resultStoreBase().resultAt(0);
T ret(std::move_if_noexcept(position.value<T>()));
reset();
@@ -384,16 +439,20 @@ T QFutureInterface<T>::takeResult()
return ret;
}
+#if 0
template<typename T>
std::vector<T> QFutureInterface<T>::takeResults()
{
Q_ASSERT(isValid());
waitForResult(-1);
+
+ Q_ASSERT(!hasException());
+
std::vector<T> res;
res.reserve(resultCount());
- const std::lock_guard<QMutex> locker{mutex()};
+ const QMutexLocker<QMutex> locker{&mutex()};
QtPrivate::ResultIteratorBase it = resultStoreBase().begin();
for (auto endIt = resultStoreBase().end(); it != endIt; ++it)
@@ -404,24 +463,32 @@ std::vector<T> QFutureInterface<T>::takeResults()
return res;
}
+#endif
template <>
class QFutureInterface<void> : public QFutureInterfaceBase
{
public:
- explicit QFutureInterface<void>(State initialState = NoState)
+ explicit QFutureInterface(State initialState = NoState)
: QFutureInterfaceBase(initialState)
{ }
+ QFutureInterface(const QFutureInterfaceBase &dd) : QFutureInterfaceBase(dd) { }
+
static QFutureInterface<void> canceledResult()
{ return QFutureInterface(State(Started | Finished | Canceled)); }
inline QFuture<void> future(); // implemented in qfuture.h
- void reportResult(const void *, int) { }
- void reportResults(const QList<void> &, int) { }
- void reportFinished(const void * = nullptr)
+ bool reportResult(const void *, int) { return false; }
+ bool reportResults(const QList<void> &, int) { return false; }
+ bool reportFinished(const void *)
+ {
+ reportFinished();
+ return false;
+ }
+ void reportFinished()
{
QFutureInterfaceBase::reportFinished();
QFutureInterfaceBase::runContinuation();
diff --git a/src/corelib/thread/qfutureinterface_p.h b/src/corelib/thread/qfutureinterface_p.h
index 8ef27044bf..92a1072591 100644
--- a/src/corelib/thread/qfutureinterface_p.h
+++ b/src/corelib/thread/qfutureinterface_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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 QFUTUREINTERFACE_P_H
#define QFUTUREINTERFACE_P_H
@@ -58,13 +22,20 @@
#include <QtCore/qwaitcondition.h>
#include <QtCore/qrunnable.h>
#include <QtCore/qthreadpool.h>
+#include <QtCore/qfutureinterface.h>
+#include <QtCore/qexception.h>
QT_REQUIRE_CONFIG(future);
QT_BEGIN_NAMESPACE
-class QFutureCallOutEvent : public QEvent
+// Although QFutureCallOutEvent and QFutureCallOutInterface are private,
+// for historical reasons they were used externally (in QtJambi, see
+// https://github.com/OmixVisualization/qtjambi), so we export them to
+// not break the pre-existing code.
+class Q_CORE_EXPORT QFutureCallOutEvent : public QEvent
{
+ Q_DECL_EVENT_COMMON(QFutureCallOutEvent)
public:
enum CallOutType {
Started,
@@ -100,29 +71,12 @@ public:
int index1;
int index2;
QString text;
-
- QFutureCallOutEvent *clone() const
- {
- return new QFutureCallOutEvent(callOutType, index1, index2, text);
- }
-
-private:
- QFutureCallOutEvent(CallOutType callOutType,
- int index1,
- int index2,
- const QString &text)
- : QEvent(QEvent::FutureCallOut),
- callOutType(callOutType),
- index1(index1),
- index2(index2),
- text(text)
- { }
};
-class QFutureCallOutInterface
+class Q_CORE_EXPORT QFutureCallOutInterface
{
public:
- virtual ~QFutureCallOutInterface() {}
+ virtual ~QFutureCallOutInterface();
virtual void postCallOutEvent(const QFutureCallOutEvent &) = 0;
virtual void callOutInterfaceDisconnected() = 0;
};
@@ -131,6 +85,7 @@ class QFutureInterfaceBasePrivate
{
public:
QFutureInterfaceBasePrivate(QFutureInterfaceBase::State initialState);
+ ~QFutureInterfaceBasePrivate();
// When the last QFuture<T> reference is removed, we need to make
// sure that data stored in the ResultStore is cleaned out.
@@ -158,23 +113,55 @@ public:
// T: accessed from executing thread
// Q: accessed from the waiting/querying thread
- RefCount refCount;
mutable QMutex m_mutex;
- QWaitCondition waitCondition;
+ QBasicMutex continuationMutex;
QList<QFutureCallOutInterface *> outputConnections;
- int m_progressValue; // TQ
- int m_progressMinimum; // TQ
- int m_progressMaximum; // TQ
- QAtomicInt state; // reads and writes can happen unprotected, both must be atomic
QElapsedTimer progressTime;
+ QWaitCondition waitCondition;
QWaitCondition pausedWaitCondition;
- QtPrivate::ResultStoreBase m_results;
- bool manualProgress; // only accessed from executing thread
- int m_expectedResultCount;
- QtPrivate::ExceptionStore m_exceptionStore;
- QString m_progressText;
- QRunnable *runnable;
- QThreadPool *m_pool;
+
+ union Data {
+ QtPrivate::ResultStoreBase m_results;
+ QtPrivate::ExceptionStore m_exceptionStore;
+
+#ifndef QT_NO_EXCEPTIONS
+ void setException(const std::exception_ptr &e)
+ {
+ m_results.~ResultStoreBase();
+ new (&m_exceptionStore) QtPrivate::ExceptionStore();
+ m_exceptionStore.setException(e);
+ }
+#endif
+
+ ~Data() { }
+ };
+ Data data = { QtPrivate::ResultStoreBase() };
+
+ QRunnable *runnable = nullptr;
+ QThreadPool *m_pool = nullptr;
+ // Wrapper for continuation
+ std::function<void(const QFutureInterfaceBase &)> continuation;
+ QFutureInterfaceBasePrivate *continuationData = nullptr;
+
+ RefCount refCount = 1;
+ QAtomicInt state; // reads and writes can happen unprotected, both must be atomic
+
+ int m_progressValue = 0; // TQ
+ struct ProgressData
+ {
+ int minimum = 0; // TQ
+ int maximum = 0; // TQ
+ QString text;
+ };
+ QScopedPointer<ProgressData> m_progress;
+
+ int m_expectedResultCount = 0;
+ bool launchAsync = false;
+ bool isValid = false;
+ bool hasException = false;
+
+ enum ContinuationState : quint8 { Default, Canceled, Cleaned };
+ std::atomic<ContinuationState> continuationState { Default };
inline QThreadPool *pool() const
{ return m_pool ? m_pool : QThreadPool::globalInstance(); }
@@ -184,6 +171,7 @@ public:
int internal_resultCount() const;
bool internal_isResultReadyAt(int index) const;
bool internal_waitForNextResult();
+ bool internal_updateProgressValue(int progress);
bool internal_updateProgress(int progress, const QString &progressText = QString());
void internal_setThrottled(bool enable);
void sendCallOut(const QFutureCallOutEvent &callOut);
@@ -192,13 +180,6 @@ public:
void disconnectOutputInterface(QFutureCallOutInterface *iface);
void setState(QFutureInterfaceBase::State state);
-
- // Wrapper for continuation
- std::function<void()> continuation;
- QBasicMutex continuationMutex;
-
- bool launchAsync = false;
- bool isValid = false;
};
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qfuturesynchronizer.h b/src/corelib/thread/qfuturesynchronizer.h
index 5006ebb9cf..a996362bfd 100644
--- a/src/corelib/thread/qfuturesynchronizer.h
+++ b/src/corelib/thread/qfuturesynchronizer.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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 QFUTURESYNCHRONIZER_H
#define QFUTURESYNCHRONIZER_H
@@ -53,33 +17,34 @@ class QFutureSynchronizer
Q_DISABLE_COPY(QFutureSynchronizer)
public:
- QFutureSynchronizer() : m_cancelOnWait(false) { }
- explicit QFutureSynchronizer(const QFuture<T> &future)
+ Q_NODISCARD_CTOR QFutureSynchronizer() : m_cancelOnWait(false) { }
+ Q_NODISCARD_CTOR_X("Use future.waitForFinished() instead.")
+ explicit QFutureSynchronizer(QFuture<T> future)
: m_cancelOnWait(false)
- { addFuture(future); }
+ { addFuture(std::move(future)); }
~QFutureSynchronizer() { waitForFinished(); }
- void setFuture(const QFuture<T> &future)
+ void setFuture(QFuture<T> future)
{
waitForFinished();
m_futures.clear();
- addFuture(future);
+ addFuture(std::move(future));
}
- void addFuture(const QFuture<T> &future)
+ void addFuture(QFuture<T> future)
{
- m_futures.append(future);
+ m_futures.append(std::move(future));
}
void waitForFinished()
{
if (m_cancelOnWait) {
- for (int i = 0; i < m_futures.count(); ++i) {
+ for (int i = 0; i < m_futures.size(); ++i) {
m_futures[i].cancel();
}
}
- for (int i = 0; i < m_futures.count(); ++i) {
+ for (int i = 0; i < m_futures.size(); ++i) {
m_futures[i].waitForFinished();
}
}
@@ -105,7 +70,7 @@ public:
}
protected:
- QList<QFuture<T> > m_futures;
+ QList<QFuture<T>> m_futures;
bool m_cancelOnWait;
};
diff --git a/src/corelib/thread/qfuturesynchronizer.qdoc b/src/corelib/thread/qfuturesynchronizer.qdoc
index 67dafb039e..7d2d16d6e9 100644
--- a/src/corelib/thread/qfuturesynchronizer.qdoc
+++ b/src/corelib/thread/qfuturesynchronizer.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** 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 Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*! \class QFutureSynchronizer
\since 4.4
@@ -62,7 +38,7 @@
*/
/*!
- \fn template <typename T> QFutureSynchronizer<T>::QFutureSynchronizer(const QFuture<T> &future)
+ \fn template <typename T> QFutureSynchronizer<T>::QFutureSynchronizer(QFuture<T> future)
Constructs a QFutureSynchronizer and begins watching \a future by calling
addFuture().
@@ -80,7 +56,7 @@
*/
/*!
- \fn template <typename T> void QFutureSynchronizer<T>::setFuture(const QFuture<T> &future)
+ \fn template <typename T> void QFutureSynchronizer<T>::setFuture(QFuture<T> future)
Sets \a future to be the only future managed by this QFutureSynchronizer.
This is a convenience function that calls waitForFinished(),
@@ -90,7 +66,7 @@
*/
/*!
- \fn template <typename T> void QFutureSynchronizer<T>::addFuture(const QFuture<T> &future)
+ \fn template <typename T> void QFutureSynchronizer<T>::addFuture(QFuture<T> future)
Adds \a future to the list of managed futures.
diff --git a/src/corelib/thread/qfuturewatcher.cpp b/src/corelib/thread/qfuturewatcher.cpp
index 75f5e297e2..2cffadfa5b 100644
--- a/src/corelib/thread/qfuturewatcher.cpp
+++ b/src/corelib/thread/qfuturewatcher.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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
#include "qfuturewatcher.h"
#include "qfuturewatcher_p.h"
@@ -114,8 +78,8 @@ QFutureWatcherBase::QFutureWatcherBase(QObject *parent)
/*! \fn template <typename T> void QFutureWatcher<T>::cancel()
Cancels the asynchronous computation represented by the future(). Note that
- the cancelation is asynchronous. Use waitForFinished() after calling
- cancel() when you need synchronous cancelation.
+ the cancellation is asynchronous. Use waitForFinished() after calling
+ cancel() when you need synchronous cancellation.
Currently available results may still be accessed on a canceled QFuture,
but new results will \e not become available after calling this function.
@@ -136,8 +100,7 @@ void QFutureWatcherBase::cancel()
#if QT_DEPRECATED_SINCE(6, 0)
/*! \fn template <typename T> void QFutureWatcher<T>::setPaused(bool paused)
- \obsolete
- Use setSuspended() instead.
+ \deprecated [6.6] Use setSuspended() instead.
If \a paused is true, this function pauses the asynchronous computation
represented by the future(). If the computation is already paused, this
@@ -154,7 +117,7 @@ void QFutureWatcherBase::cancel()
QFuture returned by QtConcurrent::run() cannot be paused; but the QFuture
returned by QtConcurrent::mappedReduced() can.
- \sa pause(), resume(), togglePaused()
+ \sa suspend(), resume(), toggleSuspended()
*/
void QFutureWatcherBase::setPaused(bool paused)
{
@@ -163,7 +126,7 @@ void QFutureWatcherBase::setPaused(bool paused)
/*! \fn template <typename T> void QFutureWatcher<T>::pause()
- \obsolete
+ \deprecated
Use suspend() instead.
Pauses the asynchronous computation represented by the future(). This is a
@@ -233,15 +196,14 @@ void QFutureWatcherBase::resume()
#if QT_DEPRECATED_SINCE(6, 0)
/*! \fn template <typename T> void QFutureWatcher<T>::togglePaused()
- \obsolete
- Use toggleSuspended() instead.
+ \deprecated [6.0] Use toggleSuspended() instead.
Toggles the paused state of the asynchronous computation. In other words,
if the computation is currently paused, calling this function resumes it;
if the computation is running, it is paused. This is a convenience method
for calling setPaused(!isPaused()).
- \sa setPaused(), pause(), resume()
+ \sa setSuspended(), suspend(), resume()
*/
void QFutureWatcherBase::togglePaused()
{
@@ -329,8 +291,7 @@ bool QFutureWatcherBase::isStarted() const
*/
bool QFutureWatcherBase::isFinished() const
{
- Q_D(const QFutureWatcherBase);
- return d->finished;
+ return futureInterface().isFinished();
}
/*! \fn template <typename T> bool QFutureWatcher<T>::isRunning() const
@@ -360,8 +321,7 @@ bool QFutureWatcherBase::isCanceled() const
/*! \fn template <typename T> bool QFutureWatcher<T>::isPaused() const
- \obsolete
- Use isSuspending() or isSuspended() instead.
+ \deprecated [6.0] Use isSuspending() or isSuspended() instead.
Returns \c true if the asynchronous computation has been paused with the
pause() function; otherwise returns \c false.
@@ -370,7 +330,7 @@ bool QFutureWatcherBase::isCanceled() const
function returns \c true. See setPaused() for more details. To check
if pause actually took effect, use isSuspended() instead.
- \sa setPaused(), togglePaused(), isSuspended()
+ \sa setSuspended(), toggleSuspended(), isSuspended()
*/
bool QFutureWatcherBase::isPaused() const
@@ -417,7 +377,7 @@ bool QFutureWatcherBase::isSuspended() const
/*! \fn template <typename T> void QFutureWatcher<T>::waitForFinished()
Waits for the asynchronous computation to finish (including cancel()ed
- computations).
+ computations), i.e. until isFinished() returns \c true.
*/
void QFutureWatcherBase::waitForFinished()
{
@@ -480,8 +440,7 @@ void QFutureWatcherBase::disconnectNotify(const QMetaMethod &signal)
*/
QFutureWatcherBasePrivate::QFutureWatcherBasePrivate()
: maximumPendingResultsReady(QThread::idealThreadCount() * 2),
- resultAtConnected(0),
- finished(true) /* the initial m_future is a canceledResult(), with Finished set */
+ resultAtConnected(0)
{ }
/*!
@@ -500,7 +459,6 @@ void QFutureWatcherBase::disconnectOutputInterface(bool pendingAssignment)
if (pendingAssignment) {
Q_D(QFutureWatcherBase);
d->pendingResultsReady.storeRelaxed(0);
- d->finished = false; /* May soon be amended, during connectOutputInterface() */
}
futureInterface().d->disconnectOutputInterface(d_func());
@@ -532,7 +490,6 @@ void QFutureWatcherBasePrivate::sendCallOutEvent(QFutureCallOutEvent *event)
emit q->started();
break;
case QFutureCallOutEvent::Finished:
- finished = true;
emit q->finished();
break;
case QFutureCallOutEvent::Canceled:
@@ -595,7 +552,7 @@ QT_WARNING_POP
}
-/*! \fn template <typename T> const T &QFutureWatcher<T>::result() const
+/*! \fn template <typename T> template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>> const T &QFutureWatcher<T>::result() const
Returns the first result in the future(). If the result is not immediately
available, this function will block and wait for the result to become
@@ -604,7 +561,7 @@ QT_WARNING_POP
\sa resultAt()
*/
-/*! \fn template <typename T> const T &QFutureWatcher<T>::resultAt(int index) const
+/*! \fn template <typename T> template<typename U = T, typename = QtPrivate::EnableForNonVoid<U>> const T &QFutureWatcher<T>::resultAt(int index) const
Returns the result at \a index in the future(). If the result is not
immediately available, this function will block and wait for the result to
@@ -617,9 +574,15 @@ QT_WARNING_POP
Starts watching the given \a future.
- One of the signals might be emitted for the current state of the
- \a future. For example, if the future is already stopped, the
- finished signal will be emitted.
+ If \a future has already started, the watcher will initially emit signals
+ that bring their listeners up to date about the future's state. The
+ following signals will, if applicable, be emitted in the given order:
+ started(), progressRangeChanged(), progressValueChanged(),
+ progressTextChanged(), resultsReadyAt(), resultReadyAt(), suspending(),
+ suspended(), canceled(), and finished(). Of these, resultsReadyAt() and
+ resultReadyAt() may be emitted several times to cover all available
+ results. progressValueChanged() and progressTextChanged() will only be
+ emitted once for the latest available progress value and text.
To avoid a race condition, it is important to call this function
\e after doing the connections.
@@ -665,8 +628,7 @@ QT_WARNING_POP
#if QT_DEPRECATED_SINCE(6, 0)
/*! \fn template <typename T> void QFutureWatcher<T>::paused()
- \obsolete
- Use suspending() instead.
+ \deprecated [6.0] Use suspending() instead.
This signal is emitted when the state of the watched future is
set to paused.
@@ -677,7 +639,7 @@ QT_WARNING_POP
still be delivered. To to be informed when pause() actually
took effect, use the suspended() signal.
- \sa setPaused(), pause(), suspended()
+ \sa setSuspended(), suspend(), suspended()
*/
#endif // QT_DEPRECATED_SINCE(6, 0)
diff --git a/src/corelib/thread/qfuturewatcher.h b/src/corelib/thread/qfuturewatcher.h
index d2b8eeace1..89c5b408fa 100644
--- a/src/corelib/thread/qfuturewatcher.h
+++ b/src/corelib/thread/qfuturewatcher.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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) 2020 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 QFUTUREWATCHER_H
#define QFUTUREWATCHER_H
@@ -131,12 +95,6 @@ private:
virtual QFutureInterfaceBase &futureInterface() = 0;
};
-namespace QtPrivate {
-
-template<class T>
-using EnableForNonVoid = std::enable_if_t<!std::is_same_v<T, void>>;
-}
-
template <typename T>
class QFutureWatcher : public QFutureWatcherBase
{
@@ -216,7 +174,7 @@ private:
template <typename T>
Q_INLINE_TEMPLATE void QFutureWatcher<T>::setFuture(const QFuture<T> &_future)
{
- if (_future == m_future)
+ if (_future.d == m_future.d)
return;
disconnectOutputInterface(true);
diff --git a/src/corelib/thread/qfuturewatcher_p.h b/src/corelib/thread/qfuturewatcher_p.h
index b086b88773..6d6bf29774 100644
--- a/src/corelib/thread/qfuturewatcher_p.h
+++ b/src/corelib/thread/qfuturewatcher_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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 QFUTUREWATCHER_P_H
#define QFUTUREWATCHER_P_H
@@ -78,7 +42,6 @@ public:
int maximumPendingResultsReady;
QAtomicInt resultAtConnected;
- bool finished;
};
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qgenericatomic.h b/src/corelib/thread/qgenericatomic.h
index e9e5f3c74b..91ccbbd1cd 100644
--- a/src/corelib/thread/qgenericatomic.h
+++ b/src/corelib/thread/qgenericatomic.h
@@ -1,48 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 Thiago Macieira <thiago@kde.org>
-** Copyright (C) 2016 Intel Corporation.
-** 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) 2011 Thiago Macieira <thiago@kde.org>
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGENERICATOMIC_H
#define QGENERICATOMIC_H
-#include <QtCore/qglobal.h>
-#include <QtCore/qtypeinfo.h>
+#include <QtCore/qcompilerdetection.h>
+#include <QtCore/qtconfigmacros.h>
+#include <QtCore/qtypes.h>
QT_BEGIN_NAMESPACE
@@ -53,8 +18,10 @@ QT_END_NAMESPACE
#pragma qt_sync_stop_processing
#endif
-template<int> struct QAtomicOpsSupport { enum { IsSupported = 0 }; };
-template<> struct QAtomicOpsSupport<4> { enum { IsSupported = 1 }; };
+template<int Size> struct QAtomicOpsSupport
+{
+ enum { IsSupported = (Size == sizeof(int) || Size == sizeof(qptrdiff)) };
+};
template <typename T> struct QAtomicAdditiveType
{
@@ -67,335 +34,5 @@ template <typename T> struct QAtomicAdditiveType<T *>
static const int AddScale = sizeof(T);
};
-// not really atomic...
-template <typename BaseClass> struct QGenericAtomicOps
-{
- template <typename T> struct AtomicType { typedef T Type; typedef T *PointerType; };
-
- template <typename T> static void acquireMemoryFence(const T &_q_value) noexcept
- {
- BaseClass::orderedMemoryFence(_q_value);
- }
- template <typename T> static void releaseMemoryFence(const T &_q_value) noexcept
- {
- BaseClass::orderedMemoryFence(_q_value);
- }
- template <typename T> static void orderedMemoryFence(const T &) noexcept
- {
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T load(const T &_q_value) noexcept
- {
- return _q_value;
- }
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- void store(T &_q_value, X newValue) noexcept
- {
- _q_value = newValue;
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T loadRelaxed(const T &_q_value) noexcept
- {
- return _q_value;
- }
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- void storeRelaxed(T &_q_value, X newValue) noexcept
- {
- _q_value = newValue;
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T loadAcquire(const T &_q_value) noexcept
- {
- T tmp = *static_cast<const volatile T *>(&_q_value);
- BaseClass::acquireMemoryFence(_q_value);
- return tmp;
- }
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- void storeRelease(T &_q_value, X newValue) noexcept
- {
- BaseClass::releaseMemoryFence(_q_value);
- *static_cast<volatile T *>(&_q_value) = newValue;
- }
-
- static inline Q_DECL_CONSTEXPR bool isReferenceCountingNative() noexcept
- { return BaseClass::isFetchAndAddNative(); }
- static inline Q_DECL_CONSTEXPR bool isReferenceCountingWaitFree() noexcept
- { return BaseClass::isFetchAndAddWaitFree(); }
- template <typename T> static Q_ALWAYS_INLINE
- bool ref(T &_q_value) noexcept
- {
- return BaseClass::fetchAndAddRelaxed(_q_value, 1) != T(-1);
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- bool deref(T &_q_value) noexcept
- {
- return BaseClass::fetchAndAddRelaxed(_q_value, -1) != 1;
- }
-
-#if 0
- // These functions have no default implementation
- // Archictectures must implement them
- static inline Q_DECL_CONSTEXPR bool isTestAndSetNative() noexcept;
- static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() noexcept;
- template <typename T, typename X> static inline
- bool testAndSetRelaxed(T &_q_value, X expectedValue, X newValue) noexcept;
- template <typename T, typename X> static inline
- bool testAndSetRelaxed(T &_q_value, X expectedValue, X newValue, X *currentValue) noexcept;
-#endif
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- bool testAndSetAcquire(T &_q_value, X expectedValue, X newValue) noexcept
- {
- bool tmp = BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue);
- BaseClass::acquireMemoryFence(_q_value);
- return tmp;
- }
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- bool testAndSetRelease(T &_q_value, X expectedValue, X newValue) noexcept
- {
- BaseClass::releaseMemoryFence(_q_value);
- return BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue);
- }
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- bool testAndSetOrdered(T &_q_value, X expectedValue, X newValue) noexcept
- {
- BaseClass::orderedMemoryFence(_q_value);
- return BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue);
- }
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- bool testAndSetAcquire(T &_q_value, X expectedValue, X newValue, X *currentValue) noexcept
- {
- bool tmp = BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue, currentValue);
- BaseClass::acquireMemoryFence(_q_value);
- return tmp;
- }
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- bool testAndSetRelease(T &_q_value, X expectedValue, X newValue, X *currentValue) noexcept
- {
- BaseClass::releaseMemoryFence(_q_value);
- return BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue, currentValue);
- }
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- bool testAndSetOrdered(T &_q_value, X expectedValue, X newValue, X *currentValue) noexcept
- {
- BaseClass::orderedMemoryFence(_q_value);
- return BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue, currentValue);
- }
-
- static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() noexcept { return false; }
- static inline Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() noexcept { return false; }
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- T fetchAndStoreRelaxed(T &_q_value, X newValue) noexcept
- {
- // implement fetchAndStore on top of testAndSet
- Q_FOREVER {
- T tmp = loadRelaxed(_q_value);
- if (BaseClass::testAndSetRelaxed(_q_value, tmp, newValue))
- return tmp;
- }
- }
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- T fetchAndStoreAcquire(T &_q_value, X newValue) noexcept
- {
- T tmp = BaseClass::fetchAndStoreRelaxed(_q_value, newValue);
- BaseClass::acquireMemoryFence(_q_value);
- return tmp;
- }
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- T fetchAndStoreRelease(T &_q_value, X newValue) noexcept
- {
- BaseClass::releaseMemoryFence(_q_value);
- return BaseClass::fetchAndStoreRelaxed(_q_value, newValue);
- }
-
- template <typename T, typename X> static Q_ALWAYS_INLINE
- T fetchAndStoreOrdered(T &_q_value, X newValue) noexcept
- {
- BaseClass::orderedMemoryFence(_q_value);
- return BaseClass::fetchAndStoreRelaxed(_q_value, newValue);
- }
-
- static inline Q_DECL_CONSTEXPR bool isFetchAndAddNative() noexcept { return false; }
- static inline Q_DECL_CONSTEXPR bool isFetchAndAddWaitFree() noexcept { return false; }
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndAddRelaxed(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd) noexcept
- {
- // implement fetchAndAdd on top of testAndSet
- Q_FOREVER {
- T tmp = BaseClass::loadRelaxed(_q_value);
- if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp + valueToAdd)))
- return tmp;
- }
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndAddAcquire(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd) noexcept
- {
- T tmp = BaseClass::fetchAndAddRelaxed(_q_value, valueToAdd);
- BaseClass::acquireMemoryFence(_q_value);
- return tmp;
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndAddRelease(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd) noexcept
- {
- BaseClass::releaseMemoryFence(_q_value);
- return BaseClass::fetchAndAddRelaxed(_q_value, valueToAdd);
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndAddOrdered(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT valueToAdd) noexcept
- {
- BaseClass::orderedMemoryFence(_q_value);
- return BaseClass::fetchAndAddRelaxed(_q_value, valueToAdd);
- }
-
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_MSVC(4146) // unary minus operator applied to unsigned type, result still unsigned
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndSubRelaxed(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT operand) noexcept
- {
- // implement fetchAndSub on top of fetchAndAdd
- return fetchAndAddRelaxed(_q_value, -operand);
- }
-QT_WARNING_POP
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndSubAcquire(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT operand) noexcept
- {
- T tmp = BaseClass::fetchAndSubRelaxed(_q_value, operand);
- BaseClass::acquireMemoryFence(_q_value);
- return tmp;
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndSubRelease(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT operand) noexcept
- {
- BaseClass::releaseMemoryFence(_q_value);
- return BaseClass::fetchAndSubRelaxed(_q_value, operand);
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndSubOrdered(T &_q_value, typename QAtomicAdditiveType<T>::AdditiveT operand) noexcept
- {
- BaseClass::orderedMemoryFence(_q_value);
- return BaseClass::fetchAndSubRelaxed(_q_value, operand);
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndAndRelaxed(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
- {
- // implement fetchAndAnd on top of testAndSet
- T tmp = BaseClass::loadRelaxed(_q_value);
- Q_FOREVER {
- if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp & operand), &tmp))
- return tmp;
- }
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndAndAcquire(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
- {
- T tmp = BaseClass::fetchAndAndRelaxed(_q_value, operand);
- BaseClass::acquireMemoryFence(_q_value);
- return tmp;
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndAndRelease(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
- {
- BaseClass::releaseMemoryFence(_q_value);
- return BaseClass::fetchAndAndRelaxed(_q_value, operand);
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndAndOrdered(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
- {
- BaseClass::orderedMemoryFence(_q_value);
- return BaseClass::fetchAndAndRelaxed(_q_value, operand);
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndOrRelaxed(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
- {
- // implement fetchAndOr on top of testAndSet
- T tmp = BaseClass::loadRelaxed(_q_value);
- Q_FOREVER {
- if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp | operand), &tmp))
- return tmp;
- }
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndOrAcquire(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
- {
- T tmp = BaseClass::fetchAndOrRelaxed(_q_value, operand);
- BaseClass::acquireMemoryFence(_q_value);
- return tmp;
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndOrRelease(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
- {
- BaseClass::releaseMemoryFence(_q_value);
- return BaseClass::fetchAndOrRelaxed(_q_value, operand);
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndOrOrdered(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
- {
- BaseClass::orderedMemoryFence(_q_value);
- return BaseClass::fetchAndOrRelaxed(_q_value, operand);
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndXorRelaxed(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
- {
- // implement fetchAndXor on top of testAndSet
- T tmp = BaseClass::loadRelaxed(_q_value);
- Q_FOREVER {
- if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp ^ operand), &tmp))
- return tmp;
- }
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndXorAcquire(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
- {
- T tmp = BaseClass::fetchAndXorRelaxed(_q_value, operand);
- BaseClass::acquireMemoryFence(_q_value);
- return tmp;
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndXorRelease(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
- {
- BaseClass::releaseMemoryFence(_q_value);
- return BaseClass::fetchAndXorRelaxed(_q_value, operand);
- }
-
- template <typename T> static Q_ALWAYS_INLINE
- T fetchAndXorOrdered(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
- {
- BaseClass::orderedMemoryFence(_q_value);
- return BaseClass::fetchAndXorRelaxed(_q_value, operand);
- }
-};
-
QT_END_NAMESPACE
#endif // QGENERICATOMIC_H
diff --git a/src/corelib/thread/qlocking_p.h b/src/corelib/thread/qlocking_p.h
index 9a796cf7f7..9fa7e70da9 100644
--- a/src/corelib/thread/qlocking_p.h
+++ b/src/corelib/thread/qlocking_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
-** 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) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QLOCKING_P_H
#define QLOCKING_P_H
@@ -44,15 +8,15 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of qmutex.cpp, qmutex_unix.cpp, and qmutex_win.cpp. This header
-// file may change from version to version without notice, or even be
-// removed.
+// This file is not part of the Qt API. It exists for the convenience of
+// qmutex.cpp and qmutex_unix.cpp. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qmutex.h>
+#include <QtCore/private/qglobal_p.h>
#include <mutex>
@@ -84,15 +48,11 @@ QT_BEGIN_NAMESPACE
namespace {
template <typename Mutex, typename Lock =
-#if defined(__cpp_guaranteed_copy_elision) && __cpp_guaranteed_copy_elision >= 201606L
# if defined(__cpp_lib_scoped_lock) && __cpp_lib_scoped_lock >= 201703L
std::scoped_lock
# else
std::lock_guard
# endif
-#else
- std::unique_lock
-#endif
<typename std::decay<Mutex>::type>
>
Lock qt_scoped_lock(Mutex &mutex)
diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp
index 9bfd50f2d9..ec6c711a4f 100644
--- a/src/corelib/thread/qmutex.cpp
+++ b/src/corelib/thread/qmutex.cpp
@@ -1,90 +1,29 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Copyright (C) 2012 Olivier Goffart <ogoffart@woboq.com>
-** 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.
+// Copyright (C) 2016 Intel Corporation.
+// Copyright (C) 2012 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "global/qglobal.h"
#include "qplatformdefs.h"
#include "qmutex.h"
#include <qdebug.h>
#include "qatomic.h"
-#include "qelapsedtimer.h"
+#include "qfutex_p.h"
#include "qthread.h"
#include "qmutex_p.h"
-#ifndef QT_LINUX_FUTEX
+#ifndef QT_ALWAYS_USE_FUTEX
#include "private/qfreelist_p.h"
#endif
QT_BEGIN_NAMESPACE
-static inline bool isRecursive(QMutexData *d)
+using namespace QtFutex;
+static inline QMutexPrivate *dummyFutexValue()
{
- quintptr u = quintptr(d);
- if (Q_LIKELY(u <= 0x3))
- return false;
-#ifdef QT_LINUX_FUTEX
- Q_ASSERT(d->recursive);
- return true;
-#else
- return d->recursive;
-#endif
+ return reinterpret_cast<QMutexPrivate *>(quintptr(3));
}
-class QRecursiveMutexPrivate : public QMutexData
-{
-public:
- QRecursiveMutexPrivate()
- : QMutexData(QMutex::Recursive), owner(nullptr), count(0) {}
-
- // written to by the thread that first owns 'mutex';
- // read during attempts to acquire ownership of 'mutex' from any other thread:
- QAtomicPointer<std::remove_pointer<Qt::HANDLE>::type> owner;
-
- // only ever accessed from the thread that owns 'mutex':
- uint count;
-
- QMutex mutex;
-
- bool lock(int timeout) QT_MUTEX_LOCK_NOEXCEPT;
- void unlock() noexcept;
-};
-
/*
\class QBasicMutex
\inmodule QtCore
@@ -142,8 +81,8 @@ public:
lock calls unlock(). A non-blocking alternative to lock() is
tryLock().
- QMutex is optimized to be fast in the non-contended case. A non-recursive
- QMutex will not allocate memory if there is no contention on that mutex.
+ QMutex is optimized to be fast in the non-contended case. It
+ will not allocate memory if there is no contention on that mutex.
It is constructed and destroyed with almost no overhead,
which means it is fine to have many mutexes as part of other classes.
@@ -151,64 +90,28 @@ public:
*/
/*!
- \enum QMutex::RecursionMode
-
- \value Recursive In this mode, a thread can lock the same mutex
- multiple times and the mutex won't be unlocked
- until a corresponding number of unlock() calls
- have been made. You should use QRecursiveMutex
- for this use-case.
-
- \value NonRecursive In this mode, a thread may only lock a mutex
- once.
-
- \sa QMutex(), QRecursiveMutex
-*/
-
-/*!
\fn QMutex::QMutex()
Constructs a new mutex. The mutex is created in an unlocked state.
*/
-/*!
- Constructs a new mutex. The mutex is created in an unlocked state.
+/*! \fn QMutex::~QMutex()
- If \a mode is QMutex::Recursive, a thread can lock the same mutex
- multiple times and the mutex won't be unlocked until a
- corresponding number of unlock() calls have been made. Otherwise
- a thread may only lock a mutex once. The default is
- QMutex::NonRecursive.
-
- Recursive mutexes are slower and take more memory than non-recursive ones.
-
- \sa lock(), unlock()
-*/
-QMutex::QMutex(RecursionMode mode)
-{
- d_ptr.storeRelaxed(mode == Recursive ? new QRecursiveMutexPrivate : nullptr);
-}
-
-/*!
Destroys the mutex.
\warning Destroying a locked mutex may result in undefined behavior.
*/
-QMutex::~QMutex()
+void QBasicMutex::destroyInternal(QMutexPrivate *d)
{
- QMutexData *d = d_ptr.loadRelaxed();
- if (isRecursive()) {
- delete static_cast<QRecursiveMutexPrivate *>(d);
- } else if (d) {
-#ifndef QT_LINUX_FUTEX
- if (d != dummyLocked() && static_cast<QMutexPrivate *>(d)->possiblyUnlocked.loadRelaxed()
- && tryLock()) {
+ if (!d)
+ return;
+ if (!futexAvailable()) {
+ if (d != dummyLocked() && d->possiblyUnlocked.loadRelaxed() && tryLock()) {
unlock();
return;
}
-#endif
- qWarning("QMutex: destroying locked mutex");
}
+ qWarning("QMutex: destroying locked mutex");
}
/*! \fn void QMutex::lock()
@@ -217,23 +120,10 @@ QMutex::~QMutex()
call will block until that thread has unlocked it.
Calling this function multiple times on the same mutex from the
- same thread is allowed if this mutex is a
- \l{QRecursiveMutex}{recursive mutex}. If this mutex is a
- \l{QMutex}{non-recursive mutex}, this function will
- \e dead-lock when the mutex is locked recursively.
+ same thread will cause a \e dead-lock.
\sa unlock()
*/
-void QMutex::lock() QT_MUTEX_LOCK_NOEXCEPT
-{
- QMutexData *current;
- if (fastTryLock(current))
- return;
- if (QT_PREPEND_NAMESPACE(isRecursive)(current))
- static_cast<QRecursiveMutexPrivate *>(current)->lock(-1);
- else
- lockInternal();
-}
/*! \fn bool QMutex::tryLock(int timeout)
@@ -250,24 +140,42 @@ void QMutex::lock() QT_MUTEX_LOCK_NOEXCEPT
before another thread can successfully lock it.
Calling this function multiple times on the same mutex from the
- same thread is allowed if this mutex is a
- \l{QRecursiveMutex}{recursive mutex}. If this mutex is a
- \l{QMutex}{non-recursive mutex}, this function will
- \e always return false when attempting to lock the mutex
- recursively.
+ same thread will cause a \e dead-lock.
+
+ \sa lock(), unlock()
+*/
+
+/*! \fn bool QMutex::tryLock(QDeadlineTimer timer)
+ \since 6.6
+
+ Attempts to lock the mutex. This function returns \c true if the lock
+ was obtained; otherwise it returns \c false. If another thread has
+ locked the mutex, this function will wait until \a timer expires
+ for the mutex to become available.
+
+ If the lock was obtained, the mutex must be unlocked with unlock()
+ before another thread can successfully lock it.
+
+ Calling this function multiple times on the same mutex from the
+ same thread will cause a \e dead-lock.
+
+ \sa lock(), unlock()
+*/
+
+/*! \fn bool QMutex::tryLock()
+ \overload
+
+ Attempts to lock the mutex. This function returns \c true if the lock
+ was obtained; otherwise it returns \c false.
+
+ If the lock was obtained, the mutex must be unlocked with unlock()
+ before another thread can successfully lock it.
+
+ Calling this function multiple times on the same mutex from the
+ same thread will cause a \e dead-lock.
\sa lock(), unlock()
*/
-bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
-{
- QMutexData *current;
- if (fastTryLock(current))
- return true;
- if (QT_PREPEND_NAMESPACE(isRecursive)(current))
- return static_cast<QRecursiveMutexPrivate *>(current)->lock(timeout);
- else
- return lockInternal(timeout);
-}
/*! \fn bool QMutex::try_lock()
\since 5.8
@@ -277,9 +185,6 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
This function is provided for compatibility with the Standard Library
concept \c Lockable. It is equivalent to tryLock().
-
- The function returns \c true if the lock was obtained; otherwise it
- returns \c false
*/
/*! \fn template <class Rep, class Period> bool QMutex::try_lock_for(std::chrono::duration<Rep, Period> duration)
@@ -297,11 +202,7 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
before another thread can successfully lock it.
Calling this function multiple times on the same mutex from the
- same thread is allowed if this mutex is a
- \l{QRecursiveMutex}{recursive mutex}. If this mutex is a
- \l{QMutex}{non-recursive mutex}, this function will
- \e always return false when attempting to lock the mutex
- recursively.
+ same thread will cause a \e dead-lock.
\sa lock(), unlock()
*/
@@ -321,11 +222,7 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
before another thread can successfully lock it.
Calling this function multiple times on the same mutex from the
- same thread is allowed if this mutex is a
- \l{QRecursiveMutex}{recursive mutex}. If this mutex is a
- \l{QMutex}{non-recursive mutex}, this function will
- \e always return false when attempting to lock the mutex
- recursively.
+ same thread will cause a \e dead-lock.
\sa lock(), unlock()
*/
@@ -338,39 +235,6 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
\sa lock()
*/
-void QMutex::unlock() noexcept
-{
- QMutexData *current;
- if (fastTryUnlock(current))
- return;
- if (QT_PREPEND_NAMESPACE(isRecursive)(current))
- static_cast<QRecursiveMutexPrivate *>(current)->unlock();
- else
- unlockInternal();
-}
-
-
-/*!
- \fn bool QMutex::isRecursive() const
- \since 5.7
-
- Returns \c true if the mutex is recursive.
-*/
-
-bool QBasicMutex::isRecursive() noexcept
-{
- return QT_PREPEND_NAMESPACE(isRecursive)(d_ptr.loadAcquire());
-}
-
-/*!
- \since 5.7
-
- Returns \c true if the mutex is recursive.
-*/
-bool QBasicMutex::isRecursive() const noexcept
-{
- return QT_PREPEND_NAMESPACE(isRecursive)(d_ptr.loadAcquire());
-}
/*!
\class QRecursiveMutex
@@ -403,16 +267,12 @@ bool QBasicMutex::isRecursive() const noexcept
\sa QMutex, QMutexLocker, QReadWriteLock, QSemaphore, QWaitCondition
*/
-/*!
+/*! \fn QRecursiveMutex::QRecursiveMutex()
+
Constructs a new recursive mutex. The mutex is created in an unlocked state.
\sa lock(), unlock()
*/
-QRecursiveMutex::QRecursiveMutex()
- : QMutex()
-{
- d_ptr.storeRelaxed(new QRecursiveMutexPrivate);
-}
/*!
Destroys the mutex.
@@ -421,9 +281,158 @@ QRecursiveMutex::QRecursiveMutex()
*/
QRecursiveMutex::~QRecursiveMutex()
{
- delete static_cast<QRecursiveMutexPrivate*>(d_ptr.fetchAndStoreAcquire(nullptr));
}
+/*! \fn void QRecursiveMutex::lock()
+
+ Locks the mutex. If another thread has locked the mutex then this
+ call will block until that thread has unlocked it.
+
+ Calling this function multiple times on the same mutex from the
+ same thread is allowed.
+
+ \sa unlock()
+*/
+
+/*!
+ \fn QRecursiveMutex::tryLock(int timeout)
+
+ Attempts to lock the mutex. This function returns \c true if the lock
+ was obtained; otherwise it returns \c false. If another thread has
+ locked the mutex, this function will wait for at most \a timeout
+ milliseconds for the mutex to become available.
+
+ Note: Passing a negative number as the \a timeout is equivalent to
+ calling lock(), i.e. this function will wait forever until mutex
+ can be locked if \a timeout is negative.
+
+ If the lock was obtained, the mutex must be unlocked with unlock()
+ before another thread can successfully lock it.
+
+ Calling this function multiple times on the same mutex from the
+ same thread is allowed.
+
+ \sa lock(), unlock()
+*/
+
+/*!
+ \since 6.6
+
+ Attempts to lock the mutex. This function returns \c true if the lock
+ was obtained; otherwise it returns \c false. If another thread has
+ locked the mutex, this function will wait until \a timeout expires
+ for the mutex to become available.
+
+ If the lock was obtained, the mutex must be unlocked with unlock()
+ before another thread can successfully lock it.
+
+ Calling this function multiple times on the same mutex from the
+ same thread is allowed.
+
+ \sa lock(), unlock()
+*/
+bool QRecursiveMutex::tryLock(QDeadlineTimer timeout) QT_MUTEX_LOCK_NOEXCEPT
+{
+ unsigned tsanFlags = QtTsan::MutexWriteReentrant | QtTsan::TryLock;
+ QtTsan::mutexPreLock(this, tsanFlags);
+
+ Qt::HANDLE self = QThread::currentThreadId();
+ if (owner.loadRelaxed() == self) {
+ ++count;
+ Q_ASSERT_X(count != 0, "QMutex::lock", "Overflow in recursion counter");
+ QtTsan::mutexPostLock(this, tsanFlags, 0);
+ return true;
+ }
+ bool success = true;
+ if (timeout.isForever()) {
+ mutex.lock();
+ } else {
+ success = mutex.tryLock(timeout);
+ }
+
+ if (success)
+ owner.storeRelaxed(self);
+ else
+ tsanFlags |= QtTsan::TryLockFailed;
+
+ QtTsan::mutexPostLock(this, tsanFlags, 0);
+
+ return success;
+}
+
+/*! \fn bool QRecursiveMutex::try_lock()
+ \since 5.8
+
+ Attempts to lock the mutex. This function returns \c true if the lock
+ was obtained; otherwise it returns \c false.
+
+ This function is provided for compatibility with the Standard Library
+ concept \c Lockable. It is equivalent to tryLock().
+*/
+
+/*! \fn template <class Rep, class Period> bool QRecursiveMutex::try_lock_for(std::chrono::duration<Rep, Period> duration)
+ \since 5.8
+
+ Attempts to lock the mutex. This function returns \c true if the lock
+ was obtained; otherwise it returns \c false. If another thread has
+ locked the mutex, this function will wait for at least \a duration
+ for the mutex to become available.
+
+ Note: Passing a negative duration as the \a duration is equivalent to
+ calling try_lock(). This behavior differs from tryLock().
+
+ If the lock was obtained, the mutex must be unlocked with unlock()
+ before another thread can successfully lock it.
+
+ Calling this function multiple times on the same mutex from the
+ same thread is allowed.
+
+ \sa lock(), unlock()
+*/
+
+/*! \fn template<class Clock, class Duration> bool QRecursiveMutex::try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
+ \since 5.8
+
+ Attempts to lock the mutex. This function returns \c true if the lock
+ was obtained; otherwise it returns \c false. If another thread has
+ locked the mutex, this function will wait at least until \a timePoint
+ for the mutex to become available.
+
+ Note: Passing a \a timePoint which has already passed is equivalent
+ to calling try_lock(). This behavior differs from tryLock().
+
+ If the lock was obtained, the mutex must be unlocked with unlock()
+ before another thread can successfully lock it.
+
+ Calling this function multiple times on the same mutex from the
+ same thread is allowed.
+
+ \sa lock(), unlock()
+*/
+
+/*!
+ Unlocks the mutex. Attempting to unlock a mutex in a different
+ thread to the one that locked it results in an error. Unlocking a
+ mutex that is not locked results in undefined behavior.
+
+ \sa lock()
+*/
+void QRecursiveMutex::unlock() noexcept
+{
+ Q_ASSERT(owner.loadRelaxed() == QThread::currentThreadId());
+ QtTsan::mutexPreUnlock(this, 0u);
+
+ if (count > 0) {
+ count--;
+ } else {
+ owner.storeRelaxed(nullptr);
+ mutex.unlock();
+ }
+
+ QtTsan::mutexPostUnlock(this, 0u);
+}
+
+
/*!
\class QMutexLocker
\inmodule QtCore
@@ -434,7 +443,7 @@ QRecursiveMutex::~QRecursiveMutex()
\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
difficult to debug. QMutexLocker can be used in such situations
to ensure that the state of the mutex is always well-defined.
@@ -478,7 +487,7 @@ QRecursiveMutex::~QRecursiveMutex()
*/
/*!
- \fn QMutexLocker::QMutexLocker(QMutex *mutex)
+ \fn template <typename Mutex> QMutexLocker<Mutex>::QMutexLocker(Mutex *mutex) noexcept
Constructs a QMutexLocker and locks \a mutex. The mutex will be
unlocked when the QMutexLocker is destroyed. If \a mutex is \nullptr,
@@ -488,18 +497,41 @@ QRecursiveMutex::~QRecursiveMutex()
*/
/*!
- \fn QMutexLocker::QMutexLocker(QRecursiveMutex *mutex)
- \since 5.14
+ \fn template <typename Mutex> QMutexLocker<Mutex>::QMutexLocker(QMutexLocker &&other) noexcept
+ \since 6.4
- Constructs a QMutexLocker and locks \a mutex. The mutex will be
- unlocked (unlock() called) when the QMutexLocker is destroyed.
- If \a mutex is \nullptr, QMutexLocker does nothing.
+ Move-constructs a QMutexLocker from \a other. The mutex and the
+ state of \a other is transferred to the newly constructed instance.
+ After the move, \a other will no longer be managing any mutex.
\sa QMutex::lock()
*/
/*!
- \fn QMutexLocker::~QMutexLocker()
+ \fn template <typename Mutex> QMutexLocker<Mutex> &QMutexLocker<Mutex>::operator=(QMutexLocker &&other) noexcept
+ \since 6.4
+
+ Move-assigns \a other onto this QMutexLocker. If this QMutexLocker
+ was holding a locked mutex before the assignment, the mutex will be
+ unlocked. The mutex and the state of \a other is then transferred
+ to this QMutexLocker. After the move, \a other will no longer be
+ managing any mutex.
+
+ \sa QMutex::lock()
+*/
+
+/*!
+ \fn template <typename Mutex> void QMutexLocker<Mutex>::swap(QMutexLocker &other) noexcept
+ \since 6.4
+
+ Swaps the mutex and the state of this QMutexLocker with \a other.
+ This operation is very fast and never fails.
+
+ \sa QMutex::lock()
+*/
+
+/*!
+ \fn template <typename Mutex> QMutexLocker<Mutex>::~QMutexLocker() noexcept
Destroys the QMutexLocker and unlocks the mutex that was locked
in the constructor.
@@ -508,7 +540,15 @@ QRecursiveMutex::~QRecursiveMutex()
*/
/*!
- \fn void QMutexLocker::unlock()
+ \fn template <typename Mutex> bool QMutexLocker<Mutex>::isLocked() const noexcept
+ \since 6.4
+
+ Returns true if this QMutexLocker is currently locking its associated
+ mutex, or false otherwise.
+*/
+
+/*!
+ \fn template <typename Mutex> void QMutexLocker<Mutex>::unlock() noexcept
Unlocks this mutex locker. You can use \c relock() to lock
it again. It does not need to be locked when destroyed.
@@ -517,7 +557,7 @@ QRecursiveMutex::~QRecursiveMutex()
*/
/*!
- \fn void QMutexLocker::relock()
+ \fn template <typename Mutex> void QMutexLocker<Mutex>::relock() noexcept
Relocks an unlocked mutex locker.
@@ -525,14 +565,12 @@ QRecursiveMutex::~QRecursiveMutex()
*/
/*!
- \fn QMutex *QMutexLocker::mutex() const
+ \fn template <typename Mutex> QMutex *QMutexLocker<Mutex>::mutex() const
Returns the mutex on which the QMutexLocker is operating.
*/
-#ifndef QT_LINUX_FUTEX //linux implementation is in qmutex_linux.cpp
-
/*
For a rough introduction on how this works, refer to
http://woboq.com/blog/internals-of-qmutex-in-qt5.html
@@ -553,28 +591,125 @@ QRecursiveMutex::~QRecursiveMutex()
possiblyUnlocked flag.
*/
+/*
+ * QBasicMutex implementation with futexes (Linux, Windows 10)
+ *
+ * QBasicMutex contains one pointer value, which can contain one of four
+ * different values:
+ * 0x0 unlocked
+ * 0x1 locked, no waiters
+ * 0x3 locked, at least one waiter
+ *
+ * LOCKING:
+ *
+ * A starts in the 0x0 state, indicating that it's unlocked. When the first
+ * thread attempts to lock it, it will perform a testAndSetAcquire
+ * from 0x0 to 0x1. If that succeeds, the caller concludes that it
+ * successfully locked the mutex. That happens in fastTryLock().
+ *
+ * If that testAndSetAcquire fails, QBasicMutex::lockInternal is called.
+ *
+ * lockInternal will examine the value of the pointer. Otherwise, it will use
+ * futexes to sleep and wait for another thread to unlock. To do that, it needs
+ * to set a pointer value of 0x3, which indicates that thread is waiting. It
+ * does that by a simple fetchAndStoreAcquire operation.
+ *
+ * If the pointer value was 0x0, it means we succeeded in acquiring the mutex.
+ * For other values, it will then call FUTEX_WAIT and with an expected value of
+ * 0x3.
+ *
+ * If the pointer value changed before futex(2) managed to sleep, it will
+ * return -1 / EWOULDBLOCK, in which case we have to start over. And even if we
+ * are woken up directly by a FUTEX_WAKE, we need to acquire the mutex, so we
+ * start over again.
+ *
+ * UNLOCKING:
+ *
+ * To unlock, we need to set a value of 0x0 to indicate it's unlocked. The
+ * first attempt is a testAndSetRelease operation from 0x1 to 0x0. If that
+ * succeeds, we're done.
+ *
+ * If it fails, unlockInternal() is called. The only possibility is that the
+ * mutex value was 0x3, which indicates some other thread is waiting or was
+ * waiting in the past. We then set the mutex to 0x0 and perform a FUTEX_WAKE.
+ */
+
/*!
\internal helper for lock()
*/
void QBasicMutex::lockInternal() QT_MUTEX_LOCK_NOEXCEPT
{
- lockInternal(-1);
+ if (futexAvailable()) {
+ // note we must set to dummyFutexValue because there could be other threads
+ // also waiting
+ while (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) != nullptr) {
+ // successfully set the waiting bit, now sleep
+ futexWait(d_ptr, dummyFutexValue());
+
+ // we got woken up, so try to acquire the mutex
+ }
+ Q_ASSERT(d_ptr.loadRelaxed());
+ } else {
+ lockInternal(-1);
+ }
}
/*!
\internal helper for lock(int)
*/
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
{
- Q_ASSERT(!isRecursive());
+ if (timeout == 0)
+ return false;
+
+ return lockInternal(QDeadlineTimer(timeout));
+}
+#endif
+
+/*!
+ \internal helper for tryLock(QDeadlineTimer)
+ */
+bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXCEPT
+{
+ if (deadlineTimer.hasExpired())
+ return false;
+
+ if (futexAvailable()) {
+ if (Q_UNLIKELY(deadlineTimer.isForever())) {
+ lockInternal();
+ return true;
+ }
+
+ // The mutex is already locked, set a bit indicating we're waiting.
+ // Note we must set to dummyFutexValue because there could be other threads
+ // also waiting.
+ if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
+ return true;
+
+ for (;;) {
+ if (!futexWait(d_ptr, dummyFutexValue(), deadlineTimer))
+ return false;
+
+ // We got woken up, so must try to acquire the mutex. We must set
+ // to dummyFutexValue() again because there could be other threads
+ // waiting.
+ if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
+ return true;
+
+ if (deadlineTimer.hasExpired())
+ return false;
+ }
+ }
+#if !defined(QT_ALWAYS_USE_FUTEX)
while (!fastTryLock()) {
- QMutexData *copy = d_ptr.loadAcquire();
+ QMutexPrivate *copy = d_ptr.loadAcquire();
if (!copy) // if d is 0, the mutex is unlocked
continue;
if (copy == dummyLocked()) {
- if (timeout == 0)
+ if (deadlineTimer.hasExpired())
return false;
// The mutex is locked but does not have a QMutexPrivate yet.
// we need to allocate a QMutexPrivate
@@ -589,7 +724,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
}
QMutexPrivate *d = static_cast<QMutexPrivate *>(copy);
- if (timeout == 0 && !d->possiblyUnlocked.loadRelaxed())
+ if (deadlineTimer.hasExpired() && !d->possiblyUnlocked.loadRelaxed())
return false;
// At this point we have a pointer to a QMutexPrivate. But the other thread
@@ -597,7 +732,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
// We will try to reference it to avoid unlock to release it to the pool to make
// sure it won't be released. But if the refcount is already 0 it has been released.
if (!d->ref())
- continue; //that QMutexData was already released
+ continue; //that QMutexPrivate was already released
// We now hold a reference to the QMutexPrivate. It won't be released and re-used.
// But it is still possible that it was already re-used by another QMutex right before
@@ -613,7 +748,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
// is set to the BigNumber magic value set in unlockInternal()
int old_waiters;
do {
- old_waiters = d->waiters.loadRelaxed();
+ old_waiters = d->waiters.loadAcquire();
if (old_waiters == -QMutexPrivate::BigNumber) {
// we are unlocking, and the thread that unlocks is about to change d to 0
// we try to acquire the mutex by changing to dummyLocked()
@@ -642,7 +777,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
continue;
}
- if (d->wait(timeout)) {
+ if (d->wait(deadlineTimer)) {
// reset the possiblyUnlocked flag if needed (and deref its corresponding reference)
if (d->possiblyUnlocked.loadRelaxed() && d->possiblyUnlocked.testAndSetRelaxed(true, false))
d->deref();
@@ -651,8 +786,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
Q_ASSERT(d == d_ptr.loadRelaxed());
return true;
} else {
- Q_ASSERT(timeout >= 0);
- //timeout
+ // timed out
d->derefWaiters(1);
//There may be a race in which the mutex is unlocked right after we timed out,
// and before we deref the waiters, so maybe the mutex is actually unlocked.
@@ -667,6 +801,9 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
}
Q_ASSERT(d_ptr.loadRelaxed() != 0);
return true;
+#else
+ Q_UNREACHABLE();
+#endif
}
/*!
@@ -674,11 +811,16 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
*/
void QBasicMutex::unlockInternal() noexcept
{
- QMutexData *copy = d_ptr.loadAcquire();
+ QMutexPrivate *copy = d_ptr.loadAcquire();
Q_ASSERT(copy); //we must be locked
Q_ASSERT(copy != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed
- Q_ASSERT(!isRecursive());
+ if (futexAvailable()) {
+ d_ptr.storeRelease(nullptr);
+ return futexWakeOne(d_ptr);
+ }
+
+#if !defined(QT_ALWAYS_USE_FUTEX)
QMutexPrivate *d = reinterpret_cast<QMutexPrivate *>(copy);
// If no one is waiting for the lock anymore, we should reset d to 0x0.
@@ -700,15 +842,19 @@ void QBasicMutex::unlockInternal() noexcept
d->wakeUp();
}
d->deref();
+#else
+ Q_UNUSED(copy);
+#endif
}
+#if !defined(QT_ALWAYS_USE_FUTEX)
//The freelist management
namespace {
struct FreeListConstants : QFreeListDefaultConstants {
enum { BlockCount = 4, MaxIndex=0xffff };
static const int Sizes[BlockCount];
};
-const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = {
+Q_CONSTINIT const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = {
16,
128,
1024,
@@ -717,7 +863,7 @@ const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = {
typedef QFreeList<QMutexPrivate, FreeListConstants> FreeList;
// We cannot use Q_GLOBAL_STATIC because it uses QMutex
-static FreeList freeList_;
+Q_CONSTINIT static FreeList freeList_;
FreeList *freelist()
{
return &freeList_;
@@ -730,7 +876,6 @@ QMutexPrivate *QMutexPrivate::allocate()
QMutexPrivate *d = &(*freelist())[i];
d->id = i;
Q_ASSERT(d->refCount.loadRelaxed() == 0);
- Q_ASSERT(!d->recursive);
Q_ASSERT(!d->possiblyUnlocked.loadRelaxed());
Q_ASSERT(d->waiters.loadRelaxed() == 0);
d->refCount.storeRelaxed(1);
@@ -739,7 +884,6 @@ QMutexPrivate *QMutexPrivate::allocate()
void QMutexPrivate::release()
{
- Q_ASSERT(!recursive);
Q_ASSERT(refCount.loadRelaxed() == 0);
Q_ASSERT(!possiblyUnlocked.loadRelaxed());
Q_ASSERT(waiters.loadRelaxed() == 0);
@@ -762,50 +906,12 @@ void QMutexPrivate::derefWaiters(int value) noexcept
}
#endif
-/*!
- \internal
- */
-inline bool QRecursiveMutexPrivate::lock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
-{
- Qt::HANDLE self = QThread::currentThreadId();
- if (owner.loadRelaxed() == self) {
- ++count;
- Q_ASSERT_X(count != 0, "QMutex::lock", "Overflow in recursion counter");
- return true;
- }
- bool success = true;
- if (timeout == -1) {
- mutex.QBasicMutex::lock();
- } else {
- success = mutex.tryLock(timeout);
- }
-
- if (success)
- owner.storeRelaxed(self);
- return success;
-}
-
-/*!
- \internal
- */
-inline void QRecursiveMutexPrivate::unlock() noexcept
-{
- if (count > 0) {
- count--;
- } else {
- owner.storeRelaxed(nullptr);
- mutex.QBasicMutex::unlock();
- }
-}
-
QT_END_NAMESPACE
-#ifdef QT_LINUX_FUTEX
-# include "qmutex_linux.cpp"
-#elif defined(Q_OS_MAC)
+#if defined(QT_ALWAYS_USE_FUTEX)
+// nothing
+#elif defined(Q_OS_DARWIN)
# include "qmutex_mac.cpp"
-#elif defined(Q_OS_WIN)
-# include "qmutex_win.cpp"
#else
# include "qmutex_unix.cpp"
#endif
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 *&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
+};
- 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
diff --git a/src/corelib/thread/qmutex_linux.cpp b/src/corelib/thread/qmutex_linux.cpp
deleted file mode 100644
index 72002838cf..0000000000
--- a/src/corelib/thread/qmutex_linux.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** 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$
-**
-****************************************************************************/
-
-#include "qplatformdefs.h"
-#include "qmutex.h"
-#include "qatomic.h"
-#include "qmutex_p.h"
-#include "qfutex_p.h"
-
-#ifndef QT_ALWAYS_USE_FUTEX
-# error "Qt build is broken: qmutex_linux.cpp is being built but futex support is not wanted"
-#endif
-
-#ifndef FUTEX_PRIVATE_FLAG
-# define FUTEX_PRIVATE_FLAG 0
-#endif
-
-QT_BEGIN_NAMESPACE
-
-using namespace QtFutex;
-
-/*
- * QBasicMutex implementation on Linux with futexes
- *
- * QBasicMutex contains one pointer value, which can contain one of four
- * different values:
- * 0x0 unlocked, non-recursive mutex
- * 0x1 locked non-recursive mutex, no waiters
- * 0x3 locked non-recursive mutex, at least one waiter
- * > 0x3 recursive mutex, points to a QMutexPrivate object
- *
- * LOCKING (non-recursive):
- *
- * A non-recursive mutex starts in the 0x0 state, indicating that it's
- * unlocked. When the first thread attempts to lock it, it will perform a
- * testAndSetAcquire from 0x0 to 0x1. If that succeeds, the caller concludes
- * that it successfully locked the mutex. That happens in fastTryLock().
- *
- * If that testAndSetAcquire fails, QBasicMutex::lockInternal is called.
- *
- * lockInternal will examine the value of the pointer. Otherwise, it will use
- * futexes to sleep and wait for another thread to unlock. To do that, it needs
- * to set a pointer value of 0x3, which indicates that thread is waiting. It
- * does that by a simple fetchAndStoreAcquire operation.
- *
- * If the pointer value was 0x0, it means we succeeded in acquiring the mutex.
- * For other values, it will then call FUTEX_WAIT and with an expected value of
- * 0x3.
- *
- * If the pointer value changed before futex(2) managed to sleep, it will
- * return -1 / EWOULDBLOCK, in which case we have to start over. And even if we
- * are woken up directly by a FUTEX_WAKE, we need to acquire the mutex, so we
- * start over again.
- *
- * UNLOCKING (non-recursive):
- *
- * To unlock, we need to set a value of 0x0 to indicate it's unlocked. The
- * first attempt is a testAndSetRelease operation from 0x1 to 0x0. If that
- * succeeds, we're done.
- *
- * If it fails, unlockInternal() is called. The only possibility is that the
- * mutex value was 0x3, which indicates some other thread is waiting or was
- * waiting in the past. We then set the mutex to 0x0 and perform a FUTEX_WAKE.
- */
-
-static inline QMutexData *dummyFutexValue()
-{
- return reinterpret_cast<QMutexData *>(quintptr(3));
-}
-
-template <bool IsTimed> static inline
-bool lockInternal_helper(QBasicAtomicPointer<QMutexData> &d_ptr, int timeout = -1, QElapsedTimer *elapsedTimer = nullptr) noexcept
-{
- if (!IsTimed)
- timeout = -1;
-
- // we're here because fastTryLock() has just failed
- if (timeout == 0)
- return false;
-
- // the mutex is locked already, set a bit indicating we're waiting
- if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
- return true;
-
- qint64 nstimeout = timeout * Q_INT64_C(1000) * 1000;
- qint64 remainingTime = nstimeout;
- forever {
- // successfully set the waiting bit, now sleep
- if (IsTimed && nstimeout >= 0) {
- bool r = futexWait(d_ptr, dummyFutexValue(), remainingTime);
- if (!r)
- return false;
-
- // we got woken up, so try to acquire the mutex
- // note we must set to dummyFutexValue because there could be other threads
- // also waiting
- if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
- return true;
-
- // recalculate the timeout
- remainingTime = nstimeout - elapsedTimer->nsecsElapsed();
- if (remainingTime <= 0)
- return false;
- } else {
- futexWait(d_ptr, dummyFutexValue());
-
- // we got woken up, so try to acquire the mutex
- // note we must set to dummyFutexValue because there could be other threads
- // also waiting
- if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
- return true;
- }
- }
-
- Q_ASSERT(d_ptr.loadRelaxed());
- return true;
-}
-
-void QBasicMutex::lockInternal() noexcept
-{
- Q_ASSERT(!isRecursive());
- lockInternal_helper<false>(d_ptr);
-}
-
-bool QBasicMutex::lockInternal(int timeout) noexcept
-{
- Q_ASSERT(!isRecursive());
- QElapsedTimer elapsedTimer;
- elapsedTimer.start();
- return lockInternal_helper<true>(d_ptr, timeout, &elapsedTimer);
-}
-
-void QBasicMutex::unlockInternal() noexcept
-{
- QMutexData *d = d_ptr.loadRelaxed();
- Q_ASSERT(d); //we must be locked
- Q_ASSERT(d != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed
- Q_UNUSED(d);
- Q_ASSERT(!isRecursive());
-
- d_ptr.storeRelease(nullptr);
- futexWakeOne(d_ptr);
-}
-
-QT_END_NAMESPACE
diff --git a/src/corelib/thread/qmutex_mac.cpp b/src/corelib/thread/qmutex_mac.cpp
index 923f89f697..7849133e58 100644
--- a/src/corelib/thread/qmutex_mac.cpp
+++ b/src/corelib/thread/qmutex_mac.cpp
@@ -1,46 +1,12 @@
-/****************************************************************************
-**
-** 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
#include "qplatformdefs.h"
#include "qmutex.h"
#include "qmutex_p.h"
+#include "private/qcore_unix_p.h"
+
#include <mach/mach.h>
#include <mach/task.h>
@@ -62,18 +28,19 @@ QMutexPrivate::~QMutexPrivate()
qWarning("QMutex: failed to destroy semaphore, error %d", r);
}
-bool QMutexPrivate::wait(int timeout)
+bool QMutexPrivate::wait(QDeadlineTimer timeout)
{
kern_return_t r;
- if (timeout < 0) {
+ if (timeout.isForever()) {
do {
r = semaphore_wait(mach_semaphore);
} while (r == KERN_ABORTED);
Q_ASSERT(r == KERN_SUCCESS);
} else {
+ timespec tv = durationToTimespec(timeout.remainingTimeAsDuration());
mach_timespec_t ts;
- ts.tv_nsec = ((timeout % 1000) * 1000) * 1000;
- ts.tv_sec = (timeout / 1000);
+ ts.tv_nsec = tv.tv_nsec;
+ ts.tv_sec = tv.tv_sec;
r = semaphore_timedwait(mach_semaphore, ts);
}
return (r == KERN_SUCCESS);
diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h
index 048d8707c4..aabb66fa55 100644
--- a/src/corelib/thread/qmutex_p.h
+++ b/src/corelib/thread/qmutex_p.h
@@ -1,43 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Copyright (C) 2012 Olivier Goffart <ogoffart@woboq.com>
-** 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.
+// Copyright (C) 2016 Intel Corporation.
+// Copyright (C) 2012 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMUTEX_P_H
#define QMUTEX_P_H
@@ -46,10 +10,9 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of qmutex.cpp, qmutex_unix.cpp, and qmutex_win.cpp. This header
-// file may change from version to version without notice, or even be
-// removed.
+// This file is not part of the Qt API. It exists for the convenience of
+// qmutex.cpp and qmutex_unix.cpp. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
@@ -60,45 +23,35 @@
#include <QtCore/qatomic.h>
#include <QtCore/qdeadlinetimer.h>
-#if defined(Q_OS_MAC)
+#include "qplatformdefs.h" // _POSIX_VERSION
+
+#if defined(Q_OS_DARWIN)
# include <mach/semaphore.h>
-#elif defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
-// use Linux mutexes everywhere except for LSB builds
-# define QT_LINUX_FUTEX
#elif defined(Q_OS_UNIX)
-# if _POSIX_VERSION-0 >= 200112L || _XOPEN_VERSION-0 >= 600
# include <semaphore.h>
-# define QT_UNIX_SEMAPHORE
-# endif
#endif
struct timespec;
QT_BEGIN_NAMESPACE
-class QMutexData
-{
-public:
- bool recursive;
- QMutexData(QMutex::RecursionMode mode = QMutex::NonRecursive)
- : recursive(mode == QMutex::Recursive) {}
-};
-
-#if !defined(QT_LINUX_FUTEX)
-class QMutexPrivate : public QMutexData
+// We manipulate the pointer to this class in inline, atomic code,
+// so syncqt mustn't mark them as private, so ELFVERSION:ignore-next
+class QMutexPrivate
{
public:
~QMutexPrivate();
QMutexPrivate();
- bool wait(int timeout = -1);
+ bool wait(QDeadlineTimer timeout = QDeadlineTimer::Forever);
void wakeUp() noexcept;
// Control the lifetime of the privates
QAtomicInt refCount;
int id;
- bool ref() {
+ bool ref()
+ {
Q_ASSERT(refCount.loadRelaxed() >= 0);
int c;
do {
@@ -109,7 +62,8 @@ public:
Q_ASSERT(refCount.loadRelaxed() >= 0);
return true;
}
- void deref() {
+ void deref()
+ {
Q_ASSERT(refCount.loadRelaxed() >= 0);
if (!refCount.deref())
release();
@@ -128,27 +82,12 @@ public:
void derefWaiters(int value) noexcept;
//platform specific stuff
-#if defined(Q_OS_MAC)
+#if defined(Q_OS_DARWIN)
semaphore_t mach_semaphore;
-#elif defined(QT_UNIX_SEMAPHORE)
- sem_t semaphore;
#elif defined(Q_OS_UNIX)
- bool wakeup;
- pthread_mutex_t mutex;
- pthread_cond_t cond;
-#elif defined(Q_OS_WIN)
- Qt::HANDLE event;
+ sem_t semaphore;
#endif
};
-#endif //QT_LINUX_FUTEX
-
-
-#ifdef Q_OS_UNIX
-// helper functions for qmutex_unix.cpp and qwaitcondition_unix.cpp
-// they are in qwaitcondition_unix.cpp actually
-void qt_initialize_pthread_cond(pthread_cond_t *cond, const char *where);
-void qt_abstime_for_timeout(struct timespec *ts, QDeadlineTimer deadline);
-#endif
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qmutex_unix.cpp b/src/corelib/thread/qmutex_unix.cpp
index df2f606a23..4d01deb7d4 100644
--- a/src/corelib/thread/qmutex_unix.cpp
+++ b/src/corelib/thread/qmutex_unix.cpp
@@ -1,47 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
-** 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.
+// Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformdefs.h"
#include "qmutex.h"
#include "qstring.h"
-#include "qelapsedtimer.h"
#include "qatomic.h"
#include "qmutex_p.h"
#include <errno.h>
@@ -55,105 +18,48 @@
QT_BEGIN_NAMESPACE
-static void report_error(int code, const char *where, const char *what)
+static void qt_report_error(int code, const char *where, const char *what)
{
if (code != 0)
qErrnoWarning(code, "%s: %s failure", where, what);
}
-#ifdef QT_UNIX_SEMAPHORE
-
QMutexPrivate::QMutexPrivate()
{
- report_error(sem_init(&semaphore, 0, 0), "QMutex", "sem_init");
+ qt_report_error(sem_init(&semaphore, 0, 0), "QMutex", "sem_init");
}
QMutexPrivate::~QMutexPrivate()
{
- report_error(sem_destroy(&semaphore), "QMutex", "sem_destroy");
+ qt_report_error(sem_destroy(&semaphore), "QMutex", "sem_destroy");
}
-bool QMutexPrivate::wait(int timeout)
+bool QMutexPrivate::wait(QDeadlineTimer timeout)
{
int errorCode;
- if (timeout < 0) {
+ if (timeout.isForever()) {
do {
errorCode = sem_wait(&semaphore);
} while (errorCode && errno == EINTR);
- report_error(errorCode, "QMutex::lock()", "sem_wait");
+ qt_report_error(errorCode, "QMutex::lock()", "sem_wait");
} else {
- timespec ts;
- report_error(clock_gettime(CLOCK_REALTIME, &ts), "QMutex::lock()", "clock_gettime");
- ts.tv_sec += timeout / 1000;
- ts.tv_nsec += timeout % 1000 * Q_UINT64_C(1000) * 1000;
- normalizedTimespec(ts);
do {
+ auto tp = timeout.deadline<std::chrono::system_clock>();
+ timespec ts = durationToTimespec(tp.time_since_epoch());
errorCode = sem_timedwait(&semaphore, &ts);
} while (errorCode && errno == EINTR);
if (errorCode && errno == ETIMEDOUT)
return false;
- report_error(errorCode, "QMutex::lock()", "sem_timedwait");
+ qt_report_error(errorCode, "QMutex::lock()", "sem_timedwait");
}
return true;
}
void QMutexPrivate::wakeUp() noexcept
{
- report_error(sem_post(&semaphore), "QMutex::unlock", "sem_post");
-}
-
-#else // QT_UNIX_SEMAPHORE
-
-QMutexPrivate::QMutexPrivate()
- : wakeup(false)
-{
- report_error(pthread_mutex_init(&mutex, NULL), "QMutex", "mutex init");
- qt_initialize_pthread_cond(&cond, "QMutex");
-}
-
-QMutexPrivate::~QMutexPrivate()
-{
- report_error(pthread_cond_destroy(&cond), "QMutex", "cv destroy");
- report_error(pthread_mutex_destroy(&mutex), "QMutex", "mutex destroy");
-}
-
-bool QMutexPrivate::wait(int timeout)
-{
- report_error(pthread_mutex_lock(&mutex), "QMutex::lock", "mutex lock");
- int errorCode = 0;
- while (!wakeup) {
- if (timeout < 0) {
- errorCode = pthread_cond_wait(&cond, &mutex);
- } else {
- timespec ti;
- qt_abstime_for_timeout(&ti, QDeadlineTimer(timeout));
- errorCode = pthread_cond_timedwait(&cond, &mutex, &ti);
- }
- if (errorCode) {
- if (errorCode == ETIMEDOUT) {
- if (wakeup)
- errorCode = 0;
- break;
- }
- report_error(errorCode, "QMutex::lock()", "cv wait");
- }
- }
- bool ret = wakeup;
- wakeup = false;
- report_error(pthread_mutex_unlock(&mutex), "QMutex::lock", "mutex unlock");
- return ret;
-}
-
-void QMutexPrivate::wakeUp() noexcept
-{
- report_error(pthread_mutex_lock(&mutex), "QMutex::unlock", "mutex lock");
- wakeup = true;
- report_error(pthread_cond_signal(&cond), "QMutex::unlock", "cv signal");
- report_error(pthread_mutex_unlock(&mutex), "QMutex::unlock", "mutex unlock");
+ qt_report_error(sem_post(&semaphore), "QMutex::unlock", "sem_post");
}
-#endif
-
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qmutex_win.cpp b/src/corelib/thread/qmutex_win.cpp
deleted file mode 100644
index 73673cd5fb..0000000000
--- a/src/corelib/thread/qmutex_win.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qmutex.h"
-#include <qatomic.h>
-#include "qmutex_p.h"
-#include <qt_windows.h>
-
-QT_BEGIN_NAMESPACE
-
-QMutexPrivate::QMutexPrivate()
-{
- event = CreateEvent(0, FALSE, FALSE, 0);
-
- if (!event)
- qWarning("QMutexData::QMutexData: Cannot create event");
-}
-
-QMutexPrivate::~QMutexPrivate()
-{ CloseHandle(event); }
-
-bool QMutexPrivate::wait(int timeout)
-{
- return (WaitForSingleObjectEx(event, timeout < 0 ? INFINITE : timeout, FALSE) == WAIT_OBJECT_0);
-}
-
-void QMutexPrivate::wakeUp() noexcept
-{ SetEvent(event); }
-
-QT_END_NAMESPACE
diff --git a/src/corelib/thread/qorderedmutexlocker_p.h b/src/corelib/thread/qorderedmutexlocker_p.h
index 83edfd5879..fb2e223e01 100644
--- a/src/corelib/thread/qorderedmutexlocker_p.h
+++ b/src/corelib/thread/qorderedmutexlocker_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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 QORDEREDMUTEXLOCKER_P_H
#define QORDEREDMUTEXLOCKER_P_H
@@ -67,6 +31,7 @@ QT_BEGIN_NAMESPACE
class QOrderedMutexLocker
{
public:
+ Q_NODISCARD_CTOR
QOrderedMutexLocker(QBasicMutex *m1, QBasicMutex *m2)
: mtx1((m1 == m2) ? m1 : (std::less<QBasicMutex *>()(m1, m2) ? m1 : m2)),
mtx2((m1 == m2) ? nullptr : (std::less<QBasicMutex *>()(m1, m2) ? m2 : m1)),
@@ -74,6 +39,25 @@ public:
{
relock();
}
+
+ Q_DISABLE_COPY(QOrderedMutexLocker)
+
+ void swap(QOrderedMutexLocker &other) noexcept
+ {
+ qSwap(this->mtx1, other.mtx1);
+ qSwap(this->mtx2, other.mtx2);
+ qSwap(this->locked, other.locked);
+ }
+
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QOrderedMutexLocker)
+
+ Q_NODISCARD_CTOR
+ QOrderedMutexLocker(QOrderedMutexLocker &&other) noexcept
+ : mtx1(std::exchange(other.mtx1, nullptr))
+ , mtx2(std::exchange(other.mtx2, nullptr))
+ , locked(std::exchange(other.locked, false))
+ {}
+
~QOrderedMutexLocker()
{
unlock();
@@ -88,6 +72,21 @@ public:
}
}
+ /*!
+ \internal
+ Can be called if the mutexes have been unlocked manually, and sets the
+ state of the QOrderedMutexLocker to unlocked.
+ The caller is expected to have unlocked both of them if they
+ are not the same. Calling this method when the QOrderedMutexLocker is
+ unlocked or when the provided mutexes have not actually been unlocked is
+ UB.
+ */
+ void dismiss()
+ {
+ Q_ASSERT(locked);
+ locked = false;
+ }
+
void unlock()
{
if (locked) {
@@ -119,51 +118,26 @@ private:
bool locked;
};
-class QBasicMutexLocker
-{
-public:
- inline explicit QBasicMutexLocker(QBasicMutex *m) QT_MUTEX_LOCK_NOEXCEPT
- : m(m), isLocked(true)
- {
- m->lock();
- }
- inline ~QBasicMutexLocker() { if (isLocked) unlock(); }
-
- inline void unlock() noexcept
- {
- isLocked = false;
- m->unlock();
- }
-
- inline void relock() QT_MUTEX_LOCK_NOEXCEPT
- {
- isLocked = true;
- m->lock();
- }
-
-private:
- Q_DISABLE_COPY(QBasicMutexLocker)
-
- QBasicMutex *m;
- bool isLocked;
-};
-
#else
class QOrderedMutexLocker
{
public:
+ Q_DISABLE_COPY(QOrderedMutexLocker)
+ Q_NODISCARD_CTOR
QOrderedMutexLocker(QBasicMutex *, QBasicMutex *) {}
+ Q_NODISCARD_CTOR
+ QOrderedMutexLocker(QOrderedMutexLocker &&) = default;
+ QOrderedMutexLocker& operator=(QOrderedMutexLocker &&other) = default;
~QOrderedMutexLocker() {}
void relock() {}
void unlock() {}
+ void dismiss() {}
static bool relock(QBasicMutex *, QBasicMutex *) { return false; }
};
-using QBasicMutexLocker = QMutexLocker;
-
#endif
diff --git a/src/corelib/thread/qpromise.h b/src/corelib/thread/qpromise.h
index 32aaa202f5..c2b6c119ae 100644
--- a/src/corelib/thread/qpromise.h
+++ b/src/corelib/thread/qpromise.h
@@ -1,47 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 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) 2020 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 QPROMISE_H
#define QPROMISE_H
#include <QtCore/qglobal.h>
-#include <QtCore/qfuture.h>
+#include <QtCore/qfutureinterface.h>
#include <utility>
@@ -49,55 +13,66 @@ QT_REQUIRE_CONFIG(future);
QT_BEGIN_NAMESPACE
+namespace QtPrivate {
+
+template<class T, class U>
+using EnableIfSameOrConvertible = std::enable_if_t<std::is_convertible_v<T, U>>;
+
+} // namespace QtPrivate
+
template<typename T>
class QPromise
{
- static_assert (std::is_copy_constructible_v<T>
- || std::is_move_constructible_v<T>
+ static_assert (std::is_move_constructible_v<T>
|| std::is_same_v<T, void>,
- "Type with copy or move constructors or type void is required");
+ "A move-constructible type or type void is required");
public:
QPromise() = default;
Q_DISABLE_COPY(QPromise)
- QPromise(QPromise<T> &&other) : d(other.d)
- {
- other.d = QFutureInterface<T>();
- }
- QPromise& operator=(QPromise<T> &&other)
- {
- QPromise<T> tmp(std::move(other));
- tmp.swap(*this);
- return *this;
- }
+ QPromise(QPromise<T> &&other) = default;
+ QPromise(const QFutureInterface<T> &other) : d(other) {}
+ QPromise(QFutureInterface<T> &&other) noexcept : d(std::move(other)) {}
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QPromise)
~QPromise()
{
- const int state = d.loadState();
- // If QFutureInterface has no state, there is nothing to be done
- if (state == static_cast<int>(QFutureInterfaceBase::State::NoState))
- return;
- // Otherwise, if computation is not finished at this point, cancel
+ // If computation is not finished at this point, cancel
// potential waits
- if (!(state & QFutureInterfaceBase::State::Finished)) {
- d.cancel();
- reportFinished(); // required to finalize the state
+ if (d.d && !(d.loadState() & QFutureInterfaceBase::State::Finished)) {
+ d.cancelAndFinish(); // cancel and finalize the state
+ d.runContinuation();
}
+ d.cleanContinuation();
}
// Core QPromise APIs
QFuture<T> future() const { return d.future(); }
- template<typename U = T,
- typename = QtPrivate::EnableForNonVoid<std::decay_t<U>>,
- typename = QtPrivate::EnableIfSameOrConvertible<std::decay_t<U>, std::decay_t<T>>>
- void addResult(U &&result, int index = -1)
+ template<typename...Args, std::enable_if_t<std::is_constructible_v<T, Args...>, bool> = true>
+ bool emplaceResultAt(int index, Args&&...args)
+ {
+ return d.reportAndEmplaceResult(index, std::forward<Args>(args)...);
+ }
+ template<typename...Args, std::enable_if_t<std::is_constructible_v<T, Args...>, bool> = true>
+ bool emplaceResult(Args&&...args)
{
- d.reportResult(std::forward<U>(result), index);
+ return d.reportAndEmplaceResult(-1, std::forward<Args>(args)...);
}
+ template<typename U = T, typename = QtPrivate::EnableIfSameOrConvertible<U, T>>
+ bool addResult(U &&result, int index = -1)
+ {
+ return d.reportAndEmplaceResult(index, std::forward<U>(result));
+ }
+ bool addResults(const QList<T> &result)
+ { return d.reportResults(result); }
#ifndef QT_NO_EXCEPTIONS
void setException(const QException &e) { d.reportException(e); }
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
void setException(std::exception_ptr e) { d.reportException(e); }
+#else
+ void setException(const std::exception_ptr &e) { d.reportException(e); }
+#endif
#endif
- void reportStarted() { d.reportStarted(); }
- void reportFinished() { d.reportFinished(); }
+ void start() { d.reportStarted(); }
+ void finish() { d.reportFinished(); }
void suspendIfRequested() { d.suspendIfRequested(); }
@@ -113,15 +88,15 @@ public:
void swap(QPromise<T> &other) noexcept
{
- qSwap(this->d, other.d);
+ d.swap(other.d);
}
-#if defined(Q_CLANG_QDOC) // documentation-only simplified signatures
- void addResult(const T &result, int index = -1) { }
- void addResult(T &&result, int index = -1) { }
+#if defined(Q_QDOC) // documentation-only simplified signatures
+ bool addResult(const T &result, int index = -1) { }
+ bool addResult(T &&result, int index = -1) { }
#endif
private:
- mutable QFutureInterface<T> d = QFutureInterface<T>();
+ mutable QFutureInterface<T> d;
};
template<typename T>
diff --git a/src/corelib/thread/qpromise.qdoc b/src/corelib/thread/qpromise.qdoc
index b404732a9e..e9c3eb4b7e 100644
--- a/src/corelib/thread/qpromise.qdoc
+++ b/src/corelib/thread/qpromise.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** 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 Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*! \class QPromise
\inmodule QtCore
@@ -62,6 +38,8 @@
\snippet snippet_qpromise.cpp multithread_init
\codeline
\snippet snippet_qpromise.cpp multithread_main
+ \codeline
+ \snippet snippet_qpromise.cpp multithread_cleanup
\sa QFuture
*/
@@ -78,6 +56,19 @@
\sa operator=()
*/
+/*! \fn template <typename T> QPromise<T>::QPromise(const QFutureInterface<T> &other)
+ \fn template <typename T> QPromise<T>::QPromise(QFutureInterface<T> &&other) noexcept
+
+ \internal
+ Constructs a QPromise with a passed QFutureInterface \a other.
+ Used internally for QtConcurrent::run(), when its callable takes
+ a reference to the associated promise as its first argument
+ (run with promise mode).
+
+ \sa operator=()
+*/
+
+
/*! \fn template <typename T> QPromise<T> &QPromise<T>::operator=(QPromise<T> &&other)
Move assigns \a other to this promise and returns a reference to this
@@ -88,8 +79,8 @@
Destroys the promise.
- \note The promise implicitly transitions to a cancelled state on destruction
- unless reportFinished() is called beforehand by the user.
+ \note The promise implicitly transitions to a canceled state on destruction
+ unless finish() is called beforehand by the user.
*/
/*! \fn template <typename T> QFuture<T> QPromise<T>::future() const
@@ -97,10 +88,40 @@
Returns a future associated with this promise.
*/
-/*! \fn template <typename T> void QPromise<T>::addResult(const T &result, int index = -1)
+/*! \fn template <typename T> bool QPromise<T>::addResult(const T &result, int index = -1)
+ \fn template <typename T> bool QPromise<T>::addResult(T &&result, int index = -1)
+
+ Same as
+ \code
+ emplaceResultAt(index, result); // first overload
+ emplaceResultAt(index, std::move(result)); // second overload
+ \endcode
+ or, if \c{index == -1} (the default)
+ \code
+ emplaceResult(result); // first overload
+ emplaceResult(std::move(result)); // second overload
+ \endcode
+
+ \sa emplaceResultAt(), emplaceResult(), addResults()
+*/
+
+/*!
+ \fn template <typename T> template <typename...Args, std::enable_if_t<std::is_constructible_v<T, Args...>, bool> = true> bool QPromise<T>::emplaceResultAt(int index, Args&&...args)
+ \fn template <typename T> template <typename...Args, std::enable_if_t<std::is_constructible_v<T, Args...>, bool> = true> bool QPromise<T>::emplaceResult(Args&&...args)
+ \since 6.6
+
+ Adds a result constructed from \a args... to the internal result collection
+ at \a index position (emplaceResultAt()) or the end of of the collection
+ (emplaceResult()).
- Adds \a result to the internal result collection at \a index position. If
- index is unspecified, \a result is added to the end of the collection.
+ Returns \c true when the result was added to the collection.
+
+ Returns \c false when this promise is in canceled or finished state or when
+ the result was rejected. addResult() rejects to add a result if there's already
+ another result in the collection stored at the same index.
+
+ These functions only participate in overload resolutions if \c T is
+ constructible from \a args....
You can get a result at a specific index by calling QFuture::resultAt().
@@ -109,11 +130,29 @@
For instance, iterative approaches that use QFuture::resultCount() or
QFuture::const_iterator. In order to get all available results without
thinking if there are index gaps or not, use QFuture::results().
+
+ \sa addResult(), addResults()
*/
-/*! \fn template <typename T> void QPromise<T>::addResult(T &&result, int index = -1)
+/*!
+ \fn template <typename T> bool QPromise<T>::addResults(const QList<T> &results)
+ \since 6.6
- \overload
+ Adds \a results at the end of the internal result collection.
+
+ Returns \c true when \a results are added to the collection.
+
+ Returns \c false when this promise is in canceled or finished state.
+
+ This is more efficient than looping over addResult(), because associated
+ futures will be notified only once per addResults() call, instead of once
+ per element contained in \a results, as would be the case with individual
+ addResult() calls. But if the calculation of each element takes time, then
+ the code on the receiving end (future) cannot make progress until all
+ results are reported, so use this function only if the calculation of
+ consecutive elements is relatively fast.
+
+ \sa addResult()
*/
/*! \fn template<typename T> void QPromise<T>::setException(const QException &e)
@@ -124,41 +163,48 @@
execution.
\note This method must not be used after QFuture::cancel() or
- reportFinished().
+ finish().
\sa isCanceled()
*/
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*! \fn template<typename T> void QPromise<T>::setException(std::exception_ptr e)
\overload
*/
+#else
+/*! \fn template<typename T> void QPromise<T>::setException(const std::exception_ptr &e)
+
+ \overload
+*/
+#endif
-/*! \fn template<typename T> void QPromise<T>::reportStarted()
+/*! \fn template<typename T> void QPromise<T>::start()
Reports that the computation is started. Calling this method is important to
state the beginning of the computation as QFuture methods rely on this
information.
- \note Extra attention is required when reportStarted() is called from a
+ \note Extra attention is required when start() is called from a
newly created thread. In such case, the call might naturally be delayed due
to the implementation details of the thread scheduling.
- \sa QFuture::isStarted(), QFuture::waitForFinished(), reportFinished()
+ \sa QFuture::isStarted(), QFuture::waitForFinished(), finish()
*/
-/*! \fn template<typename T> void QPromise<T>::reportFinished()
+/*! \fn template<typename T> void QPromise<T>::finish()
Reports that the computation is finished. Once finished, no new results will
- be added when calling addResult(). This method accompanies reportStarted().
+ be added when calling addResult(). This method accompanies start().
- \sa QFuture::isFinished(), QFuture::waitForFinished(), reportStarted()
+ \sa QFuture::isFinished(), QFuture::waitForFinished(), start()
*/
/*! \fn template<typename T> void QPromise<T>::suspendIfRequested()
Conditionally suspends current thread of execution and waits until resumed
- or cancelled by the corresponding methods of QFuture. This method does not
+ or canceled by the corresponding methods of QFuture. This method does not
block unless the computation is requested to be suspended by
QFuture::suspend() or another related method. If you want to check that the
execution has been suspended, use QFuture::isSuspended().
@@ -192,9 +238,9 @@
/*! \fn template<typename T> bool QPromise<T>::isCanceled() const
- Returns whether the computation has been cancelled with the
+ Returns whether the computation has been canceled with the
QFuture::cancel() function. The returned value \c true indicates that the
- computation should be finished and reportFinished() called.
+ computation should be finished and finish() called.
\note After cancellation, results currently available may still be accessed
by a future, but new results will not be added when calling addResult().
@@ -205,7 +251,16 @@
Sets the progress range of the computation to be between \a minimum and \a
maximum.
- \sa QFuture::progressMinimum(), QFuture::progressMaximum()
+ If \a maximum is smaller than \a minimum, \a minimum becomes the only
+ legal value.
+
+ The progress value is reset to be \a minimum.
+
+ The progress range usage can be disabled by using setProgressRange(0, 0).
+ In this case progress value is also reset to 0.
+
+ \sa QFuture::progressMinimum(), QFuture::progressMaximum(),
+ QFuture::progressValue()
*/
/*! \fn template<typename T> void QPromise<T>::setProgressValue(int progressValue)
@@ -214,7 +269,10 @@
possible to only increment the progress value. This is a convenience method
for calling setProgressValueAndText(progressValue, QString()).
- \sa QFuture::progressValue()
+ In case of the \a progressValue falling out of the progress range,
+ this method has no effect.
+
+ \sa QFuture::progressValue(), setProgressRange()
*/
/*! \fn template<typename T> void QPromise<T>::setProgressValueAndText(int progressValue, const QString &progressText)
@@ -223,11 +281,11 @@
progressValue and \a progressText respectively. It is possible to only
increment the progress value.
- \note This function has no effect if the promise is in cancelled or finished
+ \note This function has no effect if the promise is in canceled or finished
state.
\sa QFuture::progressValue(), QFuture::progressText(), QFuture::cancel(),
- reportFinished()
+ finish()
*/
/*! \fn template<typename T> void QPromise<T>::swap(QPromise<T> &other) noexcept
diff --git a/src/corelib/thread/qreadwritelock.cpp b/src/corelib/thread/qreadwritelock.cpp
index 8c28507d5a..e79bed2231 100644
--- a/src/corelib/thread/qreadwritelock.cpp
+++ b/src/corelib/thread/qreadwritelock.cpp
@@ -1,55 +1,18 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
-** 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.
+// Copyright (C) 2016 Intel Corporation.
+// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformdefs.h"
#include "qreadwritelock.h"
-#include "qmutex.h"
#include "qthread.h"
-#include "qwaitcondition.h"
#include "qreadwritelock_p.h"
-#include "qelapsedtimer.h"
#include "private/qfreelist_p.h"
#include "private/qlocking_p.h"
+#include <algorithm>
+
QT_BEGIN_NAMESPACE
/*
@@ -64,18 +27,22 @@ QT_BEGIN_NAMESPACE
* - In any other case, d_ptr points to an actual QReadWriteLockPrivate.
*/
+using namespace QReadWriteLockStates;
namespace {
-enum {
- StateMask = 0x3,
- StateLockedForRead = 0x1,
- StateLockedForWrite = 0x2,
-};
+
+using steady_clock = std::chrono::steady_clock;
+
const auto dummyLockedForRead = reinterpret_cast<QReadWriteLockPrivate *>(quintptr(StateLockedForRead));
const auto dummyLockedForWrite = reinterpret_cast<QReadWriteLockPrivate *>(quintptr(StateLockedForWrite));
inline bool isUncontendedLocked(const QReadWriteLockPrivate *d)
{ return quintptr(d) & StateMask; }
}
+static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
+ QDeadlineTimer timeout, QReadWriteLockPrivate *d);
+static bool contendedTryLockForWrite(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
+ QDeadlineTimer timeout, QReadWriteLockPrivate *d);
+
/*! \class QReadWriteLock
\inmodule QtCore
\brief The QReadWriteLock class provides read-write locking.
@@ -133,6 +100,7 @@ inline bool isUncontendedLocked(const QReadWriteLockPrivate *d)
*/
/*!
+ \fn QReadWriteLock::QReadWriteLock(RecursionMode recursionMode)
\since 4.4
Constructs a QReadWriteLock object in the given \a recursionMode.
@@ -141,21 +109,22 @@ inline bool isUncontendedLocked(const QReadWriteLockPrivate *d)
\sa lockForRead(), lockForWrite(), RecursionMode
*/
-QReadWriteLock::QReadWriteLock(RecursionMode recursionMode)
- : d_ptr(recursionMode == Recursive ? new QReadWriteLockPrivate(true) : nullptr)
+QReadWriteLockPrivate *QReadWriteLock::initRecursive()
{
- Q_ASSERT_X(!(quintptr(d_ptr.loadRelaxed()) & StateMask), "QReadWriteLock::QReadWriteLock", "bad d_ptr alignment");
+ auto d = new QReadWriteLockPrivate(true);
+ Q_ASSERT_X(!(quintptr(d) & StateMask), "QReadWriteLock::QReadWriteLock", "bad d_ptr alignment");
+ return d;
}
/*!
+ \fn QReadWriteLock::~QReadWriteLock()
Destroys the QReadWriteLock object.
\warning Destroying a read-write lock that is in use may result
in undefined behavior.
*/
-QReadWriteLock::~QReadWriteLock()
+void QReadWriteLock::destroyRecursive(QReadWriteLockPrivate *d)
{
- auto d = d_ptr.loadRelaxed();
if (isUncontendedLocked(d)) {
qWarning("QReadWriteLock: destroying locked QReadWriteLock");
return;
@@ -164,6 +133,7 @@ QReadWriteLock::~QReadWriteLock()
}
/*!
+ \fn QReadWriteLock::lockForRead()
Locks the lock for reading. This function will block the current
thread if another thread has locked for writing.
@@ -172,20 +142,18 @@ QReadWriteLock::~QReadWriteLock()
\sa unlock(), lockForWrite(), tryLockForRead()
*/
-void QReadWriteLock::lockForRead()
-{
- if (d_ptr.testAndSetAcquire(nullptr, dummyLockedForRead))
- return;
- tryLockForRead(-1);
-}
/*!
- Attempts to lock for reading. If the lock was obtained, this
- function returns \c true, otherwise it returns \c false instead of
- waiting for the lock to become available, i.e. it does not block.
+ \fn bool QReadWriteLock::tryLockForRead(int timeout)
- The lock attempt will fail if another thread has locked for
- writing.
+ Attempts to lock for reading. This function returns \c true if the
+ lock was obtained; otherwise it returns \c false. If another thread
+ has locked for writing, this function will wait for at most \a
+ timeout milliseconds for the lock to become available.
+
+ Note: Passing a negative number as the \a timeout is equivalent to
+ calling lockForRead(), i.e. this function will wait forever until
+ lock can be locked for reading when \a timeout is negative.
If the lock was obtained, the lock must be unlocked with unlock()
before another thread can successfully lock it for writing.
@@ -195,21 +163,15 @@ void QReadWriteLock::lockForRead()
\sa unlock(), lockForRead()
*/
-bool QReadWriteLock::tryLockForRead()
-{
- return tryLockForRead(0);
-}
-/*! \overload
-
- Attempts to lock for reading. This function returns \c true if the
- lock was obtained; otherwise it returns \c false. If another thread
- has locked for writing, this function will wait for at most \a
- timeout milliseconds for the lock to become available.
+/*!
+ \overload
+ \since 6.6
- Note: Passing a negative number as the \a timeout is equivalent to
- calling lockForRead(), i.e. this function will wait forever until
- lock can be locked for reading when \a timeout is negative.
+ Attempts to lock for reading. This function returns \c true if the lock was
+ obtained; otherwise it returns \c false. If another thread has locked for
+ writing, this function will wait until \a timeout expires for the lock to
+ become available.
If the lock was obtained, the lock must be unlocked with unlock()
before another thread can successfully lock it for writing.
@@ -219,13 +181,18 @@ bool QReadWriteLock::tryLockForRead()
\sa unlock(), lockForRead()
*/
-bool QReadWriteLock::tryLockForRead(int timeout)
+bool QReadWriteLock::tryLockForRead(QDeadlineTimer timeout)
{
// Fast case: non contended:
- QReadWriteLockPrivate *d;
- if (d_ptr.testAndSetAcquire(nullptr, dummyLockedForRead, d))
+ QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
+ if (d == nullptr && d_ptr.testAndSetAcquire(nullptr, dummyLockedForRead, d))
return true;
+ return contendedTryLockForRead(d_ptr, timeout, d);
+}
+Q_NEVER_INLINE static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
+ QDeadlineTimer timeout, QReadWriteLockPrivate *d)
+{
while (true) {
if (d == nullptr) {
if (!d_ptr.testAndSetAcquire(nullptr, dummyLockedForRead, d))
@@ -244,7 +211,7 @@ bool QReadWriteLock::tryLockForRead(int timeout)
}
if (d == dummyLockedForWrite) {
- if (!timeout)
+ if (timeout.hasExpired())
return false;
// locked for write, assign a d_ptr and wait.
@@ -274,11 +241,12 @@ bool QReadWriteLock::tryLockForRead(int timeout)
d = d_ptr.loadAcquire();
continue;
}
- return d->lockForRead(timeout);
+ return d->lockForRead(lock, timeout);
}
}
/*!
+ \fn QReadWriteLock::lockForWrite()
Locks the lock for writing. This function will block the current
thread if another thread (including the current) has locked for
reading or writing (unless the lock has been created using the
@@ -289,17 +257,18 @@ bool QReadWriteLock::tryLockForRead(int timeout)
\sa unlock(), lockForRead(), tryLockForWrite()
*/
-void QReadWriteLock::lockForWrite()
-{
- tryLockForWrite(-1);
-}
/*!
- Attempts to lock for writing. If the lock was obtained, this
- function returns \c true; otherwise, it returns \c false immediately.
+ \fn QReadWriteLock::tryLockForWrite(int timeout)
+
+ Attempts to lock for writing. This function returns \c true if the
+ lock was obtained; otherwise it returns \c false. If another thread
+ has locked for reading or writing, this function will wait for at
+ most \a timeout milliseconds for the lock to become available.
- The lock attempt will fail if another thread has locked for
- reading or writing.
+ Note: Passing a negative number as the \a timeout is equivalent to
+ calling lockForWrite(), i.e. this function will wait forever until
+ lock can be locked for writing when \a timeout is negative.
If the lock was obtained, the lock must be unlocked with unlock()
before another thread can successfully lock it.
@@ -309,21 +278,15 @@ void QReadWriteLock::lockForWrite()
\sa unlock(), lockForWrite()
*/
-bool QReadWriteLock::tryLockForWrite()
-{
- return tryLockForWrite(0);
-}
-
-/*! \overload
- Attempts to lock for writing. This function returns \c true if the
- lock was obtained; otherwise it returns \c false. If another thread
- has locked for reading or writing, this function will wait for at
- most \a timeout milliseconds for the lock to become available.
+/*!
+ \overload
+ \since 6.6
- Note: Passing a negative number as the \a timeout is equivalent to
- calling lockForWrite(), i.e. this function will wait forever until
- lock can be locked for writing when \a timeout is negative.
+ Attempts to lock for writing. This function returns \c true if the lock was
+ obtained; otherwise it returns \c false. If another thread has locked for
+ reading or writing, this function will wait until \a timeout expires for
+ the lock to become available.
If the lock was obtained, the lock must be unlocked with unlock()
before another thread can successfully lock it.
@@ -333,13 +296,18 @@ bool QReadWriteLock::tryLockForWrite()
\sa unlock(), lockForWrite()
*/
-bool QReadWriteLock::tryLockForWrite(int timeout)
+bool QReadWriteLock::tryLockForWrite(QDeadlineTimer timeout)
{
// Fast case: non contended:
- QReadWriteLockPrivate *d;
- if (d_ptr.testAndSetAcquire(nullptr, dummyLockedForWrite, d))
+ QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
+ if (d == nullptr && d_ptr.testAndSetAcquire(nullptr, dummyLockedForWrite, d))
return true;
+ return contendedTryLockForWrite(d_ptr, timeout, d);
+}
+Q_NEVER_INLINE static bool contendedTryLockForWrite(QAtomicPointer<QReadWriteLockPrivate> &d_ptr,
+ QDeadlineTimer timeout, QReadWriteLockPrivate *d)
+{
while (true) {
if (d == nullptr) {
if (!d_ptr.testAndSetAcquire(d, dummyLockedForWrite, d))
@@ -348,7 +316,7 @@ bool QReadWriteLock::tryLockForWrite(int timeout)
}
if (isUncontendedLocked(d)) {
- if (!timeout)
+ if (timeout.hasExpired())
return false;
// locked for either read or write, assign a d_ptr and wait.
@@ -378,7 +346,7 @@ bool QReadWriteLock::tryLockForWrite(int timeout)
d = d_ptr.loadAcquire();
continue;
}
- return d->lockForWrite(timeout);
+ return d->lockForWrite(lock, timeout);
}
}
@@ -442,45 +410,19 @@ void QReadWriteLock::unlock()
}
}
-/*! \internal Helper for QWaitCondition::wait */
-QReadWriteLock::StateForWaitCondition QReadWriteLock::stateForWaitCondition() const
+bool QReadWriteLockPrivate::lockForRead(std::unique_lock<std::mutex> &lock, QDeadlineTimer timeout)
{
- QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
- switch (quintptr(d) & StateMask) {
- case StateLockedForRead: return LockedForRead;
- case StateLockedForWrite: return LockedForWrite;
- }
-
- if (!d)
- return Unlocked;
- if (d->writerCount > 1)
- return RecursivelyLocked;
- else if (d->writerCount == 1)
- return LockedForWrite;
- return LockedForRead;
-
-}
-
-bool QReadWriteLockPrivate::lockForRead(int timeout)
-{
- Q_ASSERT(!mutex.tryLock()); // mutex must be locked when entering this function
-
- QElapsedTimer t;
- if (timeout > 0)
- t.start();
+ Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function
while (waitingWriters || writerCount) {
- if (timeout == 0)
+ if (timeout.hasExpired())
return false;
- if (timeout > 0) {
- auto elapsed = t.elapsed();
- if (elapsed > timeout)
- return false;
+ if (!timeout.isForever()) {
waitingReaders++;
- readerCond.wait(&mutex, QDeadlineTimer(timeout - elapsed));
+ readerCond.wait_until(lock, timeout.deadline<steady_clock>());
} else {
waitingReaders++;
- readerCond.wait(&mutex);
+ readerCond.wait(lock);
}
waitingReaders--;
}
@@ -489,32 +431,25 @@ bool QReadWriteLockPrivate::lockForRead(int timeout)
return true;
}
-bool QReadWriteLockPrivate::lockForWrite(int timeout)
+bool QReadWriteLockPrivate::lockForWrite(std::unique_lock<std::mutex> &lock, QDeadlineTimer timeout)
{
- Q_ASSERT(!mutex.tryLock()); // mutex must be locked when entering this function
-
- QElapsedTimer t;
- if (timeout > 0)
- t.start();
+ Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function
while (readerCount || writerCount) {
- if (timeout == 0)
- return false;
- if (timeout > 0) {
- auto elapsed = t.elapsed();
- if (elapsed > timeout) {
- if (waitingReaders && !waitingWriters && !writerCount) {
- // We timed out and now there is no more writers or waiting writers, but some
- // readers were queueud (probably because of us). Wake the waiting readers.
- readerCond.wakeAll();
- }
- return false;
+ if (timeout.hasExpired()) {
+ if (waitingReaders && !waitingWriters && !writerCount) {
+ // We timed out and now there is no more writers or waiting writers, but some
+ // readers were queued (probably because of us). Wake the waiting readers.
+ readerCond.notify_all();
}
+ return false;
+ }
+ if (!timeout.isForever()) {
waitingWriters++;
- writerCond.wait(&mutex, QDeadlineTimer(timeout - elapsed));
+ writerCond.wait_until(lock, timeout.deadline<steady_clock>());
} else {
waitingWriters++;
- writerCond.wait(&mutex);
+ writerCond.wait(lock);
}
waitingWriters--;
}
@@ -527,34 +462,41 @@ bool QReadWriteLockPrivate::lockForWrite(int timeout)
void QReadWriteLockPrivate::unlock()
{
- Q_ASSERT(!mutex.tryLock()); // mutex must be locked when entering this function
+ Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function
if (waitingWriters)
- writerCond.wakeOne();
+ writerCond.notify_one();
else if (waitingReaders)
- readerCond.wakeAll();
+ readerCond.notify_all();
}
-bool QReadWriteLockPrivate::recursiveLockForRead(int timeout)
+static auto handleEquals(Qt::HANDLE handle)
+{
+ return [handle](QReadWriteLockPrivate::Reader reader) { return reader.handle == handle; };
+}
+
+bool QReadWriteLockPrivate::recursiveLockForRead(QDeadlineTimer timeout)
{
Q_ASSERT(recursive);
auto lock = qt_unique_lock(mutex);
Qt::HANDLE self = QThread::currentThreadId();
- auto it = currentReaders.find(self);
+ auto it = std::find_if(currentReaders.begin(), currentReaders.end(),
+ handleEquals(self));
if (it != currentReaders.end()) {
- ++it.value();
+ ++it->recursionLevel;
return true;
}
- if (!lockForRead(timeout))
+ if (!lockForRead(lock, timeout))
return false;
- currentReaders.insert(self, 1);
+ Reader r = {self, 1};
+ currentReaders.append(std::move(r));
return true;
}
-bool QReadWriteLockPrivate::recursiveLockForWrite(int timeout)
+bool QReadWriteLockPrivate::recursiveLockForWrite(QDeadlineTimer timeout)
{
Q_ASSERT(recursive);
auto lock = qt_unique_lock(mutex);
@@ -565,7 +507,7 @@ bool QReadWriteLockPrivate::recursiveLockForWrite(int timeout)
return true;
}
- if (!lockForWrite(timeout))
+ if (!lockForWrite(lock, timeout))
return false;
currentWriter = self;
@@ -583,12 +525,13 @@ void QReadWriteLockPrivate::recursiveUnlock()
return;
currentWriter = nullptr;
} else {
- auto it = currentReaders.find(self);
+ auto it = std::find_if(currentReaders.begin(), currentReaders.end(),
+ handleEquals(self));
if (it == currentReaders.end()) {
qWarning("QReadWriteLock::unlock: unlocking from a thread that did not lock");
return;
} else {
- if (--it.value() <= 0) {
+ if (--it->recursionLevel <= 0) {
currentReaders.erase(it);
readerCount--;
}
@@ -602,25 +545,24 @@ void QReadWriteLockPrivate::recursiveUnlock()
// The freelist management
namespace {
-struct FreeListConstants : QFreeListDefaultConstants {
+struct QReadWriteLockFreeListConstants : QFreeListDefaultConstants
+{
enum { BlockCount = 4, MaxIndex=0xffff };
static const int Sizes[BlockCount];
};
-const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = {
- 16,
- 128,
- 1024,
- FreeListConstants::MaxIndex - (16 + 128 + 1024)
-};
+Q_CONSTINIT const int
+ QReadWriteLockFreeListConstants::Sizes[QReadWriteLockFreeListConstants::BlockCount] = {
+ 16, 128, 1024, QReadWriteLockFreeListConstants::MaxIndex - (16 + 128 + 1024)
+ };
-typedef QFreeList<QReadWriteLockPrivate, FreeListConstants> FreeList;
-Q_GLOBAL_STATIC(FreeList, freelist);
+typedef QFreeList<QReadWriteLockPrivate, QReadWriteLockFreeListConstants> QReadWriteLockFreeList;
+Q_GLOBAL_STATIC(QReadWriteLockFreeList, qrwl_freelist);
}
QReadWriteLockPrivate *QReadWriteLockPrivate::allocate()
{
- int i = freelist->next();
- QReadWriteLockPrivate *d = &(*freelist)[i];
+ int i = qrwl_freelist->next();
+ QReadWriteLockPrivate *d = &(*qrwl_freelist)[i];
d->id = i;
Q_ASSERT(!d->recursive);
Q_ASSERT(!d->waitingReaders && !d->waitingWriters && !d->readerCount && !d->writerCount);
@@ -631,7 +573,7 @@ void QReadWriteLockPrivate::release()
{
Q_ASSERT(!recursive);
Q_ASSERT(!waitingReaders && !waitingWriters && !readerCount && !writerCount);
- freelist->release(id);
+ qrwl_freelist->release(id);
}
/*!
diff --git a/src/corelib/thread/qreadwritelock.h b/src/corelib/thread/qreadwritelock.h
index 3c1ed91b94..6ca9be440a 100644
--- a/src/corelib/thread/qreadwritelock.h
+++ b/src/corelib/thread/qreadwritelock.h
@@ -1,50 +1,14 @@
-/****************************************************************************
-**
-** 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 QREADWRITELOCK_H
#define QREADWRITELOCK_H
#include <QtCore/qglobal.h>
+#include <QtCore/qdeadlinetimer.h>
QT_BEGIN_NAMESPACE
-
#if QT_CONFIG(thread)
class QReadWriteLockPrivate;
@@ -54,36 +18,81 @@ class Q_CORE_EXPORT QReadWriteLock
public:
enum RecursionMode { NonRecursive, Recursive };
+ QT_CORE_INLINE_SINCE(6, 6)
explicit QReadWriteLock(RecursionMode recursionMode = NonRecursive);
+ QT_CORE_INLINE_SINCE(6, 6)
~QReadWriteLock();
+ QT_CORE_INLINE_SINCE(6, 6)
void lockForRead();
+#if QT_CORE_REMOVED_SINCE(6, 6)
bool tryLockForRead();
+#endif
+ QT_CORE_INLINE_SINCE(6, 6)
bool tryLockForRead(int timeout);
+ bool tryLockForRead(QDeadlineTimer timeout = {});
+ QT_CORE_INLINE_SINCE(6, 6)
void lockForWrite();
+#if QT_CORE_REMOVED_SINCE(6, 6)
bool tryLockForWrite();
+#endif
+ QT_CORE_INLINE_SINCE(6, 6)
bool tryLockForWrite(int timeout);
+ bool tryLockForWrite(QDeadlineTimer timeout = {});
void unlock();
private:
Q_DISABLE_COPY(QReadWriteLock)
QAtomicPointer<QReadWriteLockPrivate> d_ptr;
-
- enum StateForWaitCondition { LockedForRead, LockedForWrite, Unlocked, RecursivelyLocked };
- StateForWaitCondition stateForWaitCondition() const;
- friend class QWaitCondition;
+ friend class QReadWriteLockPrivate;
+ static QReadWriteLockPrivate *initRecursive();
+ static void destroyRecursive(QReadWriteLockPrivate *);
};
+#if QT_CORE_INLINE_IMPL_SINCE(6, 6)
+QReadWriteLock::QReadWriteLock(RecursionMode recursionMode)
+ : d_ptr(recursionMode == Recursive ? initRecursive() : nullptr)
+{
+}
+
+QReadWriteLock::~QReadWriteLock()
+{
+ if (auto d = d_ptr.loadAcquire())
+ destroyRecursive(d);
+}
+
+void QReadWriteLock::lockForRead()
+{
+ tryLockForRead(QDeadlineTimer(QDeadlineTimer::Forever));
+}
+
+bool QReadWriteLock::tryLockForRead(int timeout)
+{
+ return tryLockForRead(QDeadlineTimer(timeout));
+}
+
+void QReadWriteLock::lockForWrite()
+{
+ tryLockForWrite(QDeadlineTimer(QDeadlineTimer::Forever));
+}
+
+bool QReadWriteLock::tryLockForWrite(int timeout)
+{
+ return tryLockForWrite(QDeadlineTimer(timeout));
+}
+#endif // inline since 6.6
+
#if defined(Q_CC_MSVC)
#pragma warning( push )
#pragma warning( disable : 4312 ) // ignoring the warning from /Wp64
#endif
-class Q_CORE_EXPORT QReadLocker
+class QT6_ONLY(Q_CORE_EXPORT) QReadLocker
{
public:
+ Q_NODISCARD_CTOR
inline QReadLocker(QReadWriteLock *readWriteLock);
inline ~QReadLocker()
@@ -125,9 +134,10 @@ inline QReadLocker::QReadLocker(QReadWriteLock *areadWriteLock)
relock();
}
-class Q_CORE_EXPORT QWriteLocker
+class QT6_ONLY(Q_CORE_EXPORT) QWriteLocker
{
public:
+ Q_NODISCARD_CTOR
inline QWriteLocker(QReadWriteLock *readWriteLock);
inline ~QWriteLocker()
@@ -176,7 +186,7 @@ inline QWriteLocker::QWriteLocker(QReadWriteLock *areadWriteLock)
#else // QT_CONFIG(thread)
-class Q_CORE_EXPORT QReadWriteLock
+class QT6_ONLY(Q_CORE_EXPORT) QReadWriteLock
{
public:
enum RecursionMode { NonRecursive, Recursive };
@@ -185,10 +195,12 @@ public:
void lockForRead() noexcept { }
bool tryLockForRead() noexcept { return true; }
+ bool tryLockForRead(QDeadlineTimer) noexcept { return true; }
bool tryLockForRead(int timeout) noexcept { Q_UNUSED(timeout); return true; }
void lockForWrite() noexcept { }
bool tryLockForWrite() noexcept { return true; }
+ bool tryLockForWrite(QDeadlineTimer) noexcept { return true; }
bool tryLockForWrite(int timeout) noexcept { Q_UNUSED(timeout); return true; }
void unlock() noexcept { }
@@ -197,9 +209,10 @@ private:
Q_DISABLE_COPY(QReadWriteLock)
};
-class Q_CORE_EXPORT QReadLocker
+class QT6_ONLY(Q_CORE_EXPORT) QReadLocker
{
public:
+ Q_NODISCARD_CTOR
inline explicit QReadLocker(QReadWriteLock *) noexcept { }
inline ~QReadLocker() noexcept { }
@@ -211,9 +224,10 @@ private:
Q_DISABLE_COPY(QReadLocker)
};
-class Q_CORE_EXPORT QWriteLocker
+class QT6_ONLY(Q_CORE_EXPORT) QWriteLocker
{
public:
+ Q_NODISCARD_CTOR
inline explicit QWriteLocker(QReadWriteLock *) noexcept { }
inline ~QWriteLocker() noexcept { }
diff --git a/src/corelib/thread/qreadwritelock_p.h b/src/corelib/thread/qreadwritelock_p.h
index a4d002b7f2..68fa642a73 100644
--- a/src/corelib/thread/qreadwritelock_p.h
+++ b/src/corelib/thread/qreadwritelock_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
-** 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.
+// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QREADWRITELOCK_P_H
#define QREADWRITELOCK_P_H
@@ -52,23 +16,39 @@
// We mean it.
//
-#include <QtCore/private/qglobal_p.h>
-#include <QtCore/qhash.h>
-#include <QtCore/qwaitcondition.h>
+#include <QtCore/private/qlocking_p.h>
+#include <QtCore/private/qwaitcondition_p.h>
+#include <QtCore/qreadwritelock.h>
+#include <QtCore/qvarlengtharray.h>
QT_REQUIRE_CONFIG(thread);
QT_BEGIN_NAMESPACE
+namespace QReadWriteLockStates {
+enum {
+ StateMask = 0x3,
+ StateLockedForRead = 0x1,
+ StateLockedForWrite = 0x2,
+};
+enum StateForWaitCondition {
+ LockedForRead,
+ LockedForWrite,
+ Unlocked,
+ RecursivelyLocked
+};
+}
+
class QReadWriteLockPrivate
{
public:
explicit QReadWriteLockPrivate(bool isRecursive = false)
: recursive(isRecursive) {}
- QMutex mutex;
- QWaitCondition writerCond;
- QWaitCondition readerCond;
+ alignas(QtPrivate::IdealMutexAlignment) std::condition_variable writerCond;
+ std::condition_variable readerCond;
+
+ alignas(QtPrivate::IdealMutexAlignment) std::mutex mutex;
int readerCount = 0;
int writerCount = 0;
int waitingReaders = 0;
@@ -76,8 +56,8 @@ public:
const bool recursive;
//Called with the mutex locked
- bool lockForWrite(int timeout);
- bool lockForRead(int timeout);
+ bool lockForWrite(std::unique_lock<std::mutex> &lock, QDeadlineTimer timeout);
+ bool lockForRead(std::unique_lock<std::mutex> &lock, QDeadlineTimer timeout);
void unlock();
//memory management
@@ -85,15 +65,47 @@ public:
void release();
static QReadWriteLockPrivate *allocate();
- // Recusive mutex handling
+ // Recursive mutex handling
Qt::HANDLE currentWriter = {};
- QHash<Qt::HANDLE, int> currentReaders;
+
+ struct Reader {
+ Qt::HANDLE handle;
+ int recursionLevel;
+ };
+
+ QVarLengthArray<Reader, 16> currentReaders;
// called with the mutex unlocked
- bool recursiveLockForWrite(int timeout);
- bool recursiveLockForRead(int timeout);
+ bool recursiveLockForWrite(QDeadlineTimer timeout);
+ bool recursiveLockForRead(QDeadlineTimer timeout);
void recursiveUnlock();
+
+ static QReadWriteLockStates::StateForWaitCondition
+ stateForWaitCondition(const QReadWriteLock *lock);
};
+Q_DECLARE_TYPEINFO(QReadWriteLockPrivate::Reader, Q_PRIMITIVE_TYPE);\
+
+/*! \internal Helper for QWaitCondition::wait */
+inline QReadWriteLockStates::StateForWaitCondition
+QReadWriteLockPrivate::stateForWaitCondition(const QReadWriteLock *q)
+{
+ using namespace QReadWriteLockStates;
+ QReadWriteLockPrivate *d = q->d_ptr.loadAcquire();
+ switch (quintptr(d) & StateMask) {
+ case StateLockedForRead: return LockedForRead;
+ case StateLockedForWrite: return LockedForWrite;
+ }
+
+ if (!d)
+ return Unlocked;
+ const auto lock = qt_scoped_lock(d->mutex);
+ if (d->writerCount > 1)
+ return RecursivelyLocked;
+ else if (d->writerCount == 1)
+ return LockedForWrite;
+ return LockedForRead;
+
+}
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qresultstore.cpp b/src/corelib/thread/qresultstore.cpp
index 0b82b938e1..14ed7c6b87 100644
--- a/src/corelib/thread/qresultstore.cpp
+++ b/src/corelib/thread/qresultstore.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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) 2020 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
#include "qresultstore.h"
@@ -44,6 +8,42 @@ QT_BEGIN_NAMESPACE
namespace QtPrivate {
/*!
+ \internal
+
+ Finds result in \a store by \a index
+ */
+static ResultIteratorBase findResult(const QMap<int, ResultItem> &store, int index)
+{
+ if (store.isEmpty())
+ return ResultIteratorBase(store.end());
+ QMap<int, ResultItem>::const_iterator it = store.lowerBound(index);
+
+ // lowerBound returns either an iterator to the result or an iterator
+ // to the nearest greater index. If the latter happens it might be
+ // that the result is stored in a vector at the previous index.
+ if (it == store.end()) {
+ --it;
+ if (it.value().isVector() == false) {
+ return ResultIteratorBase(store.end());
+ }
+ } else {
+ if (it.key() > index) {
+ if (it == store.begin())
+ return ResultIteratorBase(store.end());
+ --it;
+ }
+ }
+
+ const int vectorIndex = index - it.key();
+
+ if (vectorIndex >= it.value().count())
+ return ResultIteratorBase(store.end());
+ else if (it.value().isVector() == false && vectorIndex != 0)
+ return ResultIteratorBase(store.end());
+ return ResultIteratorBase(it, vectorIndex);
+}
+
+/*!
\class QtPrivate::ResultItem
\internal
*/
@@ -108,6 +108,11 @@ bool ResultIteratorBase::canIncrementVectorIndex() const
return (m_vectorIndex + 1 < mapIterator.value().m_count);
}
+bool ResultIteratorBase::isValid() const
+{
+ return mapIterator.value().isValid();
+}
+
ResultStoreBase::ResultStoreBase()
: insertIndex(0), resultCount(0), m_filterMode(false), filteredResults(0) { }
@@ -160,6 +165,15 @@ int ResultStoreBase::insertResultItem(int index, ResultItem &resultItem)
return storeIndex;
}
+bool ResultStoreBase::containsValidResultItem(int index) const
+{
+ // index might refer to either visible or pending result
+ const bool inPending = m_filterMode && index != -1 && index > insertIndex;
+ const auto &store = inPending ? pendingResults : m_results;
+ auto it = findResult(store, index);
+ return it != ResultIteratorBase(store.end()) && it.isValid();
+}
+
void ResultStoreBase::syncPendingResults()
{
// check if we can insert any of the pending results:
@@ -185,6 +199,7 @@ int ResultStoreBase::addResult(int index, const void *result)
int ResultStoreBase::addResults(int index, const void *results, int vectorSize, int totalCount)
{
if (m_filterMode == false || vectorSize == totalCount) {
+ Q_ASSERT(vectorSize != 0);
ResultItem resultItem(results, vectorSize);
return insertResultItem(index, resultItem);
} else {
@@ -214,33 +229,7 @@ bool ResultStoreBase::hasNextResult() const
ResultIteratorBase ResultStoreBase::resultAt(int index) const
{
- if (m_results.isEmpty())
- return ResultIteratorBase(m_results.end());
- QMap<int, ResultItem>::const_iterator it = m_results.lowerBound(index);
-
- // lowerBound returns either an iterator to the result or an iterator
- // to the nearest greater index. If the latter happens it might be
- // that the result is stored in a vector at the previous index.
- if (it == m_results.end()) {
- --it;
- if (it.value().isVector() == false) {
- return ResultIteratorBase(m_results.end());
- }
- } else {
- if (it.key() > index) {
- if (it == m_results.begin())
- return ResultIteratorBase(m_results.end());
- --it;
- }
- }
-
- const int vectorIndex = index - it.key();
-
- if (vectorIndex >= it.value().count())
- return ResultIteratorBase(m_results.end());
- else if (it.value().isVector() == false && vectorIndex != 0)
- return ResultIteratorBase(m_results.end());
- return ResultIteratorBase(it, vectorIndex);
+ return findResult(m_results, index);
}
bool ResultStoreBase::contains(int index) const
diff --git a/src/corelib/thread/qresultstore.h b/src/corelib/thread/qresultstore.h
index 523c745a8e..30ce1fe904 100644
--- a/src/corelib/thread/qresultstore.h
+++ b/src/corelib/thread/qresultstore.h
@@ -1,47 +1,10 @@
-/****************************************************************************
-**
-** 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) 2020 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 QTCORE_RESULTSTORE_H
#define QTCORE_RESULTSTORE_H
#include <QtCore/qmap.h>
-#include <QtCore/qdebug.h>
#include <utility>
@@ -53,7 +16,7 @@ QT_BEGIN_NAMESPACE
ResultStore stores indexed results. Results can be added and retrieved
either individually batched in a QList. Retriveing results and checking
which indexes are in the store can be done either by iterating or by random
- accees. In addition results kan be removed from the front of the store,
+ access. In addition results can be removed from the front of the store,
either individually or in batches.
*/
@@ -62,7 +25,7 @@ namespace QtPrivate {
class ResultItem
{
public:
- ResultItem(const void *_result, int _count) : m_count(_count), result(_result) { } // contruct with vector of results
+ ResultItem(const void *_result, int _count) : m_count(_count), result(_result) { } // construct with vector of results
ResultItem(const void *_result) : m_count(0), result(_result) { } // construct with result
ResultItem() : m_count(0), result(nullptr) { }
bool isValid() const { return result != nullptr; }
@@ -87,6 +50,8 @@ public:
bool operator!=(const ResultIteratorBase &other) const;
bool isVector() const;
bool canIncrementVectorIndex() const;
+ bool isValid() const;
+
protected:
QMap<int, ResultItem>::const_iterator mapIterator;
int m_vectorIndex;
@@ -106,7 +71,7 @@ public:
template <typename T>
T *pointer()
{
- const T *p = qAsConst(*this).pointer<T>();
+ const T *p = std::as_const(*this).pointer<T>();
return const_cast<T *>(p);
}
@@ -120,7 +85,7 @@ public:
}
};
-class Q_CORE_EXPORT ResultStoreBase
+class Q_CORE_EXPORT ResultStoreBase final
{
public:
ResultStoreBase();
@@ -134,11 +99,14 @@ public:
ResultIteratorBase resultAt(int index) const;
bool contains(int index) const;
int count() const;
+ // ### Qt 7: 'virtual' isn't required, can be removed, along with renaming
+ // the class to ResultStore and changing the members below to be private.
virtual ~ResultStoreBase();
protected:
int insertResultItem(int index, ResultItem &resultItem);
void insertResultItemIfValid(int index, ResultItem &resultItem);
+ bool containsValidResultItem(int index) const;
void syncPendingResults();
void syncResultCount();
int updateInsertIndex(int index, int _count);
@@ -151,10 +119,35 @@ protected:
QMap<int, ResultItem> pendingResults;
int filteredResults;
+ template <typename T>
+ static void clear(QMap<int, ResultItem> &store)
+ {
+ QMap<int, ResultItem>::const_iterator mapIterator = store.constBegin();
+ while (mapIterator != store.constEnd()) {
+ if (mapIterator.value().isVector())
+ delete reinterpret_cast<const QList<T> *>(mapIterator.value().result);
+ else
+ delete reinterpret_cast<const T *>(mapIterator.value().result);
+ ++mapIterator;
+ }
+ store.clear();
+ }
+
public:
+ template <typename T, typename...Args>
+ int emplaceResult(int index, Args&&...args)
+ {
+ if (containsValidResultItem(index)) // reject if already present
+ return -1;
+ return addResult(index, static_cast<void *>(new T(std::forward<Args>(args)...)));
+ }
+
template <typename T>
int addResult(int index, const T *result)
{
+ if (containsValidResultItem(index)) // reject if already present
+ return -1;
+
if (result == nullptr)
return addResult(index, static_cast<void *>(nullptr));
@@ -164,32 +157,53 @@ public:
template <typename T>
int moveResult(int index, T &&result)
{
- return addResult(index, static_cast<void *>(new T(std::move_if_noexcept(result))));
+ static_assert(!std::is_reference_v<T>, "trying to move from an lvalue!");
+
+ return emplaceResult<std::remove_cv_t<T>>(index, std::forward<T>(result));
}
template<typename T>
int addResults(int index, const QList<T> *results)
{
- return addResults(index, new QList<T>(*results), results->count(), results->count());
+ if (results->empty()) // reject if results are empty
+ return -1;
+
+ if (containsValidResultItem(index)) // reject if already present
+ return -1;
+
+ return addResults(index, new QList<T>(*results), results->size(), results->size());
}
template<typename T>
int addResults(int index, const QList<T> *results, int totalCount)
{
- if (m_filterMode == true && results->count() != totalCount && 0 == results->count())
+ // reject if results are empty, and nothing is filtered away
+ if ((m_filterMode == false || results->size() == totalCount) && results->empty())
+ return -1;
+
+ if (containsValidResultItem(index)) // reject if already present
+ return -1;
+
+ if (m_filterMode == true && results->size() != totalCount && 0 == results->size())
return addResults(index, nullptr, 0, totalCount);
- return addResults(index, new QList<T>(*results), results->count(), totalCount);
+ return addResults(index, new QList<T>(*results), results->size(), totalCount);
}
int addCanceledResult(int index)
{
+ if (containsValidResultItem(index)) // reject if already present
+ return -1;
+
return addResult(index, static_cast<void *>(nullptr));
}
template <typename T>
int addCanceledResults(int index, int _count)
{
+ if (containsValidResultItem(index)) // reject if already present
+ return -1;
+
QList<T> empty;
return addResults(index, &empty, _count);
}
@@ -197,16 +211,11 @@ public:
template <typename T>
void clear()
{
- QMap<int, ResultItem>::const_iterator mapIterator = m_results.constBegin();
- while (mapIterator != m_results.constEnd()) {
- if (mapIterator.value().isVector())
- delete reinterpret_cast<const QList<T> *>(mapIterator.value().result);
- else
- delete reinterpret_cast<const T *>(mapIterator.value().result);
- ++mapIterator;
- }
+ ResultStoreBase::clear<T>(m_results);
resultCount = 0;
- m_results.clear();
+ insertIndex = 0;
+ ResultStoreBase::clear<T>(pendingResults);
+ filteredResults = 0;
}
};
diff --git a/src/corelib/thread/qrunnable.cpp b/src/corelib/thread/qrunnable.cpp
index 32f3cd657e..f9d69ed5cb 100644
--- a/src/corelib/thread/qrunnable.cpp
+++ b/src/corelib/thread/qrunnable.cpp
@@ -1,44 +1,10 @@
-/****************************************************************************
-**
-** 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
#include "qrunnable.h"
+#include <QtCore/qlogging.h>
+
QT_BEGIN_NAMESPACE
QRunnable::~QRunnable()
@@ -46,6 +12,27 @@ QRunnable::~QRunnable()
}
/*!
+ \internal
+ Prints a warning and returns nullptr.
+*/
+QRunnable *QRunnable::warnNullCallable()
+{
+ qWarning("Trying to create null QRunnable. This may stop working.");
+ return nullptr;
+}
+
+
+QRunnable::QGenericRunnable::~QGenericRunnable()
+{
+ runHelper->destroy();
+}
+
+void QRunnable::QGenericRunnable::run()
+{
+ runHelper->run();
+}
+
+/*!
\class QRunnable
\inmodule QtCore
\since 4.4
@@ -113,31 +100,20 @@ QRunnable::~QRunnable()
\sa autoDelete(), QThreadPool
*/
-class FunctionRunnable : public QRunnable
-{
- std::function<void()> m_functionToRun;
-public:
- FunctionRunnable(std::function<void()> functionToRun) : m_functionToRun(std::move(functionToRun))
- {
- }
- void run() override
- {
- m_functionToRun();
- }
-};
-
/*!
+ \fn template<typename Callable, QRunnable::if_callable<Callable>> QRunnable *QRunnable::create(Callable &&callableToRun);
\since 5.15
- Creates a QRunnable that calls \a functionToRun in run().
+ Creates a QRunnable that calls \a callableToRun in run().
Auto-deletion is enabled by default.
+ \note This function participates in overload resolution only if \c Callable
+ is a function or function object which can be called with zero arguments.
+
+ \note In Qt versions prior to 6.6, this method took copyable functions only.
+
\sa run(), autoDelete()
*/
-QRunnable *QRunnable::create(std::function<void()> functionToRun)
-{
- return new FunctionRunnable(std::move(functionToRun));
-}
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qrunnable.h b/src/corelib/thread/qrunnable.h
index 9f8ab2a3d0..f0dd0a582e 100644
--- a/src/corelib/thread/qrunnable.h
+++ b/src/corelib/thread/qrunnable.h
@@ -1,69 +1,137 @@
-/****************************************************************************
-**
-** 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) 2023 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 QRUNNABLE_H
#define QRUNNABLE_H
-#include <QtCore/qglobal.h>
+#include <QtCore/qcompilerdetection.h>
+#include <QtCore/qfunctionaltools_impl.h>
+#include <QtCore/qtclasshelpermacros.h>
+#include <QtCore/qtcoreexports.h>
+
#include <functional>
+#include <type_traits>
QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QRunnable
{
- int ref; // Qt6: Make this a bool, or make autoDelete() virtual.
+ bool m_autoDelete = true;
- friend class QThreadPool;
- friend class QThreadPoolPrivate;
- friend class QThreadPoolThread;
Q_DISABLE_COPY(QRunnable)
public:
virtual void run() = 0;
- QRunnable() : ref(0) { }
+ constexpr QRunnable() noexcept = default;
virtual ~QRunnable();
+#if QT_CORE_REMOVED_SINCE(6, 6)
static QRunnable *create(std::function<void()> functionToRun);
+#endif
+ template <typename Callable>
+ using if_callable = std::enable_if_t<std::is_invocable_r_v<void, Callable>, bool>;
+
+ template <typename Callable, if_callable<Callable> = true>
+ static QRunnable *create(Callable &&functionToRun);
+ static QRunnable *create(std::nullptr_t) = delete;
- bool autoDelete() const { return ref != -1; }
- void setAutoDelete(bool _autoDelete) { ref = _autoDelete ? 0 : -1; }
+ bool autoDelete() const { return m_autoDelete; }
+ void setAutoDelete(bool autoDelete) { m_autoDelete = autoDelete; }
+
+private:
+ static Q_DECL_COLD_FUNCTION QRunnable *warnNullCallable();
+ class QGenericRunnable;
};
+class Q_CORE_EXPORT QRunnable::QGenericRunnable : public QRunnable
+{
+ // Type erasure, to only instantiate a non-virtual class per Callable:
+ class HelperBase
+ {
+ protected:
+ enum class Op {
+ Run,
+ Destroy,
+ };
+ using OpFn = void* (*)(Op, HelperBase *, void*);
+ OpFn fn;
+ protected:
+ constexpr explicit HelperBase(OpFn f) noexcept : fn(f) {}
+ ~HelperBase() = default;
+ public:
+ void run() { fn(Op::Run, this, nullptr); }
+ void destroy() { fn(Op::Destroy, this, nullptr); }
+ };
+
+ template <typename Callable>
+ class Helper : public HelperBase, private QtPrivate::CompactStorage<Callable>
+ {
+ using Storage = QtPrivate::CompactStorage<Callable>;
+ static void *impl(Op op, HelperBase *that, [[maybe_unused]] void *arg)
+ {
+ const auto _this = static_cast<Helper*>(that);
+ switch (op) {
+ case Op::Run: _this->object()(); break;
+ case Op::Destroy: delete _this; break;
+ }
+ return nullptr;
+ }
+ public:
+ template <typename UniCallable>
+ explicit Helper(UniCallable &&functionToRun) noexcept
+ : HelperBase(&impl),
+ Storage{std::forward<UniCallable>(functionToRun)}
+ {
+ }
+ };
+
+ HelperBase *runHelper;
+public:
+ template <typename Callable, if_callable<Callable> = true>
+ explicit QGenericRunnable(Callable &&c)
+ : runHelper(new Helper<std::decay_t<Callable>>(std::forward<Callable>(c)))
+ {
+ }
+ ~QGenericRunnable() override;
+
+ void run() override;
+};
+
+namespace QtPrivate {
+
+template <typename T>
+constexpr inline bool is_function_pointer_v = std::conjunction_v<
+ std::is_pointer<T>,
+ std::is_function<std::remove_pointer_t<T>>
+ >;
+template <typename T>
+constexpr inline bool is_std_function_v = false;
+template <typename T>
+constexpr inline bool is_std_function_v<std::function<T>> = true;
+
+} // namespace QtPrivate
+
+template <typename Callable, QRunnable::if_callable<Callable>>
+QRunnable *QRunnable::create(Callable &&functionToRun)
+{
+ using F = std::decay_t<Callable>;
+ constexpr bool is_std_function = QtPrivate::is_std_function_v<F>;
+ constexpr bool is_function_pointer = QtPrivate::is_function_pointer_v<F>;
+ if constexpr (is_std_function || is_function_pointer) {
+ bool is_null;
+ if constexpr (is_std_function) {
+ is_null = !functionToRun;
+ } else if constexpr (is_function_pointer) {
+ // shut up warnings about functions always having a non-null address:
+ const void *functionPtr = reinterpret_cast<void *>(functionToRun);
+ is_null = !functionPtr;
+ }
+ if (is_null)
+ return warnNullCallable();
+ }
+
+ return new QGenericRunnable(std::forward<Callable>(functionToRun));
+}
+
QT_END_NAMESPACE
#endif
diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp
index d4fb756b94..72781942ac 100644
--- a/src/corelib/thread/qsemaphore.cpp
+++ b/src/corelib/thread/qsemaphore.cpp
@@ -1,49 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Copyright (C) 2018 Intel Corporation.
-** 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) 2022 The Qt Company Ltd.
+// Copyright (C) 2018 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsemaphore.h"
-#include "qmutex.h"
#include "qfutex_p.h"
-#include "qwaitcondition.h"
#include "qdeadlinetimer.h"
#include "qdatetime.h"
+#include "qdebug.h"
+#include "qlocking_p.h"
+#include "qwaitcondition_p.h"
+
+#include <chrono>
QT_BEGIN_NAMESPACE
@@ -83,7 +50,7 @@ using namespace QtFutex;
A typical application of semaphores is for controlling access to
a circular buffer shared by a producer thread and a consumer
- thread. The \l{Semaphores Example} shows how
+ thread. The \l{Producer and Consumer using Semaphores} example shows how
to use QSemaphore to solve that problem.
A non-computing example of a semaphore would be dining at a
@@ -96,7 +63,8 @@ using namespace QtFutex;
seated (taking the available seats to 5, making the party of 10
people wait longer).
- \sa QSemaphoreReleaser, QMutex, QWaitCondition, QThread, {Semaphores Example}
+ \sa QSemaphoreReleaser, QMutex, QWaitCondition, QThread,
+ {Producer and Consumer using Semaphores}
*/
/*
@@ -127,13 +95,13 @@ using namespace QtFutex;
*/
#if defined(FUTEX_OP) && QT_POINTER_SIZE > 4
-static Q_CONSTEXPR bool futexHasWaiterCount = true;
+static constexpr bool futexHasWaiterCount = true;
#else
-static Q_CONSTEXPR bool futexHasWaiterCount = false;
+static constexpr bool futexHasWaiterCount = false;
#endif
-static const quintptr futexNeedsWakeAllBit =
- Q_UINT64_C(1) << (sizeof(quintptr) * CHAR_BIT - 1);
+static constexpr quintptr futexNeedsWakeAllBit = futexHasWaiterCount ?
+ (Q_UINT64_C(1) << (sizeof(quintptr) * CHAR_BIT - 1)) : 0x80000000U;
static int futexAvailCounter(quintptr v)
{
@@ -150,10 +118,11 @@ static int futexAvailCounter(quintptr v)
static bool futexNeedsWake(quintptr v)
{
- // If we're counting waiters, the number of waiters is stored in the low 31
- // bits of the high word (that is, bits 32-62). If we're not, then we use
- // bit 31 to indicate anyone is waiting. Either way, if any bit 31 or above
- // is set, there are waiters.
+ // If we're counting waiters, the number of waiters plus value is stored in the
+ // low 31 bits of the high word (that is, bits 32-62). If we're not, then we only
+ // use futexNeedsWakeAllBit to indicate anyone is waiting.
+ if constexpr (futexHasWaiterCount)
+ return unsigned(quint64(v) >> 32) > unsigned(v);
return v >> 31;
}
@@ -168,6 +137,7 @@ static QBasicAtomicInteger<quint32> *futexLow32(QBasicAtomicInteger<quintptr> *p
static QBasicAtomicInteger<quint32> *futexHigh32(QBasicAtomicInteger<quintptr> *ptr)
{
+ Q_ASSERT(futexHasWaiterCount);
auto result = reinterpret_cast<QBasicAtomicInteger<quint32> *>(ptr);
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN && QT_POINTER_SIZE > 4
++result;
@@ -176,43 +146,28 @@ static QBasicAtomicInteger<quint32> *futexHigh32(QBasicAtomicInteger<quintptr> *
}
template <bool IsTimed> bool
-futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValue, quintptr nn, int timeout)
+futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValue, quintptr nn,
+ QDeadlineTimer timer)
{
- QDeadlineTimer timer(IsTimed ? QDeadlineTimer(timeout) : QDeadlineTimer());
- qint64 remainingTime = timeout * Q_INT64_C(1000) * 1000;
+ using namespace std::chrono;
int n = int(unsigned(nn));
// we're called after one testAndSet, so start by waiting first
- goto start_wait;
-
- forever {
- if (futexAvailCounter(curValue) >= n) {
- // try to acquire
- quintptr newValue = curValue - nn;
- if (u.testAndSetOrdered(curValue, newValue, curValue))
- return true; // succeeded!
- continue;
- }
-
- // not enough tokens available, put us to wait
- if (remainingTime == 0)
- return false;
-
+ for (;;) {
// indicate we're waiting
-start_wait:
auto ptr = futexLow32(&u);
if (n > 1 || !futexHasWaiterCount) {
u.fetchAndOrRelaxed(futexNeedsWakeAllBit);
curValue |= futexNeedsWakeAllBit;
- if (n > 1 && futexHasWaiterCount) {
+ if constexpr (futexHasWaiterCount) {
+ Q_ASSERT(n > 1);
ptr = futexHigh32(&u);
- //curValue >>= 32; // but this is UB in 32-bit, so roundabout:
curValue = quint64(curValue) >> 32;
}
}
- if (IsTimed && remainingTime > 0) {
- bool timedout = !futexWait(*ptr, curValue, remainingTime);
+ if (IsTimed) {
+ bool timedout = !futexWait(*ptr, curValue, timer);
if (timedout)
return false;
} else {
@@ -220,13 +175,27 @@ start_wait:
}
curValue = u.loadAcquire();
- if (IsTimed)
- remainingTime = timer.remainingTimeNSecs();
+
+ // try to acquire
+ while (futexAvailCounter(curValue) >= n) {
+ quintptr newValue = curValue - nn;
+ if (u.testAndSetOrdered(curValue, newValue, curValue))
+ return true; // succeeded!
+ }
+
+ // not enough tokens available, put us to wait
+ if (IsTimed && timer.hasExpired())
+ return false;
}
}
-template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintptr> &u, int n, int timeout)
+static constexpr QDeadlineTimer::ForeverConstant Expired =
+ QDeadlineTimer::ForeverConstant(1);
+
+template <typename T> bool
+futexSemaphoreTryAcquire(QBasicAtomicInteger<quintptr> &u, int n, T timeout)
{
+ constexpr bool IsTimed = std::is_same_v<QDeadlineTimer, T>;
// Try to acquire without waiting (we still loop because the testAndSet
// call can fail).
quintptr nn = unsigned(n);
@@ -240,19 +209,27 @@ template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintp
if (u.testAndSetOrdered(curValue, newValue, curValue))
return true; // succeeded!
}
- if (timeout == 0)
- return false;
+ if constexpr (IsTimed) {
+ if (timeout.hasExpired())
+ return false;
+ } else {
+ if (timeout == Expired)
+ return false;
+ }
// we need to wait
- quintptr oneWaiter = quintptr(Q_UINT64_C(1) << 32); // zero on 32-bit
- if (futexHasWaiterCount) {
- // increase the waiter count
- u.fetchAndAddRelaxed(oneWaiter);
-
+ constexpr quintptr oneWaiter = quintptr(Q_UINT64_C(1) << 32); // zero on 32-bit
+ if constexpr (futexHasWaiterCount) {
// We don't use the fetched value from above so futexWait() fails if
// it changed after the testAndSetOrdered above.
- if ((quint64(curValue) >> 32) == 0x7fffffff)
- return false; // overflow!
+ quint32 waiterCount = (quint64(curValue) >> 32) & 0x7fffffffU;
+ if (waiterCount == 0x7fffffffU) {
+ qCritical() << "Waiter count overflow in QSemaphore";
+ return false;
+ }
+
+ // increase the waiter count
+ u.fetchAndAddRelaxed(oneWaiter);
curValue += oneWaiter;
// Also adjust nn to subtract oneWaiter when we succeed in acquiring.
@@ -262,6 +239,8 @@ template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintp
if (futexSemaphoreTryAcquire_loop<IsTimed>(u, curValue, nn, timeout))
return true;
+ Q_ASSERT(IsTimed);
+
if (futexHasWaiterCount) {
// decrement the number of threads waiting
Q_ASSERT(futexHigh32(&u)->loadRelaxed() & 0x7fffffffU);
@@ -270,14 +249,31 @@ template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintp
return false;
}
-class QSemaphorePrivate {
-public:
- inline QSemaphorePrivate(int n) : avail(n) { }
+namespace QtSemaphorePrivate {
+using namespace QtPrivate;
+struct Layout1
+{
+ alignas(IdealMutexAlignment) std::mutex mutex;
+ qsizetype avail = 0;
+ alignas(IdealMutexAlignment) std::condition_variable cond;
+};
+
+struct Layout2
+{
+ alignas(IdealMutexAlignment) std::mutex mutex;
+ alignas(IdealMutexAlignment) std::condition_variable cond;
+ qsizetype avail = 0;
+};
- QMutex mutex;
- QWaitCondition cond;
+// Choose Layout1 if it is smaller than Layout2. That happens for platforms
+// where sizeof(mutex) is 64.
+using Members = std::conditional_t<sizeof(Layout1) <= sizeof(Layout2), Layout1, Layout2>;
+} // namespace QtSemaphorePrivate
- int avail;
+class QSemaphorePrivate : public QtSemaphorePrivate::Members
+{
+public:
+ explicit QSemaphorePrivate(qsizetype n) { avail = n; }
};
/*!
@@ -320,16 +316,22 @@ QSemaphore::~QSemaphore()
*/
void QSemaphore::acquire(int n)
{
+#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
+# warning "Move the Q_ASSERT to inline code, make QSemaphore have wide contract, " \
+ "and mark noexcept where futexes are in use."
+#else
Q_ASSERT_X(n >= 0, "QSemaphore::acquire", "parameter 'n' must be non-negative");
+#endif
if (futexAvailable()) {
- futexSemaphoreTryAcquire<false>(u, n, -1);
+ futexSemaphoreTryAcquire(u, n, QDeadlineTimer::Forever);
return;
}
- QMutexLocker locker(&d->mutex);
- while (n > d->avail)
- d->cond.wait(locker.mutex());
+ const auto sufficientResourcesAvailable = [this, n] { return d->avail >= n; };
+
+ auto locker = qt_unique_lock(d->mutex);
+ d->cond.wait(locker, sufficientResourcesAvailable);
d->avail -= n;
}
@@ -354,66 +356,56 @@ void QSemaphore::release(int n)
quintptr nn = unsigned(n);
if (futexHasWaiterCount)
nn |= quint64(nn) << 32; // token count replicated in high word
- quintptr prevValue = u.fetchAndAddRelease(nn);
+ quintptr prevValue = u.loadRelaxed();
+ quintptr newValue;
+ do { // loop just to ensure the operations are done atomically
+ newValue = prevValue + nn;
+ newValue &= (futexNeedsWakeAllBit - 1);
+ } while (!u.testAndSetRelease(prevValue, newValue, prevValue));
if (futexNeedsWake(prevValue)) {
#ifdef FUTEX_OP
- if (!futexHasWaiterCount) {
- /*
- On 32-bit systems, all waiters are waiting on the same address,
- so we'll wake them all and ask the kernel to clear the high bit.
-
- atomic {
- int oldval = u;
- u = oldval & ~(1 << 31);
- futexWake(u, INT_MAX);
- if (oldval == 0) // impossible condition
- futexWake(u, INT_MAX);
- }
- */
- quint32 op = FUTEX_OP_ANDN | FUTEX_OP_OPARG_SHIFT;
- quint32 oparg = 31;
- quint32 cmp = FUTEX_OP_CMP_EQ;
- quint32 cmparg = 0;
- futexWakeOp(u, INT_MAX, INT_MAX, u, FUTEX_OP(op, oparg, cmp, cmparg));
- } else {
+ if (futexHasWaiterCount) {
/*
On 64-bit systems, the single-token waiters wait on the low half
and the multi-token waiters wait on the upper half. So we ask
the kernel to wake up n single-token waiters and all multi-token
- waiters (if any), then clear the multi-token wait bit.
+ waiters (if any), and clear the multi-token wait bit.
atomic {
int oldval = *upper;
- *upper = oldval & ~(1 << 31);
+ *upper = oldval | 0;
futexWake(lower, n);
- if (oldval < 0) // sign bit set
+ if (oldval != 0) // always true
futexWake(upper, INT_MAX);
}
*/
- quint32 op = FUTEX_OP_ANDN | FUTEX_OP_OPARG_SHIFT;
- quint32 oparg = 31;
- quint32 cmp = FUTEX_OP_CMP_LT;
+ quint32 op = FUTEX_OP_OR;
+ quint32 oparg = 0;
+ quint32 cmp = FUTEX_OP_CMP_NE;
quint32 cmparg = 0;
futexWakeOp(*futexLow32(&u), n, INT_MAX, *futexHigh32(&u), FUTEX_OP(op, oparg, cmp, cmparg));
+ return;
}
-#else
- // Unset the bit and wake everyone. There are two possibibilies
+#endif
+ // Unset the bit and wake everyone. There are two possibilities
// under which a thread can set the bit between the AND and the
// futexWake:
// 1) it did see the new counter value, but it wasn't enough for
// its acquisition anyway, so it has to wait;
// 2) it did not see the new counter value, in which case its
// futexWait will fail.
- u.fetchAndAndRelease(futexNeedsWakeAllBit - 1);
- futexWakeAll(u);
-#endif
+ futexWakeAll(*futexLow32(&u));
+ if (futexHasWaiterCount)
+ futexWakeAll(*futexHigh32(&u));
}
return;
}
- QMutexLocker locker(&d->mutex);
+ // Keep mutex locked until after notify_all() lest another thread acquire()s
+ // the semaphore once d->avail == 0 and then destroys it, leaving `d` dangling.
+ const auto locker = qt_scoped_lock(d->mutex);
d->avail += n;
- d->cond.wakeAll();
+ d->cond.notify_all();
}
/*!
@@ -427,7 +419,7 @@ int QSemaphore::available() const
if (futexAvailable())
return futexAvailCounter(u.loadRelaxed());
- QMutexLocker locker(&d->mutex);
+ const auto locker = qt_scoped_lock(d->mutex);
return d->avail;
}
@@ -447,9 +439,9 @@ bool QSemaphore::tryAcquire(int n)
Q_ASSERT_X(n >= 0, "QSemaphore::tryAcquire", "parameter 'n' must be non-negative");
if (futexAvailable())
- return futexSemaphoreTryAcquire<false>(u, n, 0);
+ return futexSemaphoreTryAcquire(u, n, Expired);
- QMutexLocker locker(&d->mutex);
+ const auto locker = qt_scoped_lock(d->mutex);
if (n > d->avail)
return false;
d->avail -= n;
@@ -457,6 +449,8 @@ bool QSemaphore::tryAcquire(int n)
}
/*!
+ \fn QSemaphore::tryAcquire(int n, int timeout)
+
Tries to acquire \c n resources guarded by the semaphore and
returns \c true on success. If available() < \a n, this call will
wait for at most \a timeout milliseconds for resources to become
@@ -472,30 +466,89 @@ bool QSemaphore::tryAcquire(int n)
\sa acquire()
*/
-bool QSemaphore::tryAcquire(int n, int timeout)
+
+/*!
+ \since 6.6
+
+ Tries to acquire \c n resources guarded by the semaphore and returns \c
+ true on success. If available() < \a n, this call will wait until \a timer
+ expires for resources to become available.
+
+ Example:
+
+ \snippet code/src_corelib_thread_qsemaphore.cpp tryAcquire-QDeadlineTimer
+
+ \sa acquire()
+*/
+bool QSemaphore::tryAcquire(int n, QDeadlineTimer timer)
{
- Q_ASSERT_X(n >= 0, "QSemaphore::tryAcquire", "parameter 'n' must be non-negative");
+ if (timer.isForever()) {
+ acquire(n);
+ return true;
+ }
+
+ if (timer.hasExpired())
+ return tryAcquire(n);
- // We're documented to accept any negative value as "forever"
- // but QDeadlineTimer only accepts -1.
- timeout = qMax(timeout, -1);
+ Q_ASSERT_X(n >= 0, "QSemaphore::tryAcquire", "parameter 'n' must be non-negative");
if (futexAvailable())
- return futexSemaphoreTryAcquire<true>(u, n, timeout);
+ return futexSemaphoreTryAcquire(u, n, timer);
- QDeadlineTimer timer(timeout);
- QMutexLocker locker(&d->mutex);
- while (n > d->avail && !timer.hasExpired()) {
- if (!d->cond.wait(locker.mutex(), timer))
- return false;
- }
- if (n > d->avail)
+ using namespace std::chrono;
+ const auto sufficientResourcesAvailable = [this, n] { return d->avail >= n; };
+
+ auto locker = qt_unique_lock(d->mutex);
+ if (!d->cond.wait_until(locker, timer.deadline<steady_clock>(), sufficientResourcesAvailable))
return false;
d->avail -= n;
return true;
+}
+/*!
+ \fn template <typename Rep, typename Period> QSemaphore::tryAcquire(int n, std::chrono::duration<Rep, Period> timeout)
+ \overload
+ \since 6.3
+*/
-}
+/*!
+ \fn bool QSemaphore::try_acquire()
+ \since 6.3
+
+ This function is provided for \c{std::counting_semaphore} compatibility.
+
+ It is equivalent to calling \c{tryAcquire(1)}, where the function returns
+ \c true on acquiring the resource successfully.
+
+ \sa tryAcquire(), try_acquire_for(), try_acquire_until()
+*/
+
+/*!
+ \fn template <typename Rep, typename Period> bool QSemaphore::try_acquire_for(const std::chrono::duration<Rep, Period> &timeout)
+ \since 6.3
+
+ This function is provided for \c{std::counting_semaphore} compatibility.
+
+ It is equivalent to calling \c{tryAcquire(1, timeout)}, where the call
+ times out on the given \a timeout value. The function returns \c true
+ on acquiring the resource successfully.
+
+ \sa tryAcquire(), try_acquire(), try_acquire_until()
+*/
+
+/*!
+ \fn template <typename Clock, typename Duration> bool QSemaphore::try_acquire_until(const std::chrono::time_point<Clock, Duration> &tp)
+ \since 6.3
+
+ This function is provided for \c{std::counting_semaphore} compatibility.
+
+ It is equivalent to calling \c{tryAcquire(1, tp - Clock::now())},
+ which means that the \a tp (time point) is recorded, ignoring the
+ adjustments to \c{Clock} while waiting. The function returns \c true
+ on acquiring the resource successfully.
+
+ \sa tryAcquire(), try_acquire(), try_acquire_for()
+*/
/*!
\class QSemaphoreReleaser
@@ -594,7 +647,7 @@ bool QSemaphore::tryAcquire(int n, int timeout)
/*!
\fn QSemaphoreReleaser::swap(QSemaphoreReleaser &other)
- Exchanges the responsibilites of \c{*this} and \a other.
+ Exchanges the responsibilities of \c{*this} and \a other.
Unlike move assignment, neither of the two objects ever releases its
semaphore, if any, as a consequence of swapping.
diff --git a/src/corelib/thread/qsemaphore.h b/src/corelib/thread/qsemaphore.h
index b3b9b52052..dda722a582 100644
--- a/src/corelib/thread/qsemaphore.h
+++ b/src/corelib/thread/qsemaphore.h
@@ -1,53 +1,19 @@
-/****************************************************************************
-**
-** 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 QSEMAPHORE_H
#define QSEMAPHORE_H
#include <QtCore/qglobal.h>
+#include <QtCore/qdeadlinetimer.h>
+
+#include <chrono>
QT_REQUIRE_CONFIG(thread);
QT_BEGIN_NAMESPACE
class QSemaphorePrivate;
-
class Q_CORE_EXPORT QSemaphore
{
public:
@@ -56,33 +22,62 @@ public:
void acquire(int n = 1);
bool tryAcquire(int n = 1);
+ QT_CORE_INLINE_SINCE(6, 6)
bool tryAcquire(int n, int timeout);
+ bool tryAcquire(int n, QDeadlineTimer timeout);
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ template <typename Rep, typename Period>
+ bool tryAcquire(int n, std::chrono::duration<Rep, Period> timeout)
+ { return tryAcquire(n, QDeadlineTimer(timeout)); }
+#endif
void release(int n = 1);
int available() const;
+ // std::counting_semaphore compatibility:
+ bool try_acquire() noexcept { return tryAcquire(); }
+ template <typename Rep, typename Period>
+ bool try_acquire_for(const std::chrono::duration<Rep, Period> &timeout)
+ { return tryAcquire(1, timeout); }
+ template <typename Clock, typename Duration>
+ bool try_acquire_until(const std::chrono::time_point<Clock, Duration> &tp)
+ {
+ return try_acquire_for(tp - Clock::now());
+ }
private:
Q_DISABLE_COPY(QSemaphore)
union {
QSemaphorePrivate *d;
- QBasicAtomicInteger<quintptr> u; // ### Qt6: make 64-bit
+ QBasicAtomicInteger<quintptr> u;
+ QBasicAtomicInteger<quint32> u32[2];
+ QBasicAtomicInteger<quint64> u64;
};
};
+#if QT_CORE_INLINE_IMPL_SINCE(6, 6)
+bool QSemaphore::tryAcquire(int n, int timeout)
+{
+ return tryAcquire(n, QDeadlineTimer(timeout));
+}
+#endif
+
class QSemaphoreReleaser
{
public:
+ Q_NODISCARD_CTOR
QSemaphoreReleaser() = default;
+ Q_NODISCARD_CTOR
explicit QSemaphoreReleaser(QSemaphore &sem, int n = 1) noexcept
: m_sem(&sem), m_n(n) {}
+ Q_NODISCARD_CTOR
explicit QSemaphoreReleaser(QSemaphore *sem, int n = 1) noexcept
: m_sem(sem), m_n(n) {}
+ Q_NODISCARD_CTOR
QSemaphoreReleaser(QSemaphoreReleaser &&other) noexcept
: m_sem(other.cancel()), m_n(other.m_n) {}
- QSemaphoreReleaser &operator=(QSemaphoreReleaser &&other) noexcept
- { QSemaphoreReleaser moved(std::move(other)); swap(moved); return *this; }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QSemaphoreReleaser)
~QSemaphoreReleaser()
{
@@ -92,8 +87,8 @@ public:
void swap(QSemaphoreReleaser &other) noexcept
{
- qSwap(m_sem, other.m_sem);
- qSwap(m_n, other.m_n);
+ qt_ptr_swap(m_sem, other.m_sem);
+ std::swap(m_n, other.m_n);
}
QSemaphore *semaphore() const noexcept
@@ -101,7 +96,7 @@ public:
QSemaphore *cancel() noexcept
{
- return qExchange(m_sem, nullptr);
+ return std::exchange(m_sem, nullptr);
}
private:
diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp
index b136a76aeb..8d8f353aaa 100644
--- a/src/corelib/thread/qthread.cpp
+++ b/src/corelib/thread/qthread.cpp
@@ -1,48 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** 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.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qthread.h"
#include "qthreadstorage.h"
#include "qmutex.h"
#include "qreadwritelock.h"
#include "qabstracteventdispatcher.h"
+#include "qbindingstorage.h"
#include <qeventloop.h>
@@ -54,6 +19,29 @@
QT_BEGIN_NAMESPACE
/*
+ QPostEventList
+*/
+
+void QPostEventList::addEvent(const QPostEvent &ev)
+{
+ int priority = ev.priority;
+ if (isEmpty() ||
+ constLast().priority >= priority ||
+ insertionOffset >= size()) {
+ // optimization: we can simply append if the last event in
+ // the queue has higher or equal priority
+ append(ev);
+ } else {
+ // insert event in descending priority order, using upper
+ // bound for a given priority (to ensure proper ordering
+ // of events with the same priority)
+ QPostEventList::iterator at = std::upper_bound(begin() + insertionOffset, end(), ev);
+ insert(at, ev);
+ }
+}
+
+
+/*
QThreadData
*/
@@ -77,9 +65,10 @@ QThreadData::~QThreadData()
// crashing during QCoreApplicationData's global static cleanup we need to
// safeguard the main thread here.. This fix is a bit crude, but it solves
// the problem...
- if (this->thread.loadAcquire() == QCoreApplicationPrivate::theMainThread.loadAcquire()) {
- QCoreApplicationPrivate::theMainThread.storeRelease(nullptr);
- QThreadData::clearCurrentThreadData();
+ if (threadId.loadAcquire() == QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
+ QCoreApplicationPrivate::theMainThread.storeRelease(nullptr);
+ QCoreApplicationPrivate::theMainThreadId.storeRelaxed(nullptr);
+ QThreadData::clearCurrentThreadData();
}
// ~QThread() sets thread to nullptr, so if it isn't null here, it's
@@ -97,7 +86,7 @@ QThreadData::~QThreadData()
const QPostEvent &pe = postEventList.at(i);
if (pe.event) {
--pe.receiver->d_func()->postedEvents;
- pe.event->posted = false;
+ pe.event->m_posted = false;
delete pe.event;
}
}
@@ -125,7 +114,6 @@ QAbstractEventDispatcher *QThreadData::createEventDispatcher()
{
QAbstractEventDispatcher *ed = QThreadPrivate::createEventDispatcher(this);
eventDispatcher.storeRelease(ed);
- ed->startingUp();
return ed;
}
@@ -142,8 +130,9 @@ QAdoptedThread::QAdoptedThread(QThreadData *data)
d_func()->running = true;
d_func()->finished = false;
init();
+ d_func()->m_statusOrPendingObjects.setStatusAndClearList(
+ QtPrivate::getBindingStatus({}));
#endif
-
// fprintf(stderr, "new QAdoptedThread = %p\n", this);
}
@@ -158,7 +147,24 @@ void QAdoptedThread::run()
// this function should never be called
qFatal("QAdoptedThread::run(): Internal error, this implementation should never be called.");
}
+#endif
+
+QScopedScopeLevelCounter::QScopedScopeLevelCounter(QThreadData *threadData)
+ : threadData(threadData)
+{
+ ++threadData->scopeLevel;
+ qCDebug(lcDeleteLater) << "Increased" << threadData->thread
+ << "scope level to" << threadData->scopeLevel;
+}
+
+QScopedScopeLevelCounter::~QScopedScopeLevelCounter()
+{
+ --threadData->scopeLevel;
+ qCDebug(lcDeleteLater) << "Decreased" << threadData->thread
+ << "scope level to" << threadData->scopeLevel;
+}
+#if QT_CONFIG(thread)
/*
QThreadPrivate
*/
@@ -176,7 +182,7 @@ QThreadPrivate::QThreadPrivate(QThreadData *d)
#ifdef Q_OS_INTEGRITY
stackSize = 128 * 1024;
#elif defined(Q_OS_RTEMS)
- static bool envStackSizeOk = false;
+ Q_CONSTINIT static bool envStackSizeOk = false;
static const int envStackSize = qEnvironmentVariableIntValue("QT_DEFAULT_THREAD_STACK_SIZE", &envStackSizeOk);
if (envStackSizeOk)
stackSize = envStackSize;
@@ -196,6 +202,10 @@ QThreadPrivate::QThreadPrivate(QThreadData *d)
QThreadPrivate::~QThreadPrivate()
{
+ // access to m_statusOrPendingObjects cannot race with anything
+ // unless there is already a potential use-after-free bug, as the
+ // thread is in the process of being destroyed
+ delete m_statusOrPendingObjects.list();
data->deref();
}
@@ -248,11 +258,13 @@ QThreadPrivate::~QThreadPrivate()
different threads. Check that it is safe to do so.
\note Care must be taken when interacting with objects across different
- threads. See \l{Synchronizing Threads} for details.
+ threads. As a general rule, functions can only be called from the thread
+ that created the QThread object itself (e.g. setPriority()), unless the
+ documentation says otherwise. See \l{Synchronizing Threads} for details.
\section1 Managing Threads
- QThread will notifiy you via a signal when the thread is
+ QThread will notify you via a signal when the thread is
started() and finished(), or you can use isFinished() and
isRunning() to query the state of the thread.
@@ -262,22 +274,21 @@ QThreadPrivate::~QThreadPrivate()
documentation for terminate() and setTerminationEnabled() for
detailed information.
- From Qt 4.8 onwards, it is possible to deallocate objects that
- live in a thread that has just ended, by connecting the
- finished() signal to QObject::deleteLater().
+ You often want to deallocate objects that live in a thread when
+ a thread ends. To do this, connect the finished() signal to
+ QObject::deleteLater().
Use wait() to block the calling thread, until the other thread
has finished execution (or until a specified time has passed).
QThread also provides static, platform independent sleep
functions: sleep(), msleep(), and usleep() allow full second,
- millisecond, and microsecond resolution respectively. These
- functions were made public in Qt 5.0.
+ millisecond, and microsecond resolution respectively.
\note wait() and the sleep() functions should be unnecessary in
general, since Qt is an event-driven framework. Instead of
wait(), consider listening for the finished() signal. Instead of
- the sleep() functions, consider using QTimer.
+ the sleep() functions, consider using QChronoTimer.
The static functions currentThreadId() and currentThread() return
identifiers for the currently executing thread. The former
@@ -290,11 +301,12 @@ QThreadPrivate::~QThreadPrivate()
If you don't call \l{QObject::setObjectName()}{setObjectName()},
the name given to your thread will be the class name of the runtime
type of your thread object (for example, \c "RenderThread" in the case of the
- \l{Mandelbrot Example}, as that is the name of the QThread subclass).
+ \l{Mandelbrot} example, as that is the name of the QThread subclass).
Note that this is currently not available with release builds on Windows.
\sa {Thread Support in Qt}, QThreadStorage, {Synchronizing Threads},
- {Mandelbrot Example}, {Semaphores Example}, {Wait Conditions Example}
+ Mandelbrot, {Producer and Consumer using Semaphores},
+ {Producer and Consumer using Wait Conditions}
*/
/*!
@@ -313,9 +325,20 @@ QThreadPrivate::~QThreadPrivate()
/*!
\fn int QThread::idealThreadCount()
- Returns the ideal number of threads that can be run on the system. This is done querying
- the number of processor cores, both real and logical, in the system. This function returns 1
- if the number of processor cores could not be detected.
+ Returns the ideal number of threads that this process can run in parallel.
+ This is done by querying the number of logical processors available to this
+ process (if supported by this OS) or the total number of logical processors
+ in the system. This function returns 1 if neither value could be
+ determined.
+
+ \note On operating systems that support setting a thread's affinity to a
+ subset of all logical processors, the value returned by this function may
+ change between threads and over time.
+
+ \note On operating systems that support CPU hotplugging and hot-unplugging,
+ the value returned by this function may also change over time (and note
+ that CPUs can be turned on and off by software, without a physical,
+ hardware change).
*/
/*!
@@ -404,6 +427,23 @@ QThread *QThread::currentThread()
}
/*!
+ \since 6.8
+
+ Returns whether the currently executing thread is the main thread.
+
+ The main thread is the thread in which QCoreApplication was created.
+ This is usually the thread that called the \c{main()} function, but not necessarily so.
+ It is the thread that is processing the GUI events and in which graphical objects
+ (QWindow, QWidget) can be created.
+
+ \sa currentThread(), QCoreApplication::instance()
+*/
+bool QThread::isMainThread()
+{
+ return currentThreadId() == QCoreApplicationPrivate::theMainThreadId.loadRelaxed();
+}
+
+/*!
Constructs a new QThread to manage a new thread. The \a parent
takes ownership of the QThread. The thread does not begin
executing until start() is called.
@@ -437,6 +477,15 @@ QThread::QThread(QThreadPrivate &dd, QObject *parent)
isFinished() returns \c false) will result in a program
crash. Wait for the finished() signal before deleting the
QThread.
+
+ Since Qt 6.3, it is allowed to delete a QThread instance created by
+ a call to QThread::create() even if the corresponding thread is
+ still running. In such a case, Qt will post an interruption request
+ to that thread (via requestInterruption()); will ask the thread's
+ event loop (if any) to quit (via quit()); and will block until the
+ thread has finished.
+
+ \sa create(), isInterruptionRequested(), exec(), quit()
*/
QThread::~QThread()
{
@@ -456,6 +505,7 @@ QThread::~QThread()
}
/*!
+ \threadsafe
Returns \c true if the thread is finished; otherwise returns \c false.
\sa isRunning()
@@ -468,6 +518,7 @@ bool QThread::isFinished() const
}
/*!
+ \threadsafe
Returns \c true if the thread is running; otherwise returns \c false.
\sa isFinished()
@@ -514,6 +565,24 @@ uint QThread::stackSize() const
}
/*!
+ \internal
+ Transitions BindingStatusOrList to the binding status state. If we had a list of
+ pending objects, all objects get their reinitBindingStorageAfterThreadMove method
+ called, and afterwards, the list gets discarded.
+ */
+void QtPrivate::BindingStatusOrList::setStatusAndClearList(QBindingStatus *status) noexcept
+{
+
+ if (auto pendingObjects = list()) {
+ for (auto obj: *pendingObjects)
+ QObjectPrivate::get(obj)->reinitBindingStorageAfterThreadMove();
+ delete pendingObjects;
+ }
+ // synchronizes-with the load-acquire in bindingStatus():
+ data.store(encodeBindingStatus(status), std::memory_order_release);
+}
+
+/*!
Enters the event loop and waits until exit() is called, returning the value
that was passed to exit(). The value returned is 0 if exit() is called via
quit().
@@ -521,12 +590,18 @@ uint QThread::stackSize() const
This function is meant to be called from within run(). It is necessary to
call this function to start event handling.
+ \note This can only be called within the thread itself, i.e. when
+ it is the current thread.
+
\sa quit(), exit()
*/
int QThread::exec()
{
Q_D(QThread);
+ const auto status = QtPrivate::getBindingStatus(QtPrivate::QBindingStatusAccessToken{});
+
QMutexLocker locker(&d->mutex);
+ d->m_statusOrPendingObjects.setStatusAndClearList(status);
d->data->quitNow = false;
if (d->exited) {
d->exited = false;
@@ -543,7 +618,60 @@ int QThread::exec()
return returnCode;
}
+
+/*!
+ \internal
+ If BindingStatusOrList is already in the binding status state, this will
+ return that BindingStatus pointer.
+ Otherwise, \a object is added to the list, and we return nullptr.
+ The list is allocated if it does not already exist.
+ */
+QBindingStatus *QtPrivate::BindingStatusOrList::addObjectUnlessAlreadyStatus(QObject *object)
+{
+ if (auto status = bindingStatus())
+ return status;
+ List *objectList = list();
+ if (!objectList) {
+ objectList = new List();
+ objectList->reserve(8);
+ data.store(encodeList(objectList), std::memory_order_relaxed);
+ }
+ objectList->push_back(object);
+ return nullptr;
+}
+
+/*!
+ \internal
+ If BindingStatusOrList is a list, remove \a object from it
+ */
+void QtPrivate::BindingStatusOrList::removeObject(QObject *object)
+{
+ List *objectList = list();
+ if (!objectList)
+ return;
+ auto it = std::remove(objectList->begin(), objectList->end(), object);
+ objectList->erase(it, objectList->end());
+}
+
+QBindingStatus *QThreadPrivate::addObjectWithPendingBindingStatusChange(QObject *obj)
+{
+ if (auto status = m_statusOrPendingObjects.bindingStatus())
+ return status;
+ QMutexLocker lock(&mutex);
+ return m_statusOrPendingObjects.addObjectUnlessAlreadyStatus(obj);
+}
+
+void QThreadPrivate::removeObjectWithPendingBindingStatusChange(QObject *obj)
+{
+ if (m_statusOrPendingObjects.bindingStatus())
+ return;
+ QMutexLocker lock(&mutex);
+ m_statusOrPendingObjects.removeObject(obj);
+}
+
+
/*!
+ \threadsafe
Tells the thread's event loop to exit with a return code.
After calling this function, the thread leaves the event loop and
@@ -578,6 +706,7 @@ void QThread::exit(int returnCode)
}
/*!
+ \threadsafe
Tells the thread's event loop to exit with return code 0 (success).
Equivalent to calling QThread::exit(0).
@@ -657,16 +786,28 @@ QThread::Priority QThread::priority() const
}
/*!
- \fn void QThread::sleep(unsigned long secs)
+ \fn void QThread::sleep(std::chrono::nanoseconds nsecs)
+ \since 6.6
- Forces the current thread to sleep for \a secs seconds.
+ Forces the current thread to sleep for \a nsecs.
Avoid using this function if you need to wait for a given condition to
change. Instead, connect a slot to the signal that indicates the change or
use an event handler (see \l QObject::event()).
\note This function does not guarantee accuracy. The application may sleep
- longer than \a secs under heavy load conditions.
+ longer than \a nsecs under heavy load conditions.
+*/
+
+/*!
+ \fn void QThread::sleep(unsigned long secs)
+
+ Forces the current thread to sleep for \a secs seconds.
+
+ This is an overloaded function, equivalent to calling:
+ \code
+ QThread::sleep(std::chrono::seconds{secs});
+ \endcode
\sa msleep(), usleep()
*/
@@ -674,11 +815,10 @@ QThread::Priority QThread::priority() const
/*!
\fn void QThread::msleep(unsigned long msecs)
- Forces the current thread to sleep for \a msecs milliseconds.
-
- Avoid using this function if you need to wait for a given condition to
- change. Instead, connect a slot to the signal that indicates the change or
- use an event handler (see \l QObject::event()).
+ This is an overloaded function, equivalent to calling:
+ \code
+ QThread::sleep(std::chrono::milliseconds{msecs});
+ \endcode
\note This function does not guarantee accuracy. The application may sleep
longer than \a msecs under heavy load conditions. Some OSes might round \a
@@ -690,11 +830,10 @@ QThread::Priority QThread::priority() const
/*!
\fn void QThread::usleep(unsigned long usecs)
- Forces the current thread to sleep for \a usecs microseconds.
-
- Avoid using this function if you need to wait for a given condition to
- change. Instead, connect a slot to the signal that indicates the change or
- use an event handler (see \l QObject::event()).
+ This is an overloaded function, equivalent to calling:
+ \code
+ QThread::sleep(std::chrono::microseconds{secs});
+ \endcode
\note This function does not guarantee accuracy. The application may sleep
longer than \a usecs under heavy load conditions. Some OSes might round \a
@@ -706,6 +845,7 @@ QThread::Priority QThread::priority() const
/*!
\fn void QThread::terminate()
+ \threadsafe
Terminates the execution of the thread. The thread may or may not
be terminated immediately, depending on the operating system's
@@ -791,6 +931,36 @@ int QThread::loopLevel() const
return d->data->eventLoops.size();
}
+/*!
+ \internal
+ Returns the thread handle of this thread.
+ It can be compared with the return value of currentThreadId().
+
+ This is used to implement isCurrentThread, and might be useful
+ for debugging (e.g. by comparing the value in gdb with info threads).
+
+ \note Thread handles of destroyed threads might be reused by the
+ operating system. Storing the return value of this function can
+ therefore give surprising results if it outlives the QThread object
+ (threads claimed to be the same even if they aren't).
+*/
+Qt::HANDLE QThreadPrivate::threadId() const
+{
+ return data->threadId.loadRelaxed();
+}
+
+/*!
+ \since 6.8
+ Returns true if this thread is QThread::currentThread.
+
+ \sa currentThreadId()
+*/
+bool QThread::isCurrentThread() const
+{
+ Q_D(const QThread);
+ return QThread::currentThreadId() == d->threadId();
+}
+
#else // QT_CONFIG(thread)
QThread::QThread(QObject *parent)
@@ -848,7 +1018,7 @@ bool QThread::wait(QDeadlineTimer deadline)
return false;
}
-bool QThread::event(QEvent* event)
+bool QThread::event(QEvent *event)
{
return QObject::event(event);
}
@@ -863,6 +1033,11 @@ QThread *QThread::currentThread()
return QThreadData::current()->thread.loadAcquire();
}
+bool QThread::isCurrentThread() const
+{
+ return true;
+}
+
int QThread::idealThreadCount() noexcept
{
return 1;
@@ -884,8 +1059,22 @@ bool QThread::isRunning() const
return d->running;
}
+void QThread::requestInterruption()
+{
+
+}
+
+bool QThread::isInterruptionRequested() const
+{
+ return false;
+}
+
+void QThread::setTerminationEnabled(bool)
+{
+}
+
// No threads: so we can just use static variables
-static QThreadData *data = 0;
+Q_CONSTINIT static QThreadData *data = nullptr;
QThreadData *QThreadData::current(bool createIfNecessary)
{
@@ -895,8 +1084,10 @@ QThreadData *QThreadData::current(bool createIfNecessary)
data->threadId.storeRelaxed(Qt::HANDLE(data->thread.loadAcquire()));
data->deref();
data->isAdopted = true;
- if (!QCoreApplicationPrivate::theMainThread.loadAcquire())
+ if (!QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
QCoreApplicationPrivate::theMainThread.storeRelease(data->thread.loadRelaxed());
+ QCoreApplicationPrivate::theMainThreadId.storeRelaxed(data->threadId.loadRelaxed());
+ }
}
return data;
}
@@ -957,8 +1148,11 @@ QAbstractEventDispatcher *QThread::eventDispatcher() const
Sets the event dispatcher for the thread to \a eventDispatcher. This is
only possible as long as there is no event dispatcher installed for the
- thread yet. That is, before the thread has been started with start() or, in
- case of the main thread, before QCoreApplication has been instantiated.
+ thread yet.
+
+ An event dispatcher is automatically created for the main thread when \l
+ QCoreApplication is instantiated and on start() for auxiliary threads.
+
This method takes ownership of the object.
*/
void QThread::setEventDispatcher(QAbstractEventDispatcher *eventDispatcher)
@@ -977,14 +1171,11 @@ void QThread::setEventDispatcher(QAbstractEventDispatcher *eventDispatcher)
/*!
\fn bool QThread::wait(unsigned long time)
+
\overload
+ \a time is the time to wait in milliseconds.
+ If \a time is ULONG_MAX, then the wait will never timeout.
*/
-bool QThread::wait(unsigned long time)
-{
- if (time == std::numeric_limits<unsigned long>::max())
- return wait(QDeadlineTimer(QDeadlineTimer::Forever));
- return wait(QDeadlineTimer(time));
-}
#if QT_CONFIG(thread)
@@ -1003,6 +1194,7 @@ bool QThread::event(QEvent *event)
/*!
\since 5.2
+ \threadsafe
Request the interruption of the thread.
That request is advisory and it is up to code running on the thread to decide
@@ -1015,13 +1207,11 @@ bool QThread::event(QEvent *event)
void QThread::requestInterruption()
{
- if (this == QCoreApplicationPrivate::theMainThread.loadAcquire()) {
+ Q_D(QThread);
+ if (d->threadId() == QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
qWarning("QThread::requestInterruption has no effect on the main thread");
return;
}
- Q_D(QThread);
- // ### Qt 6: use std::atomic_flag, and document that
- // requestInterruption/isInterruptionRequested do not synchronize with each other
QMutexLocker locker(&d->mutex);
if (!d->running || d->finished || d->isInFinish)
return;
@@ -1049,6 +1239,9 @@ void QThread::requestInterruption()
}
\endcode
+ \note This can only be called within the thread itself, i.e. when
+ it is the current thread.
+
\sa currentThread() requestInterruption()
*/
bool QThread::isInterruptionRequested() const
@@ -1084,7 +1277,6 @@ bool QThread::isInterruptionRequested() const
\sa start()
*/
-#if QT_CONFIG(cxx11_future)
class QThreadCreateThread : public QThread
{
public:
@@ -1093,6 +1285,13 @@ public:
{
}
+ ~QThreadCreateThread()
+ {
+ requestInterruption();
+ quit();
+ wait();
+ }
+
private:
void run() override
{
@@ -1106,7 +1305,6 @@ QThread *QThread::createThreadImpl(std::future<void> &&future)
{
return new QThreadCreateThread(std::move(future));
}
-#endif // QT_CONFIG(cxx11_future)
/*!
\class QDaemonThread
@@ -1121,7 +1319,9 @@ QDaemonThread::QDaemonThread(QObject *parent)
{
// QThread::started() is emitted from the thread we start
connect(this, &QThread::started,
- [](){ QThreadData::current()->requiresCoreApplication = false; });
+ this,
+ [](){ QThreadData::current()->requiresCoreApplication = false; },
+ Qt::DirectConnection);
}
QDaemonThread::~QDaemonThread()
diff --git a/src/corelib/thread/qthread.h b/src/corelib/thread/qthread.h
index caa91b100f..641c8ef68a 100644
--- a/src/corelib/thread/qthread.h
+++ b/src/corelib/thread/qthread.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
-** 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.
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTHREAD_H
#define QTHREAD_H
@@ -45,9 +9,11 @@
#include <QtCore/qdeadlinetimer.h>
// For QThread::create
-#if QT_CONFIG(cxx11_future)
-# include <future> // for std::async
-# include <functional> // for std::invoke; no guard needed as it's a C++98 header
+#include <future> // for std::async
+#include <functional> // for std::invoke; no guard needed as it's a C++98 header
+// internal compiler error with mingw 8.1
+#if defined(Q_CC_MSVC) && defined(Q_PROCESSOR_X86)
+#include <intrin.h>
#endif
QT_BEGIN_NAMESPACE
@@ -56,6 +22,7 @@ QT_BEGIN_NAMESPACE
class QThreadData;
class QThreadPrivate;
class QAbstractEventDispatcher;
+class QEventLoopLocker;
class Q_CORE_EXPORT QThread : public QObject
{
@@ -63,6 +30,7 @@ class Q_CORE_EXPORT QThread : public QObject
public:
static Qt::HANDLE currentThreadId() noexcept Q_DECL_PURE_FUNCTION;
static QThread *currentThread();
+ static bool isMainThread();
static int idealThreadCount() noexcept;
static void yieldCurrentThread();
@@ -95,32 +63,36 @@ public:
void setStackSize(uint stackSize);
uint stackSize() const;
- void exit(int retcode = 0);
-
QAbstractEventDispatcher *eventDispatcher() const;
void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher);
bool event(QEvent *event) override;
int loopLevel() const;
-#if QT_CONFIG(cxx11_future) || defined(Q_CLANG_QDOC)
+ bool isCurrentThread() const;
+
template <typename Function, typename... Args>
- static QThread *create(Function &&f, Args &&... args);
-#endif
+ [[nodiscard]] static QThread *create(Function &&f, Args &&... args);
public Q_SLOTS:
void start(Priority = InheritPriority);
void terminate();
+ void exit(int retcode = 0);
void quit();
public:
bool wait(QDeadlineTimer deadline = QDeadlineTimer(QDeadlineTimer::Forever));
- // ### Qt6 inline this function
- bool wait(unsigned long time);
+ bool wait(unsigned long time)
+ {
+ if (time == (std::numeric_limits<unsigned long>::max)())
+ return wait(QDeadlineTimer(QDeadlineTimer::Forever));
+ return wait(QDeadlineTimer(time));
+ }
static void sleep(unsigned long);
static void msleep(unsigned long);
static void usleep(unsigned long);
+ static void sleep(std::chrono::nanoseconds nsec);
Q_SIGNALS:
void started(QPrivateSignal);
@@ -137,17 +109,15 @@ protected:
private:
Q_DECLARE_PRIVATE(QThread)
+ friend class QEventLoopLocker;
-#if QT_CONFIG(cxx11_future)
- static QThread *createThreadImpl(std::future<void> &&future);
-#endif
+ [[nodiscard]] static QThread *createThreadImpl(std::future<void> &&future);
static Qt::HANDLE currentThreadIdImpl() noexcept Q_DECL_PURE_FUNCTION;
friend class QCoreApplication;
friend class QThreadData;
};
-#if QT_CONFIG(cxx11_future)
template <typename Function, typename... Args>
QThread *QThread::create(Function &&f, Args &&... args)
{
@@ -162,7 +132,6 @@ QThread *QThread::create(Function &&f, Args &&... args)
std::move(threadFunction),
std::forward<Args>(args)...));
}
-#endif // QT_CONFIG(cxx11_future)
/*
On architectures and platforms we know, interpret the thread control
@@ -174,23 +143,47 @@ QThread *QThread::create(Function &&f, Args &&... args)
value for anything. In Qt we use the handle to check if threads are identical,
for which the TCB is sufficient.
- So we use the fastest possible way, rathern than spend time on returning
+ So we use the fastest possible way, rather than spend time on returning
some pseudo-interoperable value.
*/
inline Qt::HANDLE QThread::currentThreadId() noexcept
{
+ // define is undefed if we have to fall back to currentThreadIdImpl
+#define QT_HAS_FAST_CURRENT_THREAD_ID
Qt::HANDLE tid; // typedef to void*
static_assert(sizeof(tid) == sizeof(void*));
// See https://akkadia.org/drepper/tls.pdf for x86 ABI
-#if defined(Q_PROCESSOR_X86_32) && defined(Q_OS_LINUX) // x86 32-bit always uses GS
- __asm__("movl %%gs:0, %0" : "=r" (tid) : : );
-#elif defined(Q_PROCESSOR_X86_64) && defined(Q_OS_DARWIN64)
+#if defined(Q_PROCESSOR_X86_32) && ((defined(Q_OS_LINUX) && defined(__GLIBC__)) || defined(Q_OS_FREEBSD)) // x86 32-bit always uses GS
+ __asm__("mov %%gs:%c1, %0" : "=r" (tid) : "i" (2 * sizeof(void*)) : );
+#elif defined(Q_PROCESSOR_X86_64) && defined(Q_OS_DARWIN)
// 64bit macOS uses GS, see https://github.com/apple/darwin-xnu/blob/master/libsyscall/os/tsd.h
- __asm__("movq %%gs:0, %0" : "=r" (tid) : : );
-#elif defined(Q_PROCESSOR_X86_64) && (defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD))
+ __asm__("mov %%gs:0, %0" : "=r" (tid) : : );
+#elif defined(Q_PROCESSOR_X86_64) && ((defined(Q_OS_LINUX) && defined(__GLIBC__)) || defined(Q_OS_FREEBSD))
// x86_64 Linux, BSD uses FS
- __asm__("movq %%fs:0, %0" : "=r" (tid) : : );
+ __asm__("mov %%fs:%c1, %0" : "=r" (tid) : "i" (2 * sizeof(void*)) : );
+#elif defined(Q_PROCESSOR_X86_64) && defined(Q_OS_WIN)
+ // See https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
+ // First get the pointer to the TIB
+ quint8 *tib;
+# if defined(Q_CC_MINGW) // internal compiler error when using the intrinsics
+ __asm__("movq %%gs:0x30, %0" : "=r" (tib) : :);
+# else
+ tib = reinterpret_cast<quint8 *>(__readgsqword(0x30));
+# endif
+ // Then read the thread ID
+ tid = *reinterpret_cast<Qt::HANDLE *>(tib + 0x48);
+#elif defined(Q_PROCESSOR_X86_32) && defined(Q_OS_WIN)
+ // First get the pointer to the TIB
+ quint8 *tib;
+# if defined(Q_CC_MINGW) // internal compiler error when using the intrinsics
+ __asm__("movl %%fs:0x18, %0" : "=r" (tib) : :);
+# else
+ tib = reinterpret_cast<quint8 *>(__readfsdword(0x18));
+# endif
+ // Then read the thread ID
+ tid = *reinterpret_cast<Qt::HANDLE *>(tib + 0x24);
#else
+#undef QT_HAS_FAST_CURRENT_THREAD_ID
tid = currentThreadIdImpl();
#endif
return tid;
diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h
index c853bd1de4..c39e21ec9a 100644
--- a/src/corelib/thread/qthread_p.h
+++ b/src/corelib/thread/qthread_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** 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.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTHREAD_P_H
#define QTHREAD_P_H
@@ -85,7 +49,7 @@ public:
: receiver(r), event(e), priority(p)
{ }
};
-Q_DECLARE_TYPEINFO(QPostEvent, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QPostEvent, Q_RELOCATABLE_TYPE);
inline bool operator<(const QPostEvent &first, const QPostEvent &second)
{
@@ -94,6 +58,7 @@ inline bool operator<(const QPostEvent &first, const QPostEvent &second)
// This class holds the list of posted events.
// The list has to be kept sorted by priority
+// It's used in a virtual in QCoreApplication, so ELFVERSION:ignore-next
class QPostEventList : public QList<QPostEvent>
{
public:
@@ -101,36 +66,101 @@ public:
int recursion;
// sendOffset == the current event to start sending
- int startOffset;
+ qsizetype startOffset;
// insertionOffset == set by sendPostedEvents to tell postEvent() where to start insertions
- int insertionOffset;
+ qsizetype insertionOffset;
QMutex mutex;
inline QPostEventList() : QList<QPostEvent>(), recursion(0), startOffset(0), insertionOffset(0) { }
- void addEvent(const QPostEvent &ev) {
- int priority = ev.priority;
- if (isEmpty() ||
- constLast().priority >= priority ||
- insertionOffset >= size()) {
- // optimization: we can simply append if the last event in
- // the queue has higher or equal priority
- append(ev);
- } else {
- // insert event in descending priority order, using upper
- // bound for a given priority (to ensure proper ordering
- // of events with the same priority)
- QPostEventList::iterator at = std::upper_bound(begin() + insertionOffset, end(), ev);
- insert(at, ev);
- }
- }
+ void addEvent(const QPostEvent &ev);
+
private:
//hides because they do not keep that list sorted. addEvent must be used
using QList<QPostEvent>::append;
using QList<QPostEvent>::insert;
};
+namespace QtPrivate {
+
+/* BindingStatusOrList is basically a QBiPointer (as found in declarative)
+ with some helper methods to manipulate the list. BindingStatusOrList starts
+ its life in a null state and supports the following transitions
+
+ 0 state (initial)
+ / \
+ / \
+ v v
+ pending object list----------->binding status
+ Note that binding status is the final state, and we never transition away
+ from it
+*/
+class BindingStatusOrList
+{
+ Q_DISABLE_COPY_MOVE(BindingStatusOrList)
+public:
+ using List = std::vector<QObject *>;
+
+ constexpr BindingStatusOrList() noexcept : data(0) {}
+ explicit BindingStatusOrList(QBindingStatus *status) noexcept :
+ data(encodeBindingStatus(status)) {}
+ explicit BindingStatusOrList(List *list) noexcept : data(encodeList(list)) {}
+
+ // requires external synchronization:
+ QBindingStatus *addObjectUnlessAlreadyStatus(QObject *object);
+ void removeObject(QObject *object);
+ void setStatusAndClearList(QBindingStatus *status) noexcept;
+
+
+ static bool isBindingStatus(quintptr data) noexcept
+ {
+ return !isNull(data) && !isList(data);
+ }
+ static bool isList(quintptr data) noexcept { return data & 1; }
+ static bool isNull(quintptr data) noexcept { return data == 0; }
+
+ // thread-safe:
+ QBindingStatus *bindingStatus() const noexcept
+ {
+ // synchronizes-with the store-release in setStatusAndClearList():
+ const auto d = data.load(std::memory_order_acquire);
+ if (isBindingStatus(d))
+ return reinterpret_cast<QBindingStatus *>(d);
+ else
+ return nullptr;
+ }
+
+ // requires external synchronization:
+ List *list() const noexcept
+ {
+ return decodeList(data.load(std::memory_order_relaxed));
+ }
+
+private:
+ static List *decodeList(quintptr ptr) noexcept
+ {
+ if (isList(ptr))
+ return reinterpret_cast<List *>(ptr & ~1);
+ else
+ return nullptr;
+ }
+
+ static quintptr encodeBindingStatus(QBindingStatus *status) noexcept
+ {
+ return quintptr(status);
+ }
+
+ static quintptr encodeList(List *list) noexcept
+ {
+ return quintptr(list) | 1;
+ }
+
+ std::atomic<quintptr> data;
+};
+
+} // namespace QtPrivate
+
#if QT_CONFIG(thread)
class Q_CORE_EXPORT QDaemonThread : public QThread
@@ -140,7 +170,7 @@ public:
~QDaemonThread();
};
-class QThreadPrivate : public QObjectPrivate
+class Q_AUTOTEST_EXPORT QThreadPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QThread)
@@ -149,6 +179,7 @@ public:
~QThreadPrivate();
void setPriority(QThread::Priority prio);
+ Qt::HANDLE threadId() const;
mutable QMutex mutex;
QAtomicInt quitLockRef;
@@ -162,9 +193,7 @@ public:
int returnCode;
uint stackSize;
- QThread::Priority priority;
-
- static QThread *threadForId(int id);
+ std::underlying_type_t<QThread::Priority> priority;
#ifdef Q_OS_UNIX
QWaitCondition thread_done;
@@ -176,7 +205,7 @@ public:
#ifdef Q_OS_WIN
static unsigned int __stdcall start(void *) noexcept;
- static void finish(void *, bool lockAnyway=true) noexcept;
+ static void finish(void *, bool lockAnyway = true) noexcept;
Qt::HANDLE handle;
unsigned int id;
@@ -201,6 +230,26 @@ public:
QCoreApplication::instance()->postEvent(q_ptr, new QEvent(QEvent::Quit));
}
}
+
+ QBindingStatus *bindingStatus()
+ {
+ return m_statusOrPendingObjects.bindingStatus();
+ }
+
+ /* Returns nullptr if the object has been added, or the binding status
+ if that one has been set in the meantime
+ */
+ QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *obj);
+ void removeObjectWithPendingBindingStatusChange(QObject *obj);
+
+ // manipulating m_statusOrPendingObjects requires mutex to be locked
+ QtPrivate::BindingStatusOrList m_statusOrPendingObjects = {};
+#ifndef Q_OS_INTEGRITY
+private:
+ // Used in QThread(Private)::start to avoid racy access to QObject::objectName,
+ // unset afterwards. On INTEGRITY we set the thread name before starting it.
+ QString objectName;
+#endif
};
#else // QT_CONFIG(thread)
@@ -208,15 +257,19 @@ public:
class QThreadPrivate : public QObjectPrivate
{
public:
- QThreadPrivate(QThreadData *d = 0);
+ QThreadPrivate(QThreadData *d = nullptr);
~QThreadPrivate();
mutable QMutex mutex;
QThreadData *data;
+ QBindingStatus* m_bindingStatus;
bool running = false;
- static void setCurrentThread(QThread*) {}
- static QThread *threadForId(int) { return QThread::currentThread(); }
+ QBindingStatus* bindingStatus() { return m_bindingStatus; }
+ QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *) { return nullptr; }
+ void removeObjectWithPendingBindingStatusChange(QObject *) {}
+
+ static void setCurrentThread(QThread *) { }
static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data);
void ref() {}
@@ -258,26 +311,6 @@ public:
return canWait;
}
- // This class provides per-thread (by way of being a QThreadData
- // member) storage for qFlagLocation()
- class FlaggedDebugSignatures
- {
- static const uint Count = 2;
-
- uint idx;
- const char* locations[Count];
-
- public:
- FlaggedDebugSignatures() : idx(0)
- { std::fill_n(locations, Count, static_cast<char*>(nullptr)); }
-
- void store(const char* method)
- { locations[idx++ % Count] = method; }
-
- bool contains(const char *method) const
- { return std::find(locations, locations + Count, method) != locations + Count; }
- };
-
private:
QAtomicInt _ref;
@@ -291,7 +324,6 @@ public:
QAtomicPointer<void> threadId;
QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;
QList<void *> tls;
- FlaggedDebugSignatures flaggedSignatures;
bool quitNow;
bool canWait;
@@ -303,11 +335,8 @@ class QScopedScopeLevelCounter
{
QThreadData *threadData;
public:
- inline QScopedScopeLevelCounter(QThreadData *threadData)
- : threadData(threadData)
- { ++threadData->scopeLevel; }
- inline ~QScopedScopeLevelCounter()
- { --threadData->scopeLevel; }
+ QScopedScopeLevelCounter(QThreadData *threadData);
+ ~QScopedScopeLevelCounter();
};
// thread wrapper for the main() thread
diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp
index 70c1eb4b62..23a79074d6 100644
--- a/src/corelib/thread/qthread_unix.cpp
+++ b/src/corelib/thread/qthread_unix.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** 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.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qthread.h"
@@ -44,16 +8,21 @@
#include <private/qcoreapplication_p.h>
#include <private/qcore_unix_p.h>
+#include <private/qtools_p.h>
#if defined(Q_OS_DARWIN)
# include <private/qeventdispatcher_cf_p.h>
+#elif defined(Q_OS_WASM)
+# include <private/qeventdispatcher_wasm_p.h>
#else
# if !defined(QT_NO_GLIB)
# include "../kernel/qeventdispatcher_glib_p.h"
# endif
#endif
-#include <private/qeventdispatcher_unix_p.h>
+#if !defined(Q_OS_WASM)
+# include <private/qeventdispatcher_unix_p.h>
+#endif
#include "qthreadstorage.h"
@@ -68,8 +37,10 @@
#include <sched.h>
#include <errno.h>
-#ifdef Q_OS_BSD4
-#include <sys/sysctl.h>
+#if defined(Q_OS_FREEBSD)
+# include <sys/cpuset.h>
+#elif defined(Q_OS_BSD4)
+# include <sys/sysctl.h>
#endif
#ifdef Q_OS_VXWORKS
# if (_WRS_VXWORKS_MAJOR > 6) || ((_WRS_VXWORKS_MAJOR == 6) && (_WRS_VXWORKS_MINOR >= 6))
@@ -102,6 +73,8 @@
QT_BEGIN_NAMESPACE
+using namespace QtMiscUtils;
+
#if QT_CONFIG(thread)
static_assert(sizeof(pthread_t) <= sizeof(Qt::HANDLE));
@@ -109,10 +82,10 @@ static_assert(sizeof(pthread_t) <= sizeof(Qt::HANDLE));
enum { ThreadPriorityResetFlag = 0x80000000 };
-static thread_local QThreadData *currentThreadData = nullptr;
+Q_CONSTINIT static thread_local QThreadData *currentThreadData = nullptr;
-static pthread_once_t current_thread_data_once = PTHREAD_ONCE_INIT;
-static pthread_key_t current_thread_data_key;
+Q_CONSTINIT static pthread_once_t current_thread_data_once = PTHREAD_ONCE_INIT;
+Q_CONSTINIT static pthread_key_t current_thread_data_key;
static void destroy_current_thread_data(void *p)
{
@@ -169,30 +142,29 @@ static void set_thread_data(QThreadData *data)
static void clear_thread_data()
{
- currentThreadData = nullptr;
- pthread_setspecific(current_thread_data_key, nullptr);
+ set_thread_data(nullptr);
}
template <typename T>
-static typename std::enable_if<QTypeInfo<T>::isIntegral, Qt::HANDLE>::type to_HANDLE(T id)
+static typename std::enable_if<std::is_integral_v<T>, Qt::HANDLE>::type to_HANDLE(T id)
{
return reinterpret_cast<Qt::HANDLE>(static_cast<intptr_t>(id));
}
template <typename T>
-static typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type from_HANDLE(Qt::HANDLE id)
+static typename std::enable_if<std::is_integral_v<T>, T>::type from_HANDLE(Qt::HANDLE id)
{
return static_cast<T>(reinterpret_cast<intptr_t>(id));
}
template <typename T>
-static typename std::enable_if<QTypeInfo<T>::isPointer, Qt::HANDLE>::type to_HANDLE(T id)
+static typename std::enable_if<std::is_pointer_v<T>, Qt::HANDLE>::type to_HANDLE(T id)
{
return id;
}
template <typename T>
-static typename std::enable_if<QTypeInfo<T>::isPointer, T>::type from_HANDLE(Qt::HANDLE id)
+static typename std::enable_if<std::is_pointer_v<T>, T>::type from_HANDLE(Qt::HANDLE id)
{
return static_cast<T>(id);
}
@@ -219,8 +191,10 @@ QThreadData *QThreadData::current(bool createIfNecessary)
data->deref();
data->isAdopted = true;
data->threadId.storeRelaxed(to_HANDLE(pthread_self()));
- if (!QCoreApplicationPrivate::theMainThread.loadAcquire())
+ if (!QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
QCoreApplicationPrivate::theMainThread.storeRelease(data->thread.loadRelaxed());
+ QCoreApplicationPrivate::theMainThreadId.storeRelaxed(data->threadId.loadRelaxed());
+ }
}
return data;
}
@@ -235,7 +209,7 @@ void QAdoptedThread::init()
*/
extern "C" {
-typedef void*(*QtThreadCallback)(void*);
+typedef void *(*QtThreadCallback)(void *);
}
#endif // QT_CONFIG(thread)
@@ -250,6 +224,8 @@ QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *dat
return new QEventDispatcherCoreFoundation;
else
return new QEventDispatcherUNIX;
+#elif defined(Q_OS_WASM)
+ return new QEventDispatcherWasm();
#elif !defined(QT_NO_GLIB)
const bool isQtMainThread = data->thread.loadAcquire() == QCoreApplicationPrivate::mainThread();
if (qEnvironmentVariableIsEmpty("QT_NO_GLIB")
@@ -265,12 +241,12 @@ QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *dat
#if QT_CONFIG(thread)
-#if (defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_QNX))
+#if (defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_QNX))
static void setCurrentThreadName(const char *name)
{
# if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0);
-# elif defined(Q_OS_MAC)
+# elif defined(Q_OS_DARWIN)
pthread_setname_np(name);
# elif defined(Q_OS_QNX)
pthread_setname_np(pthread_self(), name);
@@ -278,17 +254,37 @@ static void setCurrentThreadName(const char *name)
}
#endif
+namespace {
+template <typename T>
+void terminate_on_exception(T &&t)
+{
+#ifndef QT_NO_EXCEPTIONS
+ try {
+#endif
+ std::forward<T>(t)();
+#ifndef QT_NO_EXCEPTIONS
+#ifdef __GLIBCXX__
+ // POSIX thread cancellation under glibc is implemented by throwing an exception
+ // of this type. Do what libstdc++ is doing and handle it specially in order not to
+ // abort the application if user's code calls a cancellation function.
+ } catch (abi::__forced_unwind &) {
+ throw;
+#endif // __GLIBCXX__
+ } catch (...) {
+ qTerminate();
+ }
+#endif // QT_NO_EXCEPTIONS
+}
+} // unnamed namespace
+
void *QThreadPrivate::start(void *arg)
{
-#if !defined(Q_OS_ANDROID)
+#ifdef PTHREAD_CANCEL_DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr);
#endif
pthread_cleanup_push(QThreadPrivate::finish, arg);
-#ifndef QT_NO_EXCEPTIONS
- try
-#endif
- {
+ terminate_on_exception([&] {
QThread *thr = reinterpret_cast<QThread *>(arg);
QThreadData *data = QThreadData::get2(thr);
@@ -296,11 +292,13 @@ void *QThreadPrivate::start(void *arg)
QMutexLocker locker(&thr->d_func()->mutex);
// do we need to reset the thread priority?
- if (int(thr->d_func()->priority) & ThreadPriorityResetFlag) {
+ if (thr->d_func()->priority & ThreadPriorityResetFlag) {
thr->d_func()->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag));
}
- data->threadId.storeRelaxed(to_HANDLE(pthread_self()));
+ // threadId is set in QThread::start()
+ Q_ASSERT(pthread_equal(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()),
+ pthread_self()));
set_thread_data(data);
data->ref();
@@ -308,39 +306,27 @@ void *QThreadPrivate::start(void *arg)
}
data->ensureEventDispatcher();
+ data->eventDispatcher.loadRelaxed()->startingUp();
-#if (defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_QNX))
+#if (defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_QNX))
{
// Sets the name of the current thread. We can only do this
// when the thread is starting, as we don't have a cross
// platform way of setting the name of an arbitrary thread.
- if (Q_LIKELY(thr->objectName().isEmpty()))
+ if (Q_LIKELY(thr->d_func()->objectName.isEmpty()))
setCurrentThreadName(thr->metaObject()->className());
else
- setCurrentThreadName(thr->objectName().toLocal8Bit());
+ setCurrentThreadName(std::exchange(thr->d_func()->objectName, {}).toLocal8Bit());
}
#endif
emit thr->started(QThread::QPrivateSignal());
-#if !defined(Q_OS_ANDROID)
+#ifdef PTHREAD_CANCEL_DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr);
pthread_testcancel();
#endif
thr->run();
- }
-#ifndef QT_NO_EXCEPTIONS
-#ifdef __GLIBCXX__
- // POSIX thread cancellation under glibc is implemented by throwing an exception
- // of this type. Do what libstdc++ is doing and handle it specially in order not to
- // abort the application if user's code calls a cancellation function.
- catch (const abi::__forced_unwind &) {
- throw;
- }
-#endif // __GLIBCXX__
- catch (...) {
- qTerminate();
- }
-#endif // QT_NO_EXCEPTIONS
+ });
// This pop runs finish() below. It's outside the try/catch (and has its
// own try/catch) to prevent finish() to be run in case an exception is
@@ -352,10 +338,7 @@ void *QThreadPrivate::start(void *arg)
void QThreadPrivate::finish(void *arg)
{
-#ifndef QT_NO_EXCEPTIONS
- try
-#endif
- {
+ terminate_on_exception([&] {
QThread *thr = reinterpret_cast<QThread *>(arg);
QThreadPrivate *d = thr->d_func();
@@ -366,6 +349,7 @@ void QThreadPrivate::finish(void *arg)
void *data = &d->data->tls;
locker.unlock();
emit thr->finished(QThread::QPrivateSignal());
+ qCDebug(lcDeleteLater) << "Sending deferred delete events as part of finishing thread" << thr;
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
QThreadStorageData::finish((void **)data);
locker.relock();
@@ -384,21 +368,10 @@ void QThreadPrivate::finish(void *arg)
d->interruptionRequested = false;
d->isInFinish = false;
+ d->data->threadId.storeRelaxed(nullptr);
+
d->thread_done.wakeAll();
- }
-#ifndef QT_NO_EXCEPTIONS
-#ifdef __GLIBCXX__
- // POSIX thread cancellation under glibc is implemented by throwing an exception
- // of this type. Do what libstdc++ is doing and handle it specially in order not to
- // abort the application if user's code calls a cancellation function.
- catch (const abi::__forced_unwind &) {
- throw;
- }
-#endif // __GLIBCXX__
- catch (...) {
- qTerminate();
- }
-#endif // QT_NO_EXCEPTIONS
+ });
}
@@ -446,8 +419,31 @@ int QThread::idealThreadCount() noexcept
} else {
cores = (int)psd.psd_proc_cnt;
}
+#elif (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD)
+# if defined(Q_OS_FREEBSD) && !defined(CPU_COUNT_S)
+# define CPU_COUNT_S(setsize, cpusetp) ((int)BIT_COUNT(setsize, cpusetp))
+ // match the Linux API for simplicity
+ using cpu_set_t = cpuset_t;
+ auto sched_getaffinity = [](pid_t, size_t cpusetsize, cpu_set_t *mask) {
+ return cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, cpusetsize, mask);
+ };
+# endif
+
+ // get the number of threads we're assigned, not the total in the system
+ QVarLengthArray<cpu_set_t, 1> cpuset(1);
+ int size = 1;
+ if (Q_UNLIKELY(sched_getaffinity(0, sizeof(cpu_set_t), cpuset.data()) < 0)) {
+ for (size = 2; size <= 4; size *= 2) {
+ cpuset.resize(size);
+ if (sched_getaffinity(0, sizeof(cpu_set_t) * size, cpuset.data()) == 0)
+ break;
+ }
+ if (size > 4)
+ return 1;
+ }
+ cores = CPU_COUNT_S(sizeof(cpu_set_t) * size, cpuset.data());
#elif defined(Q_OS_BSD4)
- // FreeBSD, OpenBSD, NetBSD, BSD/OS, OS X, iOS
+ // OpenBSD, NetBSD, BSD/OS, Darwin (macOS, iOS, etc.)
size_t len = sizeof(cores);
int mib[2];
mib[0] = CTL_HW;
@@ -485,7 +481,7 @@ int QThread::idealThreadCount() noexcept
#elif defined(Q_OS_WASM)
cores = QThreadPrivate::idealThreadCount;
#else
- // the rest: Linux, Solaris, AIX, Tru64
+ // the rest: Solaris, AIX, Tru64
cores = (int)sysconf(_SC_NPROCESSORS_ONLN);
if (cores == -1)
return 1;
@@ -500,27 +496,38 @@ void QThread::yieldCurrentThread()
#endif // QT_CONFIG(thread)
-static timespec makeTimespec(time_t secs, long nsecs)
+static void qt_nanosleep(timespec amount)
{
- struct timespec ts;
- ts.tv_sec = secs;
- ts.tv_nsec = nsecs;
- return ts;
+ // We'd like to use clock_nanosleep.
+ //
+ // But clock_nanosleep is from POSIX.1-2001 and both are *not*
+ // affected by clock changes when using relative sleeps, even for
+ // CLOCK_REALTIME.
+ //
+ // nanosleep is POSIX.1-1993
+
+ int r;
+ QT_EINTR_LOOP(r, nanosleep(&amount, &amount));
}
void QThread::sleep(unsigned long secs)
{
- qt_nanosleep(makeTimespec(secs, 0));
+ sleep(std::chrono::seconds{secs});
}
void QThread::msleep(unsigned long msecs)
{
- qt_nanosleep(makeTimespec(msecs / 1000, msecs % 1000 * 1000 * 1000));
+ sleep(std::chrono::milliseconds{msecs});
}
void QThread::usleep(unsigned long usecs)
{
- qt_nanosleep(makeTimespec(usecs / 1000 / 1000, usecs % (1000*1000) * 1000));
+ sleep(std::chrono::microseconds{usecs});
+}
+
+void QThread::sleep(std::chrono::nanoseconds nsec)
+{
+ qt_nanosleep(durationToTimespec(nsec));
}
#if QT_CONFIG(thread)
@@ -678,7 +685,7 @@ void QThread::start(Priority priority)
// could not set scheduling hints, fallback to inheriting them
// we'll try again from inside the thread
pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
- d->priority = Priority(priority | ThreadPriorityResetFlag);
+ d->priority = qToUnderlying(priority) | ThreadPriorityResetFlag;
}
break;
}
@@ -709,7 +716,12 @@ void QThread::start(Priority priority)
pthread_attr_setthreadname(&attr, metaObject()->className());
else
pthread_attr_setthreadname(&attr, objectName().toLocal8Bit());
+#else
+ // avoid interacting with the binding system
+ d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
+ : QString();
#endif
+
pthread_t threadId;
int code = pthread_create(&threadId, &attr, QThreadPrivate::start, this);
if (code == EPERM) {
@@ -766,6 +778,8 @@ bool QThread::wait(QDeadlineTimer deadline)
if (!d->thread_done.wait(locker.mutex(), deadline))
return false;
}
+ Q_ASSERT(d->data->threadId.loadRelaxed() == nullptr);
+
return true;
}
diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp
index b59a9d1bb4..3e0eef411a 100644
--- a/src/corelib/thread/qthread_win.cpp
+++ b/src/corelib/thread/qthread_win.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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
#include "qthread.h"
#include "qthread_p.h"
@@ -55,6 +19,17 @@
#endif // _MT
#include <process.h>
+extern "C" {
+// MinGW is missing the declaration of SetThreadDescription:
+WINBASEAPI
+HRESULT
+WINAPI
+SetThreadDescription(
+ _In_ HANDLE hThread,
+ _In_ PCWSTR lpThreadDescription
+ );
+}
+
QT_BEGIN_NAMESPACE
#if QT_CONFIG(thread)
@@ -67,7 +42,7 @@ void qt_create_tls()
{
if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES)
return;
- static QBasicMutex mutex;
+ Q_CONSTINIT static QBasicMutex mutex;
QMutexLocker locker(&mutex);
if (qt_current_thread_data_tls_index != TLS_OUT_OF_INDEXES)
return;
@@ -112,8 +87,9 @@ QThreadData *QThreadData::current(bool createIfNecessary)
threadData->isAdopted = true;
threadData->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
- if (!QCoreApplicationPrivate::theMainThread) {
- QCoreApplicationPrivate::theMainThread = threadData->thread.loadRelaxed();
+ if (!QCoreApplicationPrivate::theMainThreadId) {
+ QCoreApplicationPrivate::theMainThread.storeRelease(threadData->thread.loadRelaxed());
+ QCoreApplicationPrivate::theMainThreadId.storeRelaxed(threadData->threadId.loadRelaxed());
} else {
HANDLE realHandle = INVALID_HANDLE_VALUE;
DuplicateHandle(GetCurrentProcess(),
@@ -137,7 +113,7 @@ void QAdoptedThread::init()
static QList<HANDLE> qt_adopted_thread_handles;
static QList<QThread *> qt_adopted_qthreads;
-static QBasicMutex qt_adopted_thread_watcher_mutex;
+Q_CONSTINIT static QBasicMutex qt_adopted_thread_watcher_mutex;
static DWORD qt_adopted_thread_watcher_id = 0;
static HANDLE qt_adopted_thread_wakeup = 0;
@@ -248,39 +224,6 @@ DWORD WINAPI qt_adopted_thread_watcher_function(LPVOID)
return 0;
}
-#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC)
-
-#ifndef Q_OS_WIN64
-# define ULONG_PTR DWORD
-#endif
-
-typedef struct tagTHREADNAME_INFO
-{
- DWORD dwType; // must be 0x1000
- LPCSTR szName; // pointer to name (in user addr space)
- HANDLE dwThreadID; // thread ID (-1=caller thread)
- DWORD dwFlags; // reserved for future use, must be zero
-} THREADNAME_INFO;
-
-void qt_set_thread_name(HANDLE threadId, LPCSTR threadName)
-{
- THREADNAME_INFO info;
- info.dwType = 0x1000;
- info.szName = threadName;
- info.dwThreadID = threadId;
- info.dwFlags = 0;
-
- __try
- {
- RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD),
- reinterpret_cast<const ULONG_PTR*>(&info));
- }
- __except (EXCEPTION_CONTINUE_EXECUTION)
- {
- }
-}
-#endif // !QT_NO_DEBUG && Q_CC_MSVC
-
/**************************************************************************
** QThreadPrivate
*************************************************************************/
@@ -312,14 +255,13 @@ unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(voi
}
data->ensureEventDispatcher();
+ data->eventDispatcher.loadRelaxed()->startingUp();
-#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC)
// sets the name of the current thread.
- QByteArray objectName = thr->objectName().toLocal8Bit();
- qt_set_thread_name(HANDLE(-1),
- objectName.isEmpty() ?
- thr->metaObject()->className() : objectName.constData());
-#endif
+ QString threadName = std::exchange(thr->d_func()->objectName, {});
+ if (Q_LIKELY(threadName.isEmpty()))
+ threadName = QString::fromUtf8(thr->metaObject()->className());
+ SetThreadDescription(GetCurrentThread(), reinterpret_cast<const wchar_t *>(threadName.utf16()));
emit thr->started(QThread::QPrivateSignal());
QThread::setTerminationEnabled(true);
@@ -329,28 +271,44 @@ unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(voi
return 0;
}
+/*
+ For regularly terminating threads, this will be called and executed by the thread as the
+ last code before the thread exits. In that case, \a arg is the current QThread.
+
+ However, this function will also be called by QThread::terminate (as well as wait() and
+ setTerminationEnabled) to give Qt a chance to update the terminated thread's state and
+ process pending DeleteLater events for objects that live in the terminated thread. And for
+ adopted thread, this method is called by the thread watcher.
+
+ In those cases, \a arg will not be the current thread.
+*/
void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept
{
QThread *thr = reinterpret_cast<QThread *>(arg);
QThreadPrivate *d = thr->d_func();
- QMutexLocker locker(lockAnyway ? &d->mutex : 0);
+ QMutexLocker locker(lockAnyway ? &d->mutex : nullptr);
d->isInFinish = true;
d->priority = QThread::InheritPriority;
void **tls_data = reinterpret_cast<void **>(&d->data->tls);
- locker.unlock();
+ if (lockAnyway)
+ locker.unlock();
emit thr->finished(QThread::QPrivateSignal());
- QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ qCDebug(lcDeleteLater) << "Sending deferred delete events as part of finishing thread" << thr;
+ QCoreApplicationPrivate::sendPostedEvents(nullptr, QEvent::DeferredDelete, d->data);
QThreadStorageData::finish(tls_data);
- locker.relock();
+ if (lockAnyway)
+ locker.relock();
QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
if (eventDispatcher) {
d->data->eventDispatcher = 0;
- locker.unlock();
+ if (lockAnyway)
+ locker.unlock();
eventDispatcher->closingDown();
delete eventDispatcher;
- locker.relock();
+ if (lockAnyway)
+ locker.relock();
}
d->running = false;
@@ -389,6 +347,12 @@ void QThread::yieldCurrentThread()
#endif // QT_CONFIG(thread)
+void QThread::sleep(std::chrono::nanoseconds nsecs)
+{
+ using namespace std::chrono;
+ ::Sleep(DWORD(duration_cast<milliseconds>(nsecs).count()));
+}
+
void QThread::sleep(unsigned long secs)
{
::Sleep(secs * 1000);
@@ -420,6 +384,9 @@ void QThread::start(Priority priority)
if (d->running)
return;
+ // avoid interacting with the binding system
+ d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
+ : QString();
d->running = true;
d->finished = false;
d->exited = false;
@@ -456,7 +423,7 @@ void QThread::start(Priority priority)
int prio;
d->priority = priority;
- switch (d->priority) {
+ switch (priority) {
case IdlePriority:
prio = THREAD_PRIORITY_IDLE;
break;
@@ -583,7 +550,7 @@ void QThreadPrivate::setPriority(QThread::Priority threadPriority)
int prio;
priority = threadPriority;
- switch (priority) {
+ switch (threadPriority) {
case QThread::IdlePriority:
prio = THREAD_PRIORITY_IDLE;
break;
diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp
index 1478cfcb19..ae584656fe 100644
--- a/src/corelib/thread/qthreadpool.cpp
+++ b/src/corelib/thread/qthreadpool.cpp
@@ -1,51 +1,20 @@
-/****************************************************************************
-**
-** 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
#include "qthreadpool.h"
#include "qthreadpool_p.h"
#include "qdeadlinetimer.h"
#include "qcoreapplication.h"
+#include <QtCore/qpointer.h>
+
#include <algorithm>
+#include <memory>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*
QThread wrapper, provides synchronization against a ThreadPool
*/
@@ -88,9 +57,8 @@ void QThreadPoolThread::run()
do {
if (r) {
+ // If autoDelete() is false, r might already be deleted after run(), so check status now.
const bool del = r->autoDelete();
- Q_ASSERT(!del || r->ref == 1);
-
// run the task
locker.unlock();
@@ -113,16 +81,15 @@ void QThreadPoolThread::run()
locker.relock();
}
- // if too many threads are active, expire this thread
+ // if too many threads are active, stop working in this one
if (manager->tooManyThreadsActive())
break;
- if (manager->queue.isEmpty()) {
- r = nullptr;
+ // all work is done, time to wait for more
+ if (manager->queue.isEmpty())
break;
- }
- QueuePage *page = manager->queue.first();
+ QueuePage *page = manager->queue.constFirst();
r = page->pop();
if (page->isFinished()) {
@@ -131,26 +98,32 @@ void QThreadPoolThread::run()
}
} while (true);
- // if too many threads are active, expire this thread
- bool expired = manager->tooManyThreadsActive();
- if (!expired) {
- manager->waitingThreads.enqueue(this);
+ // this thread is about to be deleted, do not wait or expire
+ if (!manager->allThreads.contains(this)) {
registerThreadInactive();
- // wait for work, exiting after the expiry timeout is reached
- runnableReady.wait(locker.mutex(), QDeadlineTimer(manager->expiryTimeout));
- ++manager->activeThreads;
- if (manager->waitingThreads.removeOne(this))
- expired = true;
- if (!manager->allThreads.contains(this)) {
- registerThreadInactive();
- break;
- }
+ return;
}
- if (expired) {
+
+ // if too many threads are active, expire this thread
+ if (manager->tooManyThreadsActive()) {
manager->expiredThreads.enqueue(this);
registerThreadInactive();
- break;
+ return;
+ }
+ manager->waitingThreads.enqueue(this);
+ registerThreadInactive();
+ // wait for work, exiting after the expiry timeout is reached
+ runnableReady.wait(locker.mutex(), QDeadlineTimer(manager->expiryTimeout));
+ // this thread is about to be deleted, do not work or expire
+ if (!manager->allThreads.contains(this)) {
+ Q_ASSERT(manager->queue.isEmpty());
+ return;
+ }
+ if (manager->waitingThreads.removeOne(this)) {
+ manager->expiredThreads.enqueue(this);
+ return;
}
+ ++manager->activeThreads;
}
}
@@ -177,10 +150,10 @@ bool QThreadPoolPrivate::tryStart(QRunnable *task)
}
// can't do anything if we're over the limit
- if (activeThreadCount() >= maxThreadCount)
+ if (areAllThreadsActive())
return false;
- if (waitingThreads.count() > 0) {
+ if (!waitingThreads.isEmpty()) {
// recycle an available thread
enqueueTask(task);
waitingThreads.takeFirst()->runnableReady.wakeOne();
@@ -195,7 +168,12 @@ bool QThreadPoolPrivate::tryStart(QRunnable *task)
++activeThreads;
thread->runnable = task;
- thread->start();
+
+ // Ensure that the thread has actually finished, otherwise the following
+ // start() has no effect.
+ thread->wait();
+ Q_ASSERT(thread->isFinished());
+ thread->start(threadPriority);
return true;
}
@@ -212,7 +190,7 @@ inline bool comparePriority(int priority, const QueuePage *p)
void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
{
Q_ASSERT(runnable != nullptr);
- for (QueuePage *page : qAsConst(queue)) {
+ for (QueuePage *page : std::as_const(queue)) {
if (page->priority() == priority && !page->isFull()) {
page->push(runnable);
return;
@@ -224,9 +202,9 @@ void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
int QThreadPoolPrivate::activeThreadCount() const
{
- return (allThreads.count()
- - expiredThreads.count()
- - waitingThreads.count()
+ return (allThreads.size()
+ - expiredThreads.size()
+ - waitingThreads.size()
+ reservedThreads);
}
@@ -234,7 +212,7 @@ void QThreadPoolPrivate::tryToStartMoreThreads()
{
// try to push tasks on the queue to any available threads
while (!queue.isEmpty()) {
- QueuePage *page = queue.first();
+ QueuePage *page = queue.constFirst();
if (!tryStart(page->first()))
break;
@@ -247,10 +225,16 @@ void QThreadPoolPrivate::tryToStartMoreThreads()
}
}
+bool QThreadPoolPrivate::areAllThreadsActive() const
+{
+ const int activeThreadCount = this->activeThreadCount();
+ return activeThreadCount >= maxThreadCount() && (activeThreadCount - reservedThreads) >= 1;
+}
+
bool QThreadPoolPrivate::tooManyThreadsActive() const
{
const int activeThreadCount = this->activeThreadCount();
- return activeThreadCount > maxThreadCount && (activeThreadCount - reservedThreads) > 1;
+ return activeThreadCount > maxThreadCount() && (activeThreadCount - reservedThreads) > 1;
}
/*!
@@ -259,32 +243,36 @@ bool QThreadPoolPrivate::tooManyThreadsActive() const
void QThreadPoolPrivate::startThread(QRunnable *runnable)
{
Q_ASSERT(runnable != nullptr);
- QScopedPointer <QThreadPoolThread> thread(new QThreadPoolThread(this));
- thread->setObjectName(QLatin1String("Thread (pooled)"));
- Q_ASSERT(!allThreads.contains(thread.data())); // if this assert hits, we have an ABA problem (deleted threads don't get removed here)
- allThreads.insert(thread.data());
+ auto thread = std::make_unique<QThreadPoolThread>(this);
+ if (objectName.isEmpty())
+ objectName = u"Thread (pooled)"_s;
+ thread->setObjectName(objectName);
+ Q_ASSERT(!allThreads.contains(thread.get())); // if this assert hits, we have an ABA problem (deleted threads don't get removed here)
+ allThreads.insert(thread.get());
++activeThreads;
thread->runnable = runnable;
- thread.take()->start();
+ thread.release()->start(threadPriority);
}
/*!
\internal
Helper function only to be called from waitForDone(int)
+
+ Deletes all current threads.
*/
void QThreadPoolPrivate::reset()
{
// move the contents of the set out so that we can iterate without the lock
- QSet<QThreadPoolThread *> allThreadsCopy;
- allThreadsCopy.swap(allThreads);
+ auto allThreadsCopy = std::exchange(allThreads, {});
expiredThreads.clear();
waitingThreads.clear();
+
mutex.unlock();
- for (QThreadPoolThread *thread: qAsConst(allThreadsCopy)) {
- if (!thread->isFinished()) {
+ for (QThreadPoolThread *thread : std::as_const(allThreadsCopy)) {
+ if (thread->isRunning()) {
thread->runnableReady.wakeAll();
thread->wait();
}
@@ -311,33 +299,30 @@ bool QThreadPoolPrivate::waitForDone(int msecs)
{
QMutexLocker locker(&mutex);
QDeadlineTimer timer(msecs);
- do {
- if (!waitForDone(timer))
- return false;
- reset();
- // More threads can be started during reset(), in that case continue
- // waiting if we still have time left.
- } while ((!queue.isEmpty() || activeThreads) && !timer.hasExpired());
-
- return queue.isEmpty() && activeThreads == 0;
+ if (!waitForDone(timer))
+ return false;
+ reset();
+ // New jobs might have started during reset, but return anyway
+ // as the active thread and task count did reach 0 once, and
+ // race conditions are outside our scope.
+ return true;
}
void QThreadPoolPrivate::clear()
{
QMutexLocker locker(&mutex);
- for (QueuePage *page : qAsConst(queue)) {
+ while (!queue.isEmpty()) {
+ auto *page = queue.takeLast();
while (!page->isFinished()) {
QRunnable *r = page->pop();
if (r && r->autoDelete()) {
- Q_ASSERT(r->ref == 1);
locker.unlock();
delete r;
locker.relock();
}
}
+ delete page;
}
- qDeleteAll(queue);
- queue.clear();
}
/*!
@@ -365,16 +350,12 @@ bool QThreadPool::tryTake(QRunnable *runnable)
return false;
QMutexLocker locker(&d->mutex);
- for (QueuePage *page : qAsConst(d->queue)) {
+ for (QueuePage *page : std::as_const(d->queue)) {
if (page->tryTake(runnable)) {
if (page->isFinished()) {
d->queue.removeOne(page);
delete page;
}
- if (runnable->autoDelete()) {
- Q_ASSERT(runnable->ref == 1);
- --runnable->ref; // undo ++ref in start()
- }
return true;
}
}
@@ -393,14 +374,13 @@ void QThreadPoolPrivate::stealAndRunRunnable(QRunnable *runnable)
Q_Q(QThreadPool);
if (!q->tryTake(runnable))
return;
+ // If autoDelete() is false, runnable might already be deleted after run(), so check status now.
const bool del = runnable->autoDelete();
runnable->run();
- if (del) {
- Q_ASSERT(runnable->ref == 0); // tryTake already deref'ed
+ if (del)
delete runnable;
- }
}
/*!
@@ -412,7 +392,7 @@ void QThreadPoolPrivate::stealAndRunRunnable(QRunnable *runnable)
\ingroup thread
- QThreadPool manages and recyles individual QThread objects to help reduce
+ QThreadPool manages and recycles individual QThread objects to help reduce
thread creation costs in programs that use threads. Each Qt application
has one global QThreadPool object, which can be accessed by calling
globalInstance().
@@ -461,7 +441,14 @@ void QThreadPoolPrivate::stealAndRunRunnable(QRunnable *runnable)
*/
QThreadPool::QThreadPool(QObject *parent)
: QObject(*new QThreadPoolPrivate, parent)
-{ }
+{
+ Q_D(QThreadPool);
+ connect(this, &QObject::objectNameChanged, this, [d](const QString &newName) {
+ // We keep a copy of the name under our own lock, so we can access it thread-safely.
+ QMutexLocker locker(&d->mutex);
+ d->objectName = newName;
+ });
+}
/*!
Destroys the QThreadPool.
@@ -469,7 +456,10 @@ QThreadPool::QThreadPool(QObject *parent)
*/
QThreadPool::~QThreadPool()
{
+ Q_D(QThreadPool);
waitForDone();
+ Q_ASSERT(d->queue.isEmpty());
+ Q_ASSERT(d->allThreads.isEmpty());
}
/*!
@@ -477,8 +467,8 @@ QThreadPool::~QThreadPool()
*/
QThreadPool *QThreadPool::globalInstance()
{
- static QPointer<QThreadPool> theInstance;
- static QBasicMutex theMutex;
+ Q_CONSTINIT static QPointer<QThreadPool> theInstance;
+ Q_CONSTINIT static QBasicMutex theMutex;
const QMutexLocker locker(&theMutex);
if (theInstance.isNull() && !QCoreApplication::closingDown())
@@ -487,6 +477,21 @@ QThreadPool *QThreadPool::globalInstance()
}
/*!
+ Returns the QThreadPool instance for Qt Gui.
+ \internal
+*/
+QThreadPool *QThreadPoolPrivate::qtGuiInstance()
+{
+ Q_CONSTINIT static QPointer<QThreadPool> guiInstance;
+ Q_CONSTINIT static QBasicMutex theMutex;
+
+ const QMutexLocker locker(&theMutex);
+ if (guiInstance.isNull() && !QCoreApplication::closingDown())
+ guiInstance = new QThreadPool();
+ return guiInstance;
+}
+
+/*!
Reserves a thread and uses it to run \a runnable, unless this thread will
make the current thread count exceed maxThreadCount(). In that case,
\a runnable is added to a run queue instead. The \a priority argument can
@@ -508,34 +513,27 @@ void QThreadPool::start(QRunnable *runnable, int priority)
Q_D(QThreadPool);
QMutexLocker locker(&d->mutex);
- if (runnable->autoDelete()) {
- Q_ASSERT(runnable->ref == 0);
- ++runnable->ref;
- }
- if (!d->tryStart(runnable)) {
+ if (!d->tryStart(runnable))
d->enqueueTask(runnable, priority);
-
- if (!d->waitingThreads.isEmpty())
- d->waitingThreads.takeFirst()->runnableReady.wakeOne();
- }
}
/*!
+ \fn template<typename Callable, QRunnable::if_callable<Callable>> void QThreadPool::start(Callable &&callableToRun, int priority)
\overload
\since 5.15
- Reserves a thread and uses it to run \a functionToRun, unless this thread will
+ Reserves a thread and uses it to run \a callableToRun, unless this thread will
make the current thread count exceed maxThreadCount(). In that case,
- \a functionToRun is added to a run queue instead. The \a priority argument can
+ \a callableToRun is added to a run queue instead. The \a priority argument can
be used to control the run queue's order of execution.
+
+ \note This function participates in overload resolution only if \c Callable
+ is a function or function object which can be called with zero arguments.
+
+ \note In Qt version prior to 6.6, this function took std::function<void()>,
+ and therefore couldn't handle move-only callables.
*/
-void QThreadPool::start(std::function<void()> functionToRun, int priority)
-{
- if (!functionToRun)
- return;
- start(QRunnable::create(std::move(functionToRun)), priority);
-}
/*!
Attempts to reserve a thread to run \a runnable.
@@ -558,54 +556,35 @@ bool QThreadPool::tryStart(QRunnable *runnable)
if (!runnable)
return false;
- if (runnable->autoDelete()) {
- Q_ASSERT(runnable->ref == 0);
- ++runnable->ref;
- }
-
Q_D(QThreadPool);
QMutexLocker locker(&d->mutex);
if (d->tryStart(runnable))
return true;
- // Undo the reference above as we did not start the runnable and
- // take over ownership.
- if (runnable->autoDelete()) {
- --runnable->ref;
- Q_ASSERT(runnable->ref == 0);
- }
return false;
}
/*!
+ \fn template<typename Callable, QRunnable::if_callable<Callable>> bool QThreadPool::tryStart(Callable &&callableToRun)
\overload
\since 5.15
- Attempts to reserve a thread to run \a functionToRun.
+ Attempts to reserve a thread to run \a callableToRun.
If no threads are available at the time of calling, then this function
- does nothing and returns \c false. Otherwise, \a functionToRun is run immediately
+ does nothing and returns \c false. Otherwise, \a callableToRun is run immediately
using one available thread and this function returns \c true.
-*/
-bool QThreadPool::tryStart(std::function<void()> functionToRun)
-{
- if (!functionToRun)
- return false;
- Q_D(QThreadPool);
- QMutexLocker locker(&d->mutex);
- if (!d->allThreads.isEmpty() && d->activeThreadCount() >= d->maxThreadCount)
- return false;
+ \note This function participates in overload resolution only if \c Callable
+ is a function or function object which can be called with zero arguments.
- QRunnable *runnable = QRunnable::create(std::move(functionToRun));
- if (d->tryStart(runnable))
- return true;
- delete runnable;
- return false;
-}
+ \note In Qt version prior to 6.6, this function took std::function<void()>,
+ and therefore couldn't handle move-only callables.
+*/
/*! \property QThreadPool::expiryTimeout
+ \brief the thread expiry timeout value in milliseconds.
- Threads that are unused for \a expiryTimeout milliseconds are considered
+ Threads that are unused for \e expiryTimeout milliseconds are considered
to have expired and will exit. Such threads will be restarted as needed.
The default \a expiryTimeout is 30000 milliseconds (30 seconds). If
\a expiryTimeout is negative, newly created threads will not expire, e.g.,
@@ -620,12 +599,14 @@ bool QThreadPool::tryStart(std::function<void()> functionToRun)
int QThreadPool::expiryTimeout() const
{
Q_D(const QThreadPool);
+ QMutexLocker locker(&d->mutex);
return d->expiryTimeout;
}
void QThreadPool::setExpiryTimeout(int expiryTimeout)
{
Q_D(QThreadPool);
+ QMutexLocker locker(&d->mutex);
if (d->expiryTimeout == expiryTimeout)
return;
d->expiryTimeout = expiryTimeout;
@@ -633,8 +614,9 @@ void QThreadPool::setExpiryTimeout(int expiryTimeout)
/*! \property QThreadPool::maxThreadCount
- This property represents the maximum number of threads used by the thread
- pool.
+ \brief the maximum number of threads used by the thread pool. This property
+ will default to the value of QThread::idealThreadCount() at the moment the
+ QThreadPool object is created.
\note The thread pool will always use at least 1 thread, even if
\a maxThreadCount limit is zero or negative.
@@ -645,7 +627,8 @@ void QThreadPool::setExpiryTimeout(int expiryTimeout)
int QThreadPool::maxThreadCount() const
{
Q_D(const QThreadPool);
- return d->maxThreadCount;
+ QMutexLocker locker(&d->mutex);
+ return d->requestedMaxThreadCount;
}
void QThreadPool::setMaxThreadCount(int maxThreadCount)
@@ -653,16 +636,16 @@ void QThreadPool::setMaxThreadCount(int maxThreadCount)
Q_D(QThreadPool);
QMutexLocker locker(&d->mutex);
- if (maxThreadCount == d->maxThreadCount)
+ if (maxThreadCount == d->requestedMaxThreadCount)
return;
- d->maxThreadCount = maxThreadCount;
+ d->requestedMaxThreadCount = maxThreadCount;
d->tryToStartMoreThreads();
}
/*! \property QThreadPool::activeThreadCount
- This property represents the number of active threads in the thread pool.
+ \brief the number of active threads in the thread pool.
\note It is possible for this function to return a value that is greater
than maxThreadCount(). See reserveThread() for more details.
@@ -683,7 +666,10 @@ int QThreadPool::activeThreadCount() const
Once you are done with the thread, call releaseThread() to allow it to be
reused.
- \note This function will always increase the number of active threads.
+ \note Even if reserving maxThreadCount() threads or more, the thread pool
+ will still allow a minimum of one thread.
+
+ \note This function will increase the reported number of active threads.
This means that by using this function, it is possible for
activeThreadCount() to return a value greater than maxThreadCount() .
@@ -697,9 +683,7 @@ void QThreadPool::reserveThread()
}
/*! \property QThreadPool::stackSize
-
- This property contains the stack size for the thread pool worker
- threads.
+ \brief the stack size for the thread pool worker threads.
The value of the property is only used when the thread pool creates
new threads. Changing it has no effect for already created
@@ -713,15 +697,45 @@ void QThreadPool::reserveThread()
void QThreadPool::setStackSize(uint stackSize)
{
Q_D(QThreadPool);
+ QMutexLocker locker(&d->mutex);
d->stackSize = stackSize;
}
uint QThreadPool::stackSize() const
{
Q_D(const QThreadPool);
+ QMutexLocker locker(&d->mutex);
return d->stackSize;
}
+/*! \property QThreadPool::threadPriority
+ \brief the thread priority for new worker threads.
+
+ The value of the property is only used when the thread pool starts
+ new threads. Changing it has no effect for already running threads.
+
+ The default value is QThread::InheritPriority, which makes QThread
+ use the same priority as the one the QThreadPool object lives in.
+
+ \sa QThread::Priority
+
+ \since 6.2
+*/
+
+void QThreadPool::setThreadPriority(QThread::Priority priority)
+{
+ Q_D(QThreadPool);
+ QMutexLocker locker(&d->mutex);
+ d->threadPriority = priority;
+}
+
+QThread::Priority QThreadPool::threadPriority() const
+{
+ Q_D(const QThreadPool);
+ QMutexLocker locker(&d->mutex);
+ return d->threadPriority;
+}
+
/*!
Releases a thread previously reserved by a call to reserveThread().
@@ -743,6 +757,57 @@ void QThreadPool::releaseThread()
}
/*!
+ Releases a thread previously reserved with reserveThread() and uses it
+ to run \a runnable.
+
+ Note that the thread pool takes ownership of the \a runnable if
+ \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c true,
+ and the \a runnable will be deleted automatically by the thread
+ pool after the \l{QRunnable::run()}{runnable->run()} returns. If
+ \l{QRunnable::autoDelete()}{runnable->autoDelete()} returns \c false,
+ ownership of \a runnable remains with the caller. Note that
+ changing the auto-deletion on \a runnable after calling this
+ functions results in undefined behavior.
+
+ \note Calling this when no threads are reserved results in
+ undefined behavior.
+
+ \since 6.3
+ \sa reserveThread(), start()
+*/
+void QThreadPool::startOnReservedThread(QRunnable *runnable)
+{
+ if (!runnable)
+ return releaseThread();
+
+ Q_D(QThreadPool);
+ QMutexLocker locker(&d->mutex);
+ Q_ASSERT(d->reservedThreads > 0);
+ --d->reservedThreads;
+
+ if (!d->tryStart(runnable)) {
+ // This can only happen if we reserved max threads,
+ // and something took the one minimum thread.
+ d->enqueueTask(runnable, INT_MAX);
+ }
+}
+
+/*!
+ \fn template<typename Callable, QRunnable::if_callable<Callable>> void QThreadPool::startOnReservedThread(Callable &&callableToRun)
+ \overload
+ \since 6.3
+
+ Releases a thread previously reserved with reserveThread() and uses it
+ to run \a callableToRun.
+
+ \note This function participates in overload resolution only if \c Callable
+ is a function or function object which can be called with zero arguments.
+
+ \note In Qt version prior to 6.6, this function took std::function<void()>,
+ and therefore couldn't handle move-only callables.
+*/
+
+/*!
Waits up to \a msecs milliseconds for all threads to exit and removes all
threads from the thread pool. Returns \c true if all threads were removed;
otherwise it returns \c false. If \a msecs is -1 (the default), the timeout
@@ -769,25 +834,18 @@ void QThreadPool::clear()
d->clear();
}
-#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
-/*!
- \internal
-
- Returns \c true if \a thread is a thread managed by this thread pool.
-*/
-#else
/*!
\since 6.0
Returns \c true if \a thread is a thread managed by this thread pool.
*/
-#endif
bool QThreadPool::contains(const QThread *thread) const
{
Q_D(const QThreadPool);
const QThreadPoolThread *poolThread = qobject_cast<const QThreadPoolThread *>(thread);
if (!poolThread)
return false;
+ QMutexLocker locker(&d->mutex);
return d->allThreads.contains(const_cast<QThreadPoolThread *>(poolThread));
}
diff --git a/src/corelib/thread/qthreadpool.h b/src/corelib/thread/qthreadpool.h
index 0d143574cf..a097ace14b 100644
--- a/src/corelib/thread/qthreadpool.h
+++ b/src/corelib/thread/qthreadpool.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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 QTHREADPOOL_H
#define QTHREADPOOL_H
@@ -51,7 +15,6 @@ QT_REQUIRE_CONFIG(thread);
QT_BEGIN_NAMESPACE
-
class QThreadPoolPrivate;
class Q_CORE_EXPORT QThreadPool : public QObject
{
@@ -61,6 +24,7 @@ class Q_CORE_EXPORT QThreadPool : public QObject
Q_PROPERTY(int maxThreadCount READ maxThreadCount WRITE setMaxThreadCount)
Q_PROPERTY(int activeThreadCount READ activeThreadCount)
Q_PROPERTY(uint stackSize READ stackSize WRITE setStackSize)
+ Q_PROPERTY(QThread::Priority threadPriority READ threadPriority WRITE setThreadPriority)
friend class QFutureInterfaceBase;
public:
@@ -72,8 +36,22 @@ public:
void start(QRunnable *runnable, int priority = 0);
bool tryStart(QRunnable *runnable);
+#if QT_CORE_REMOVED_SINCE(6, 6)
void start(std::function<void()> functionToRun, int priority = 0);
bool tryStart(std::function<void()> functionToRun);
+#endif
+
+ void startOnReservedThread(QRunnable *runnable);
+#if QT_CORE_REMOVED_SINCE(6, 6)
+ void startOnReservedThread(std::function<void()> functionToRun);
+#endif
+
+ template <typename Callable, QRunnable::if_callable<Callable> = true>
+ void start(Callable &&functionToRun, int priority = 0);
+ template <typename Callable, QRunnable::if_callable<Callable> = true>
+ bool tryStart(Callable &&functionToRun);
+ template <typename Callable, QRunnable::if_callable<Callable> = true>
+ void startOnReservedThread(Callable &&functionToRun);
int expiryTimeout() const;
void setExpiryTimeout(int expiryTimeout);
@@ -86,6 +64,9 @@ public:
void setStackSize(uint stackSize);
uint stackSize() const;
+ void setThreadPriority(QThread::Priority priority);
+ QThread::Priority threadPriority() const;
+
void reserveThread();
void releaseThread();
@@ -95,9 +76,31 @@ public:
bool contains(const QThread *thread) const;
- Q_REQUIRED_RESULT bool tryTake(QRunnable *runnable);
+ [[nodiscard]] bool tryTake(QRunnable *runnable);
};
+template <typename Callable, QRunnable::if_callable<Callable>>
+void QThreadPool::start(Callable &&functionToRun, int priority)
+{
+ start(QRunnable::create(std::forward<Callable>(functionToRun)), priority);
+}
+
+template <typename Callable, QRunnable::if_callable<Callable>>
+bool QThreadPool::tryStart(Callable &&functionToRun)
+{
+ QRunnable *runnable = QRunnable::create(std::forward<Callable>(functionToRun));
+ if (tryStart(runnable))
+ return true;
+ delete runnable;
+ return false;
+}
+
+template <typename Callable, QRunnable::if_callable<Callable>>
+void QThreadPool::startOnReservedThread(Callable &&functionToRun)
+{
+ startOnReservedThread(QRunnable::create(std::forward<Callable>(functionToRun)));
+}
+
QT_END_NAMESPACE
#endif
diff --git a/src/corelib/thread/qthreadpool_p.h b/src/corelib/thread/qthreadpool_p.h
index f019b480f5..67c703fabd 100644
--- a/src/corelib/thread/qthreadpool_p.h
+++ b/src/corelib/thread/qthreadpool_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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 QTHREADPOOL_P_H
#define QTHREADPOOL_P_H
@@ -55,6 +19,7 @@
#include "QtCore/qmutex.h"
#include "QtCore/qthread.h"
#include "QtCore/qwaitcondition.h"
+#include "QtCore/qthreadpool.h"
#include "QtCore/qset.h"
#include "QtCore/qqueue.h"
#include "private/qobject_p.h"
@@ -65,47 +30,44 @@ QT_BEGIN_NAMESPACE
class QDeadlineTimer;
-class QueuePage {
+class QueuePage
+{
public:
enum {
MaxPageSize = 256
};
- QueuePage(QRunnable *runnable, int pri)
- : m_priority(pri)
- {
- push(runnable);
- }
+ QueuePage(QRunnable *runnable, int pri) : m_priority(pri) { push(runnable); }
- bool isFull() {
- return m_lastIndex >= MaxPageSize - 1;
- }
+ bool isFull() { return m_lastIndex >= MaxPageSize - 1; }
- bool isFinished() {
- return m_firstIndex > m_lastIndex;
- }
+ bool isFinished() { return m_firstIndex > m_lastIndex; }
- void push(QRunnable *runnable) {
+ void push(QRunnable *runnable)
+ {
Q_ASSERT(runnable != nullptr);
Q_ASSERT(!isFull());
m_lastIndex += 1;
m_entries[m_lastIndex] = runnable;
}
- void skipToNextOrEnd() {
+ void skipToNextOrEnd()
+ {
while (!isFinished() && m_entries[m_firstIndex] == nullptr) {
m_firstIndex += 1;
}
}
- QRunnable *first() {
+ QRunnable *first()
+ {
Q_ASSERT(!isFinished());
QRunnable *runnable = m_entries[m_firstIndex];
Q_ASSERT(runnable);
return runnable;
}
- QRunnable *pop() {
+ QRunnable *pop()
+ {
Q_ASSERT(!isFinished());
QRunnable *runnable = first();
Q_ASSERT(runnable);
@@ -120,7 +82,8 @@ public:
return runnable;
}
- bool tryTake(QRunnable *runnable) {
+ bool tryTake(QRunnable *runnable)
+ {
Q_ASSERT(!isFinished());
for (int i = m_firstIndex; i <= m_lastIndex; i++) {
if (m_entries[i] == runnable) {
@@ -135,9 +98,7 @@ public:
return false;
}
- int priority() const {
- return m_priority;
- }
+ int priority() const { return m_priority; }
private:
int m_priority = 0;
@@ -160,8 +121,11 @@ public:
int activeThreadCount() const;
void tryToStartMoreThreads();
+ bool areAllThreadsActive() const;
bool tooManyThreadsActive() const;
+ int maxThreadCount() const
+ { return qMax(requestedMaxThreadCount, 1); } // documentation says we start at least one
void startThread(QRunnable *runnable = nullptr);
void reset();
bool waitForDone(int msecs);
@@ -170,18 +134,22 @@ public:
void stealAndRunRunnable(QRunnable *runnable);
void deletePageIfFinished(QueuePage *page);
+ static QThreadPool *qtGuiInstance();
+
mutable QMutex mutex;
QSet<QThreadPoolThread *> allThreads;
QQueue<QThreadPoolThread *> waitingThreads;
QQueue<QThreadPoolThread *> expiredThreads;
QList<QueuePage *> queue;
QWaitCondition noActiveThreads;
+ QString objectName;
int expiryTimeout = 30000;
- int maxThreadCount = QThread::idealThreadCount();
+ int requestedMaxThreadCount = QThread::idealThreadCount(); // don't use this directly
int reservedThreads = 0;
int activeThreads = 0;
uint stackSize = 0;
+ QThread::Priority threadPriority = QThread::InheritPriority;
};
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qthreadstorage.cpp b/src/corelib/thread/qthreadstorage.cpp
index 7f78f69bc7..c2029fe1b3 100644
--- a/src/corelib/thread/qthreadstorage.cpp
+++ b/src/corelib/thread/qthreadstorage.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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
#include "qthreadstorage.h"
@@ -65,10 +29,10 @@ void qtsDebug(const char *fmt, ...)
va_end(va);
}
#else
-# define DEBUG_MSG if(false)qDebug
+# define DEBUG_MSG if (false)qDebug
#endif
-static QBasicMutex destructorsMutex;
+Q_CONSTINIT static QBasicMutex destructorsMutex;
typedef QList<void (*)(void *)> DestructorMap;
Q_GLOBAL_STATIC(DestructorMap, destructors)
@@ -87,15 +51,15 @@ QThreadStorageData::QThreadStorageData(void (*func)(void *))
no where to store it, and no way to actually call it.
*/
QThreadData *data = QThreadData::current();
- id = data->tls.count();
+ id = data->tls.size();
DEBUG_MSG("QThreadStorageData: Allocated id %d, destructor %p cannot be stored", id, func);
return;
}
- for (id = 0; id < destr->count(); id++) {
+ for (id = 0; id < destr->size(); id++) {
if (destr->at(id) == nullptr)
break;
}
- if (id == destr->count()) {
+ if (id == destr->size()) {
destr->append(func);
} else {
(*destr)[id] = func;
@@ -225,7 +189,7 @@ void QThreadStorageData::finish(void **p)
The hasLocalData() function allows the programmer to determine if
data has previously been set using the setLocalData() function.
- This is also useful for lazy initializiation.
+ This is also useful for lazy initialization.
If T is a pointer type, QThreadStorage takes ownership of the data
(which must be created on the heap with \c new) and deletes it when
diff --git a/src/corelib/thread/qthreadstorage.h b/src/corelib/thread/qthreadstorage.h
index 9eb8672e92..1a8dacae0f 100644
--- a/src/corelib/thread/qthreadstorage.h
+++ b/src/corelib/thread/qthreadstorage.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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 QTHREADSTORAGE_H
#define QTHREADSTORAGE_H
@@ -158,6 +122,8 @@ QT_END_NAMESPACE
#include <type_traits>
+QT_BEGIN_NAMESPACE
+
template <typename T, typename U>
inline bool qThreadStorage_hasLocalData(const QScopedPointer<T, U> &data)
{
@@ -209,7 +175,7 @@ public:
return qThreadStorage_hasLocalData(data);
}
- inline T& localData()
+ inline T &localData()
{
if (!data)
data.reset(new T());
@@ -227,6 +193,8 @@ public:
}
};
+QT_END_NAMESPACE
+
#endif // QT_CONFIG(thread)
#endif // QTHREADSTORAGE_H
diff --git a/src/corelib/thread/qtsan_impl.h b/src/corelib/thread/qtsan_impl.h
new file mode 100644
index 0000000000..b28d65e65f
--- /dev/null
+++ b/src/corelib/thread/qtsan_impl.h
@@ -0,0 +1,79 @@
+// Copyright (C) 2017 Intel Corporation.
+// Copyright (C) 2022 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTSAN_IMPL_H
+#define QTSAN_IMPL_H
+
+#include <QtCore/qglobal.h>
+
+#if (__has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)) && __has_include(<sanitizer/tsan_interface.h>)
+# define QT_BUILDING_UNDER_TSAN
+# include <sanitizer/tsan_interface.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QtTsan {
+#ifdef QT_BUILDING_UNDER_TSAN
+inline void futexAcquire(void *addr, void *addr2 = nullptr)
+{
+ // A futex call ensures total ordering on the futex words
+ // (in either success or failure of the call). Instruct TSAN accordingly,
+ // as TSAN does not understand the futex(2) syscall (or equivalent).
+ ::__tsan_acquire(addr);
+ if (addr2)
+ ::__tsan_acquire(addr2);
+}
+
+inline void futexRelease(void *addr, void *addr2 = nullptr)
+{
+ if (addr2)
+ ::__tsan_release(addr2);
+ ::__tsan_release(addr);
+}
+
+inline void mutexPreLock(void *addr, unsigned flags)
+{
+ ::__tsan_mutex_pre_lock(addr, flags);
+}
+
+inline void mutexPostLock(void *addr, unsigned flags, int recursion)
+{
+ ::__tsan_mutex_post_lock(addr, flags, recursion);
+}
+
+inline void mutexPreUnlock(void *addr, unsigned flags)
+{
+ ::__tsan_mutex_pre_unlock(addr, flags);
+}
+
+inline void mutexPostUnlock(void *addr, unsigned flags)
+{
+ ::__tsan_mutex_post_unlock(addr, flags);
+}
+
+enum : unsigned {
+ MutexWriteReentrant = ::__tsan_mutex_write_reentrant,
+ TryLock = ::__tsan_mutex_try_lock,
+ TryLockFailed = ::__tsan_mutex_try_lock_failed,
+};
+#else
+inline void futexAcquire(void *, void * = nullptr) {}
+inline void futexRelease(void *, void * = nullptr) {}
+
+enum : unsigned {
+ MutexWriteReentrant,
+ TryLock,
+ TryLockFailed,
+};
+inline void mutexPreLock(void *, unsigned) {}
+inline void mutexPostLock(void *, unsigned, int) {}
+inline void mutexPreUnlock(void *, unsigned) {}
+inline void mutexPostUnlock(void *, unsigned) {}
+#endif // QT_BUILDING_UNDER_TSAN
+} // namespace QtTsan
+
+QT_END_NAMESPACE
+
+#endif // QTSAN_IMPL_H
diff --git a/src/corelib/thread/qwaitcondition.h b/src/corelib/thread/qwaitcondition.h
index 0a47ac3717..8f8270c918 100644
--- a/src/corelib/thread/qwaitcondition.h
+++ b/src/corelib/thread/qwaitcondition.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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 QWAITCONDITION_H
#define QWAITCONDITION_H
@@ -73,7 +37,7 @@ public:
private:
Q_DISABLE_COPY(QWaitCondition)
- QWaitConditionPrivate * d;
+ QWaitConditionPrivate *d;
};
#else
diff --git a/src/corelib/thread/qwaitcondition.qdoc b/src/corelib/thread/qwaitcondition.qdoc
index 014d549477..8466eb13d2 100644
--- a/src/corelib/thread/qwaitcondition.qdoc
+++ b/src/corelib/thread/qwaitcondition.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** 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 Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\class QWaitCondition
@@ -75,12 +51,12 @@
simultaneously are unpredictable.
Wait conditions are a powerful thread synchronization primitive.
- The \l{Wait Conditions Example} example shows how
- to use QWaitCondition as an alternative to QSemaphore for
- controlling access to a circular buffer shared by a producer
+ The \l{Producer and Consumer using Wait Conditions} example
+ shows how to use QWaitCondition as an alternative to QSemaphore
+ for controlling access to a circular buffer shared by a producer
thread and a consumer thread.
- \sa QMutex, QSemaphore, QThread, {Wait Conditions Example}
+ \sa QMutex, QSemaphore, QThread, {Producer and Consumer using Wait Conditions}
*/
/*!
@@ -122,10 +98,16 @@
/*!
\fn bool QWaitCondition::wait(QMutex *lockedMutex, unsigned long time)
\overload
+
+ Releases the \a lockedMutex and waits on the wait condition for \a time
+ milliseconds.
*/
/*!
\fn bool QWaitCondition::wait(QReadWriteLock *lockedReadWriteLock, unsigned long time)
\overload
+
+ Releases the \a lockedReadWriteLock and waits on the wait condition for \a
+ time milliseconds.
*/
/*!
diff --git a/src/corelib/thread/qwaitcondition_p.h b/src/corelib/thread/qwaitcondition_p.h
index 5133e52e92..14833d56ef 100644
--- a/src/corelib/thread/qwaitcondition_p.h
+++ b/src/corelib/thread/qwaitcondition_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
-** Contact: http://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) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWAITCONDITION_P_H
#define QWAITCONDITION_P_H
@@ -43,10 +7,9 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of qmutex.cpp, qmutex_unix.cpp, and qmutex_win.cpp. This header
-// file may change from version to version without notice, or even be
-// removed.
+// This file is not part of the Qt API. It exists for the convenience of
+// qmutex.cpp and qmutex_unix.cpp. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
@@ -54,97 +17,21 @@
#include <QtCore/QWaitCondition>
#include <QtCore/QMutex>
#include <QtCore/QDeadlineTimer>
+#include <QtCore/private/qglobal_p.h>
#include <condition_variable>
#include <mutex>
QT_BEGIN_NAMESPACE
-namespace QtPrivate
-{
-
-#if defined(Q_OS_INTEGRITY)
-
-class condition_variable;
-
-class mutex : private QMutex
-{
- friend class QtPrivate::condition_variable;
-public:
- // all special member functions are ok!
- // do not expose the (QMutex::Recursive) ctor
- // don't use 'using QMutex::lock;' etc as those have the wrong noexcept
-
- void lock() { return QMutex::lock(); }
- void unlock() { return QMutex::unlock(); }
- bool try_lock() { return QMutex::tryLock(); }
-};
-
-class condition_variable : private QWaitCondition
-{
-public:
- // all special member functions are ok!
-
- void notify_one() { QWaitCondition::wakeOne(); }
- void notify_all() { QWaitCondition::wakeAll(); }
-
- void wait(std::unique_lock<QtPrivate::mutex> &lock) { QWaitCondition::wait(lock.mutex()); }
- template <class Predicate>
- void wait(std::unique_lock<QtPrivate::mutex> &lock, Predicate p)
- {
- while (!p())
- wait(lock);
- }
-
- template <typename Rep, typename Period>
- std::cv_status wait_for(std::unique_lock<QtPrivate::mutex> &lock,
- const std::chrono::duration<Rep, Period> &d)
- {
- return QWaitCondition::wait(lock.mutex(), QDeadlineTimer{d})
- ? std::cv_status::no_timeout
- : std::cv_status::timeout;
- }
- template <typename Rep, typename Period, typename Predicate>
- bool wait_for(std::unique_lock<QtPrivate::mutex> &lock,
- const std::chrono::duration<Rep, Period> &d, Predicate p)
- {
- const auto timer = QDeadlineTimer{d};
- while (!p()) {
- if (!QWaitCondition::wait(lock.mutex(), timer))
- return p();
- }
- return true;
- }
-
- template <typename Clock, typename Duration>
- std::cv_status wait_until(std::unique_lock<QtPrivate::mutex> &lock,
- const std::chrono::time_point<Clock, Duration> &t)
- {
- return QWaitCondition::wait(lock.mutex(), QDeadlineTimer{t})
- ? std::cv_status::no_timeout
- : std::cv_status::timeout;
- }
-
- template <typename Clock, typename Duration, typename Predicate>
- bool wait_until(std::unique_lock<QtPrivate::mutex> &lock,
- const std::chrono::time_point<Clock, Duration> &t, Predicate p)
- {
- const auto timer = QDeadlineTimer{t};
- while (!p()) {
- if (!QWaitCondition::wait(lock.mutex(), timer))
- return p();
- }
- return true;
- }
-
-};
-
-#else // Integrity
-
-using mutex = std::mutex;
-using condition_variable = std::condition_variable;
-
-#endif // Integrity
+namespace QtPrivate {
+// Ideal alignment for mutex and condition_variable: it's the hardware
+// interference size (size of a cache line) if the types are likely to contain
+// the actual data structures, otherwise just that of a pointer.
+static constexpr quintptr IdealMutexAlignment =
+ sizeof(std::mutex) > sizeof(void *) &&
+ sizeof(std::condition_variable) > sizeof(void *) ?
+ 64 : alignof(void*);
} // namespace QtPrivate
diff --git a/src/corelib/thread/qwaitcondition_unix.cpp b/src/corelib/thread/qwaitcondition_unix.cpp
index a1c973562b..d841183f09 100644
--- a/src/corelib/thread/qwaitcondition_unix.cpp
+++ b/src/corelib/thread/qwaitcondition_unix.cpp
@@ -1,55 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** 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.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qplatformdefs.h"
#include "qwaitcondition.h"
+
+#include "qatomic.h"
+#include "qdeadlinetimer.h"
#include "qmutex.h"
+#include "qplatformdefs.h"
#include "qreadwritelock.h"
-#include "qatomic.h"
#include "qstring.h"
-#include "qdeadlinetimer.h"
-#include "private/qdeadlinetimer_p.h"
-#include "qelapsedtimer.h"
-#include "private/qcore_unix_p.h"
-#include "qmutex_p.h"
+#include "private/qcore_unix_p.h"
#include "qreadwritelock_p.h"
#include <errno.h>
@@ -58,63 +20,65 @@
QT_BEGIN_NAMESPACE
-#ifdef Q_OS_ANDROID
-// pthread_condattr_setclock is available only since Android 5.0. On older versions, there's
-// a private function for relative waits (hidden in 5.0).
-// Use weakref so we can determine at runtime whether each of them is present.
-static int local_condattr_setclock(pthread_condattr_t*, clockid_t)
-__attribute__((weakref("pthread_condattr_setclock")));
-
-static int local_cond_timedwait_relative(pthread_cond_t*, pthread_mutex_t *, const timespec *)
-__attribute__((weakref("__pthread_cond_timedwait_relative")));
+static constexpr clockid_t SteadyClockClockId =
+#if !defined(CLOCK_MONOTONIC)
+ // we don't know how to set the monotonic clock
+ CLOCK_REALTIME
+#elif defined(_LIBCPP_VERSION) && defined(_LIBCPP_HAS_NO_MONOTONIC_CLOCK)
+ // libc++ falling back to system_clock
+ CLOCK_REALTIME
+#elif defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_CLOCK_MONOTONIC)
+ // libstdc++ falling back to system_clock
+ CLOCK_REALTIME
+#elif defined(Q_OS_DARWIN)
+ // Darwin lacks pthread_condattr_setclock()
+ CLOCK_REALTIME
+#elif defined(Q_OS_QNX)
+ // unknown why
+ CLOCK_REALTIME
+#elif defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
+ // both libstdc++ and libc++ do use CLOCK_MONOTONIC
+ CLOCK_MONOTONIC
+#else
+# warning "Unknown C++ Standard Library implementation - code may be sub-optimal"
+ CLOCK_REALTIME
#endif
+ ;
-static void report_error(int code, const char *where, const char *what)
+static void qt_report_pthread_error(int code, const char *where, const char *what)
{
if (code != 0)
qErrnoWarning(code, "%s: %s failure", where, what);
}
-void qt_initialize_pthread_cond(pthread_cond_t *cond, const char *where)
+static void qt_initialize_pthread_cond(pthread_cond_t *cond, const char *where)
{
+ pthread_condattr_t *attrp = nullptr;
+
+#if defined(CLOCK_MONOTONIC) && !defined(Q_OS_DARWIN)
pthread_condattr_t condattr;
+ attrp = &condattr;
pthread_condattr_init(&condattr);
-#if (_POSIX_MONOTONIC_CLOCK-0 >= 0)
-#if defined(Q_OS_ANDROID)
- if (local_condattr_setclock && QElapsedTimer::clockType() == QElapsedTimer::MonotonicClock)
- local_condattr_setclock(&condattr, CLOCK_MONOTONIC);
-#elif !defined(Q_OS_MAC)
- if (QElapsedTimer::clockType() == QElapsedTimer::MonotonicClock)
- pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
+ auto destroy = qScopeGuard([&] { pthread_condattr_destroy(&condattr); });
+ if (SteadyClockClockId != CLOCK_REALTIME)
+ pthread_condattr_setclock(&condattr, SteadyClockClockId);
#endif
-#endif
- report_error(pthread_cond_init(cond, &condattr), where, "cv init");
- pthread_condattr_destroy(&condattr);
+
+ qt_report_pthread_error(pthread_cond_init(cond, attrp), where, "cv init");
}
-void qt_abstime_for_timeout(timespec *ts, QDeadlineTimer deadline)
+static void qt_abstime_for_timeout(timespec *ts, QDeadlineTimer deadline)
{
-#ifdef Q_OS_MAC
- // on Mac, qt_gettime() (on qelapsedtimer_mac.cpp) returns ticks related to the Mach absolute time
- // that doesn't work with pthread
- // Mac also doesn't have clock_gettime
- struct timeval tv;
- qint64 nsec = deadline.remainingTimeNSecs();
- gettimeofday(&tv, 0);
- ts->tv_sec = tv.tv_sec + nsec / (1000 * 1000 * 1000);
- ts->tv_nsec = tv.tv_usec * 1000 + nsec % (1000 * 1000 * 1000);
-
- normalizedTimespec(*ts);
-#else
- // depends on QDeadlineTimer's internals!!
- static_assert(QDeadlineTimerNanosecondsInT2);
- ts->tv_sec = deadline._q_data().first;
- ts->tv_nsec = deadline._q_data().second;
-#endif
+ using namespace std::chrono;
+ using Clock =
+ std::conditional_t<SteadyClockClockId == CLOCK_REALTIME, system_clock, steady_clock>;
+ auto timePoint = deadline.deadline<Clock>();
+ *ts = durationToTimespec(timePoint.time_since_epoch());
}
-class QWaitConditionPrivate {
+class QWaitConditionPrivate
+{
public:
pthread_mutex_t mutex;
pthread_cond_t cond;
@@ -124,14 +88,6 @@ public:
int wait_relative(QDeadlineTimer deadline)
{
timespec ti;
-#ifdef Q_OS_ANDROID
- if (!local_condattr_setclock && local_cond_timedwait_relative) {
- qint64 nsec = deadline.remainingTimeNSecs();
- ti.tv_sec = nsec / (1000 * 1000 * 1000);
- ti.tv_nsec = nsec - ti.tv_sec * 1000 * 1000 * 1000;
- return local_cond_timedwait_relative(&cond, &mutex, &ti);
- }
-#endif
qt_abstime_for_timeout(&ti, deadline);
return pthread_cond_timedwait(&cond, &mutex, &ti);
}
@@ -146,9 +102,7 @@ public:
code = pthread_cond_wait(&cond, &mutex);
}
if (code == 0 && wakeups == 0) {
- // many vendors warn of spurious wakeups from
- // pthread_cond_wait(), especially after signal delivery,
- // even though POSIX doesn't allow for it... sigh
+ // spurious wakeup
continue;
}
break;
@@ -160,46 +114,51 @@ public:
Q_ASSERT_X(wakeups > 0, "QWaitCondition::wait", "internal error (wakeups)");
--wakeups;
}
- report_error(pthread_mutex_unlock(&mutex), "QWaitCondition::wait()", "mutex unlock");
+ qt_report_pthread_error(pthread_mutex_unlock(&mutex), "QWaitCondition::wait()",
+ "mutex unlock");
if (code && code != ETIMEDOUT)
- report_error(code, "QWaitCondition::wait()", "cv wait");
+ qt_report_pthread_error(code, "QWaitCondition::wait()", "cv wait");
return (code == 0);
}
};
-
QWaitCondition::QWaitCondition()
{
d = new QWaitConditionPrivate;
- report_error(pthread_mutex_init(&d->mutex, nullptr), "QWaitCondition", "mutex init");
+ qt_report_pthread_error(pthread_mutex_init(&d->mutex, nullptr), "QWaitCondition", "mutex init");
qt_initialize_pthread_cond(&d->cond, "QWaitCondition");
d->waiters = d->wakeups = 0;
}
-
QWaitCondition::~QWaitCondition()
{
- report_error(pthread_cond_destroy(&d->cond), "QWaitCondition", "cv destroy");
- report_error(pthread_mutex_destroy(&d->mutex), "QWaitCondition", "mutex destroy");
+ qt_report_pthread_error(pthread_cond_destroy(&d->cond), "QWaitCondition", "cv destroy");
+ qt_report_pthread_error(pthread_mutex_destroy(&d->mutex), "QWaitCondition", "mutex destroy");
delete d;
}
void QWaitCondition::wakeOne()
{
- report_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wakeOne()", "mutex lock");
+ qt_report_pthread_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wakeOne()",
+ "mutex lock");
d->wakeups = qMin(d->wakeups + 1, d->waiters);
- report_error(pthread_cond_signal(&d->cond), "QWaitCondition::wakeOne()", "cv signal");
- report_error(pthread_mutex_unlock(&d->mutex), "QWaitCondition::wakeOne()", "mutex unlock");
+ qt_report_pthread_error(pthread_cond_signal(&d->cond), "QWaitCondition::wakeOne()",
+ "cv signal");
+ qt_report_pthread_error(pthread_mutex_unlock(&d->mutex), "QWaitCondition::wakeOne()",
+ "mutex unlock");
}
void QWaitCondition::wakeAll()
{
- report_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wakeAll()", "mutex lock");
+ qt_report_pthread_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wakeAll()",
+ "mutex lock");
d->wakeups = d->waiters;
- report_error(pthread_cond_broadcast(&d->cond), "QWaitCondition::wakeAll()", "cv broadcast");
- report_error(pthread_mutex_unlock(&d->mutex), "QWaitCondition::wakeAll()", "mutex unlock");
+ qt_report_pthread_error(pthread_cond_broadcast(&d->cond), "QWaitCondition::wakeAll()",
+ "cv broadcast");
+ qt_report_pthread_error(pthread_mutex_unlock(&d->mutex), "QWaitCondition::wakeAll()",
+ "mutex unlock");
}
bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
@@ -211,14 +170,10 @@ bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
bool QWaitCondition::wait(QMutex *mutex, QDeadlineTimer deadline)
{
- if (! mutex)
+ if (!mutex)
return false;
- if (mutex->isRecursive()) {
- qWarning("QWaitCondition: cannot wait on recursive mutexes");
- return false;
- }
- report_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wait()", "mutex lock");
+ qt_report_pthread_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wait()", "mutex lock");
++d->waiters;
mutex->unlock();
@@ -238,24 +193,26 @@ bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
bool QWaitCondition::wait(QReadWriteLock *readWriteLock, QDeadlineTimer deadline)
{
+ using namespace QReadWriteLockStates;
+
if (!readWriteLock)
return false;
- auto previousState = readWriteLock->stateForWaitCondition();
- if (previousState == QReadWriteLock::Unlocked)
+ auto previousState = QReadWriteLockPrivate::stateForWaitCondition(readWriteLock);
+ if (previousState == Unlocked)
return false;
- if (previousState == QReadWriteLock::RecursivelyLocked) {
+ if (previousState == RecursivelyLocked) {
qWarning("QWaitCondition: cannot wait on QReadWriteLocks with recursive lockForWrite()");
return false;
}
- report_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wait()", "mutex lock");
+ qt_report_pthread_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wait()", "mutex lock");
++d->waiters;
readWriteLock->unlock();
bool returnValue = d->wait(deadline);
- if (previousState == QReadWriteLock::LockedForWrite)
+ if (previousState == LockedForWrite)
readWriteLock->lockForWrite();
else
readWriteLock->lockForRead();
diff --git a/src/corelib/thread/qwaitcondition_win.cpp b/src/corelib/thread/qwaitcondition_win.cpp
index ba34129b7f..ba53309e1b 100644
--- a/src/corelib/thread/qwaitcondition_win.cpp
+++ b/src/corelib/thread/qwaitcondition_win.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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
#include "qwaitcondition.h"
#include "qdeadlinetimer.h"
@@ -45,7 +9,7 @@
#include "qlist.h"
#include "qalgorithms.h"
-#define Q_MUTEX_T void*
+#define Q_MUTEX_T void *
#include <private/qmutex_p.h>
#include <private/qreadwritelock_p.h>
#include <qt_windows.h>
@@ -79,7 +43,7 @@ public:
EventQueue freeQueue;
QWaitConditionEvent *pre();
- bool wait(QWaitConditionEvent *wce, unsigned long time);
+ bool wait(QWaitConditionEvent *wce, QDeadlineTimer deadline);
void post(QWaitConditionEvent *wce, bool ret);
};
@@ -87,7 +51,7 @@ QWaitConditionEvent *QWaitConditionPrivate::pre()
{
mtx.lock();
QWaitConditionEvent *wce =
- freeQueue.isEmpty() ? new QWaitConditionEvent : freeQueue.takeFirst();
+ freeQueue.isEmpty() ? new QWaitConditionEvent : freeQueue.takeFirst();
wce->priority = GetThreadPriority(GetCurrentThread());
wce->wokenUp = false;
@@ -104,18 +68,25 @@ QWaitConditionEvent *QWaitConditionPrivate::pre()
return wce;
}
-bool QWaitConditionPrivate::wait(QWaitConditionEvent *wce, unsigned long time)
+bool QWaitConditionPrivate::wait(QWaitConditionEvent *wce, QDeadlineTimer deadline)
{
// wait for the event
- bool ret = false;
- switch (WaitForSingleObjectEx(wce->event, time, FALSE)) {
- default: break;
-
- case WAIT_OBJECT_0:
- ret = true;
- break;
+ while (true) {
+ const DWORD timeout = deadline.isForever()
+ ? INFINITE
+ : DWORD(std::min(deadline.remainingTime(), qint64(INFINITE - 1)));
+
+ switch (WaitForSingleObjectEx(wce->event, timeout, FALSE)) {
+ case WAIT_OBJECT_0:
+ return true;
+ case WAIT_TIMEOUT:
+ if (deadline.hasExpired())
+ return false;
+ break;
+ default:
+ return false;
+ }
}
- return ret;
}
void QWaitConditionPrivate::post(QWaitConditionEvent *wce, bool ret)
@@ -159,17 +130,20 @@ QWaitCondition::~QWaitCondition()
bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
{
+ if (time == std::numeric_limits<unsigned long>::max())
+ return wait(mutex, QDeadlineTimer(QDeadlineTimer::Forever));
+ return wait(mutex, QDeadlineTimer(time));
+}
+
+bool QWaitCondition::wait(QMutex *mutex, QDeadlineTimer deadline)
+{
if (!mutex)
return false;
- if (mutex->isRecursive()) {
- qWarning("QWaitCondition::wait: Cannot wait on recursive mutexes");
- return false;
- }
QWaitConditionEvent *wce = d->pre();
mutex->unlock();
- bool returnValue = d->wait(wce, time);
+ bool returnValue = d->wait(wce, deadline);
mutex->lock();
d->post(wce, returnValue);
@@ -177,19 +151,23 @@ bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
return returnValue;
}
-bool QWaitCondition::wait(QMutex *mutex, QDeadlineTimer deadline)
+bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
{
- return wait(mutex, deadline.remainingTime());
+ if (time == std::numeric_limits<unsigned long>::max())
+ return wait(readWriteLock, QDeadlineTimer(QDeadlineTimer::Forever));
+ return wait(readWriteLock, QDeadlineTimer(time));
}
-bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
+bool QWaitCondition::wait(QReadWriteLock *readWriteLock, QDeadlineTimer deadline)
{
+ using namespace QReadWriteLockStates;
+
if (!readWriteLock)
return false;
- auto previousState = readWriteLock->stateForWaitCondition();
- if (previousState == QReadWriteLock::Unlocked)
+ auto previousState = QReadWriteLockPrivate::stateForWaitCondition(readWriteLock);
+ if (previousState == Unlocked)
return false;
- if (previousState == QReadWriteLock::RecursivelyLocked) {
+ if (previousState == RecursivelyLocked) {
qWarning("QWaitCondition: cannot wait on QReadWriteLocks with recursive lockForWrite()");
return false;
}
@@ -197,9 +175,9 @@ bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
QWaitConditionEvent *wce = d->pre();
readWriteLock->unlock();
- bool returnValue = d->wait(wce, time);
+ bool returnValue = d->wait(wce, deadline);
- if (previousState == QReadWriteLock::LockedForWrite)
+ if (previousState == LockedForWrite)
readWriteLock->lockForWrite();
else
readWriteLock->lockForRead();
@@ -208,16 +186,11 @@ bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
return returnValue;
}
-bool QWaitCondition::wait(QReadWriteLock *readWriteLock, QDeadlineTimer deadline)
-{
- return wait(readWriteLock, deadline.remainingTime());
-}
-
void QWaitCondition::wakeOne()
{
// wake up the first waiting thread in the queue
QMutexLocker locker(&d->mtx);
- for (QWaitConditionEvent *current : qAsConst(d->queue)) {
+ for (QWaitConditionEvent *current : std::as_const(d->queue)) {
if (current->wokenUp)
continue;
SetEvent(current->event);
@@ -230,7 +203,7 @@ void QWaitCondition::wakeAll()
{
// wake up the all threads in the queue
QMutexLocker locker(&d->mtx);
- for (QWaitConditionEvent *current : qAsConst(d->queue)) {
+ for (QWaitConditionEvent *current : std::as_const(d->queue)) {
SetEvent(current->event);
current->wokenUp = true;
}
diff --git a/src/corelib/thread/qyieldcpu.h b/src/corelib/thread/qyieldcpu.h
new file mode 100644
index 0000000000..d5da58deeb
--- /dev/null
+++ b/src/corelib/thread/qyieldcpu.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2023 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QYIELDCPU_H
+#define QYIELDCPU_H
+
+#include <QtCore/qcompilerdetection.h>
+#include <QtCore/qprocessordetection.h>
+#include <QtCore/qtconfigmacros.h>
+
+#ifdef Q_CC_MSVC_ONLY
+// MSVC defines _YIELD_PROCESSOR() in <xatomic.h>, but as that is a private
+// header, we include the public ones
+# ifdef __cplusplus
+# include <atomic>
+extern "C"
+# endif
+void _mm_pause(void); // the compiler recognizes as intrinsic
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_CC_GNU
+__attribute__((artificial))
+#endif
+Q_ALWAYS_INLINE void qYieldCpu(void) Q_DECL_NOEXCEPT;
+
+void qYieldCpu(void)
+#ifdef __cplusplus
+ noexcept
+#endif
+{
+#if __has_builtin(__yield)
+ __yield(); // Generic
+#elif defined(_YIELD_PROCESSOR) && defined(Q_CC_MSVC)
+ _YIELD_PROCESSOR(); // Generic; MSVC's <atomic>
+
+#elif __has_builtin(__builtin_ia32_pause)
+ __builtin_ia32_pause();
+#elif defined(Q_PROCESSOR_X86) && defined(Q_CC_GNU)
+ // GCC < 10 didn't have __has_builtin()
+ __builtin_ia32_pause();
+#elif defined(Q_PROCESSOR_X86) && defined(Q_CC_MSVC)
+ _mm_pause();
+#elif defined(Q_PROCESSOR_X86)
+ asm("pause"); // hopefully asm() works in this compiler
+
+#elif __has_builtin(__builtin_arm_yield)
+ __builtin_arm_yield();
+#elif defined(Q_PROCESSOR_ARM) && Q_PROCESSOR_ARM >= 7 && defined(Q_CC_GNU)
+ asm("yield"); // this works everywhere
+
+#elif defined(Q_PROCESSOR_RISCV)
+ asm(".word 0x0100000f"); // a.k.a. "pause"
+
+#elif defined(_YIELD_PROCESSOR) && defined(Q_CC_GHS)
+ _YIELD_PROCESSOR; // Green Hills (INTEGRITY), but only on ARM
+#endif
+}
+
+QT_END_NAMESPACE
+
+#endif // QYIELDCPU_H
diff --git a/src/corelib/thread/qyieldcpu.qdoc b/src/corelib/thread/qyieldcpu.qdoc
new file mode 100644
index 0000000000..c55b2c8a73
--- /dev/null
+++ b/src/corelib/thread/qyieldcpu.qdoc
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2023 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \fn qYieldCpu()
+ \inmodule QtCore
+ \ingroup thread
+ \relates QAtomicInteger
+ //! \relatesalso QAtomicPointer
+ \since 6.7
+
+ Pauses the execution of the current thread for an unspecified time, using
+ hardware instructions, without de-scheduling this thread. This function is
+ meant to be used in high-throughput loops where the code expects another
+ thread to modify an atomic variable. This is completely different from
+ QThread::yieldCurrentThread(), which is an OS-level operation that may take
+ the whole thread off the CPU and allow other threads (possibly belonging to
+ other processes) to run.
+
+ So, instead of
+ \code
+ while (!condition)
+ ;
+ \endcode
+
+ one should write
+ \code
+ while (!condition)
+ qYieldCpu();
+ \endcode
+
+ This is useful both with and without hardware multithreading on the same
+ core. In the case of hardware threads, it serves to prevent further
+ speculative execution filling up the pipeline, which could starve the
+ sibling thread of resources. Across cores and higher levels of separation,
+ it allows the cache coherency protocol to allocate the cache line being
+ modified and inspected to the logical processor whose result this code is
+ expecting.
+
+ It is also recommended to loop around code that does not modify the global
+ variable, to avoid contention in exclusively obtaining the memory location.
+ Therefore, an atomic modification loop such as a spinlock acquisition
+ should be:
+
+ \code
+ while (true) {
+ while (!readOnlyCondition(atomic))
+ qYieldCpu();
+ if (modify(atomic))
+ break;
+ }
+ \endcode
+
+ On x86 processors and on RISC-V processors with the \c{Zihintpause}
+ extension, this will emit the \c PAUSE instruction, which is ignored on
+ processors that don't support it; on ARMv7 or later ARM processors, it will
+ emit the \c{YIELD} instruction.
+*/
diff --git a/src/corelib/thread/thread.pri b/src/corelib/thread/thread.pri
deleted file mode 100644
index d36c8011ef..0000000000
--- a/src/corelib/thread/thread.pri
+++ /dev/null
@@ -1,85 +0,0 @@
-# Qt core thread module
-
-HEADERS += \
- thread/qmutex.h \
- thread/qreadwritelock.h \
- thread/qrunnable.h \
- thread/qthread.h \
- thread/qthreadstorage.h \
- thread/qwaitcondition_p.h \
- thread/qwaitcondition.h
-
-SOURCES += \
- thread/qrunnable.cpp \
- thread/qthread.cpp
-
-win32 {
- HEADERS += thread/qatomic_msvc.h
-
- SOURCES += thread/qthread_win.cpp
-} else {
- SOURCES += thread/qthread_unix.cpp
-}
-
-qtConfig(thread) {
- HEADERS += \
- thread/qatomic.h \
- thread/qatomic_bootstrap.h \
- thread/qatomic_cxx11.h \
- thread/qbasicatomic.h \
- thread/qfutex_p.h \
- thread/qgenericatomic.h \
- thread/qlocking_p.h \
- thread/qmutex_p.h \
- thread/qorderedmutexlocker_p.h \
- thread/qreadwritelock_p.h \
- thread/qsemaphore.h \
- thread/qthreadpool.h \
- thread/qthreadpool_p.h \
- thread/qthread_p.h
-
- SOURCES += \
- thread/qatomic.cpp \
- thread/qmutex.cpp \
- thread/qreadwritelock.cpp \
- thread/qsemaphore.cpp \
- thread/qthreadpool.cpp \
- thread/qthreadstorage.cpp
-
- win32 {
- SOURCES += \
- thread/qmutex_win.cpp \
- thread/qwaitcondition_win.cpp
- } else {
- darwin {
- SOURCES += thread/qmutex_mac.cpp
- } else: linux {
- SOURCES += thread/qmutex_linux.cpp
- } else {
- SOURCES += thread/qmutex_unix.cpp
- }
- SOURCES += thread/qwaitcondition_unix.cpp
- }
-}
-
-qtConfig(future) {
- HEADERS += \
- thread/qexception.h \
- thread/qfuture.h \
- thread/qfuture_impl.h \
- thread/qfutureinterface.h \
- thread/qfutureinterface_p.h \
- thread/qfuturesynchronizer.h \
- thread/qfuturewatcher.h \
- thread/qfuturewatcher_p.h \
- thread/qresultstore.h \
- thread/qpromise.h
-
- SOURCES += \
- thread/qexception.cpp \
- thread/qfutureinterface.cpp \
- thread/qfuturewatcher.cpp \
- thread/qresultstore.cpp
-}
-
-qtConfig(std-atomic64): QMAKE_USE += libatomic