summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/thread')
-rw-r--r--src/corelib/thread/qatomic.cpp170
-rw-r--r--src/corelib/thread/qatomic.h83
-rw-r--r--src/corelib/thread/qatomic_bootstrap.h105
-rw-r--r--src/corelib/thread/qatomic_cxx11.h63
-rw-r--r--src/corelib/thread/qbasicatomic.h68
-rw-r--r--src/corelib/thread/qexception.cpp46
-rw-r--r--src/corelib/thread/qexception.h44
-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.h182
-rw-r--r--src/corelib/thread/qfutex_win_p.h58
-rw-r--r--src/corelib/thread/qfuture.h79
-rw-r--r--src/corelib/thread/qfuture.qdoc247
-rw-r--r--src/corelib/thread/qfuture_impl.h545
-rw-r--r--src/corelib/thread/qfutureinterface.cpp265
-rw-r--r--src/corelib/thread/qfutureinterface.h69
-rw-r--r--src/corelib/thread/qfutureinterface_p.h72
-rw-r--r--src/corelib/thread/qfuturesynchronizer.h59
-rw-r--r--src/corelib/thread/qfuturesynchronizer.qdoc34
-rw-r--r--src/corelib/thread/qfuturewatcher.cpp56
-rw-r--r--src/corelib/thread/qfuturewatcher.h40
-rw-r--r--src/corelib/thread/qfuturewatcher_p.h40
-rw-r--r--src/corelib/thread/qgenericatomic.h377
-rw-r--r--src/corelib/thread/qlocking_p.h48
-rw-r--r--src/corelib/thread/qmutex.cpp183
-rw-r--r--src/corelib/thread/qmutex.h225
-rw-r--r--src/corelib/thread/qmutex_mac.cpp51
-rw-r--r--src/corelib/thread/qmutex_p.h80
-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.h75
-rw-r--r--src/corelib/thread/qpromise.h80
-rw-r--r--src/corelib/thread/qpromise.qdoc88
-rw-r--r--src/corelib/thread/qreadwritelock.cpp265
-rw-r--r--src/corelib/thread/qreadwritelock.h110
-rw-r--r--src/corelib/thread/qreadwritelock_p.h103
-rw-r--r--src/corelib/thread/qresultstore.cpp40
-rw-r--r--src/corelib/thread/qresultstore.h69
-rw-r--r--src/corelib/thread/qrunnable.cpp88
-rw-r--r--src/corelib/thread/qrunnable.h149
-rw-r--r--src/corelib/thread/qsemaphore.cpp206
-rw-r--r--src/corelib/thread/qsemaphore.h66
-rw-r--r--src/corelib/thread/qthread.cpp307
-rw-r--r--src/corelib/thread/qthread.h72
-rw-r--r--src/corelib/thread/qthread_p.h193
-rw-r--r--src/corelib/thread/qthread_unix.cpp114
-rw-r--r--src/corelib/thread/qthread_win.cpp136
-rw-r--r--src/corelib/thread/qthreadpool.cpp155
-rw-r--r--src/corelib/thread/qthreadpool.h73
-rw-r--r--src/corelib/thread/qthreadpool_p.h42
-rw-r--r--src/corelib/thread/qthreadstorage.cpp48
-rw-r--r--src/corelib/thread/qthreadstorage.h40
-rw-r--r--src/corelib/thread/qtsan_impl.h79
-rw-r--r--src/corelib/thread/qwaitcondition.h40
-rw-r--r--src/corelib/thread/qwaitcondition.qdoc42
-rw-r--r--src/corelib/thread/qwaitcondition_p.h147
-rw-r--r--src/corelib/thread/qwaitcondition_unix.cpp188
-rw-r--r--src/corelib/thread/qwaitcondition_win.cpp106
-rw-r--r--src/corelib/thread/qyieldcpu.h64
-rw-r--r--src/corelib/thread/qyieldcpu.qdoc59
61 files changed, 3110 insertions, 3926 deletions
diff --git a/src/corelib/thread/qatomic.cpp b/src/corelib/thread/qatomic.cpp
index 3c05661980..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
@@ -433,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]
+
*/
/*!
@@ -448,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]
*/
/*!
@@ -464,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]
*/
/*!
@@ -480,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
*/
/*!
@@ -986,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
*/
/*!
@@ -1306,7 +1359,7 @@
\endlist
- \sa QAtomicInteger
+ \sa QAtomicInteger, qYieldCpu()
*/
/*!
@@ -1689,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
@@ -1707,22 +1766,19 @@ static_assert(sizeof(QAtomicInteger<quintptr>));
static_assert(sizeof(QAtomicInteger<qptrdiff>));
static_assert(sizeof(QAtomicInteger<char32_t>));
-#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
static_assert(sizeof(QAtomicInteger<char16_t>));
-#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 2080167d2c..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,7 +32,7 @@ public:
return *this;
}
-#ifdef Q_CLANG_QDOC
+#ifdef Q_QDOC
T loadRelaxed() const;
T loadAcquire() const;
void storeRelaxed(T newValue);
@@ -106,6 +55,11 @@ public:
bool testAndSetRelease(T expectedValue, T newValue);
bool testAndSetOrdered(T expectedValue, T newValue);
+ 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();
@@ -160,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
@@ -171,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());
}
@@ -227,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 125803a15c..0000000000
--- a/src/corelib/thread/qatomic_bootstrap.h
+++ /dev/null
@@ -1,105 +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
-
-#define Q_ATOMIC_INT8_IS_SUPPORTED
-template<> struct QAtomicOpsSupport<1> { enum { IsSupported = 1 }; };
-#define Q_ATOMIC_INT16_IS_SUPPORTED
-template<> struct QAtomicOpsSupport<2> { enum { IsSupported = 1 }; };
-#define Q_ATOMIC_INT32_IS_SUPPORTED
-#define Q_ATOMIC_INT64_IS_SUPPORTED
-template<> struct QAtomicOpsSupport<8> { enum { IsSupported = 1 }; };
-
-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 bf487bbf1f..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
@@ -185,7 +150,7 @@ 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
@@ -278,13 +243,27 @@ template <typename X> struct QAtomicOps
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 bool isTestAndSetNative() noexcept
diff --git a/src/corelib/thread/qbasicatomic.h b/src/corelib/thread/qbasicatomic.h
index 52ad2ff369..6d061ea49a 100644
--- a/src/corelib/thread/qbasicatomic.h
+++ b/src/corelib/thread/qbasicatomic.h
@@ -1,61 +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)
-# include <QtCore/qatomic_cxx11.h>
-
-// No fallback
-#else
-# error "Qt requires C++11 support"
-#endif
+#include <QtCore/qatomic_cxx11.h>
QT_WARNING_PUSH
QT_WARNING_DISABLE_MSVC(4522)
@@ -69,10 +19,6 @@ QT_END_NAMESPACE
#pragma qt_sync_stop_processing
#endif
-// New atomics
-
-#define QT_BASIC_ATOMIC_HAS_CONSTRUCTORS
-
template <typename T>
class QBasicAtomicInteger
{
@@ -80,7 +26,7 @@ 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;
@@ -203,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;
@@ -300,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 8967e9db1c..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
@@ -216,7 +180,7 @@ QUnhandledException *QUnhandledException::clone() const
return new QUnhandledException(*this);
}
-#if !defined(Q_CLANG_QDOC)
+#if !defined(Q_QDOC)
namespace QtPrivate {
@@ -260,7 +224,7 @@ void ExceptionStore::rethrowException() const
} // 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 1c69287615..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,7 +16,7 @@ 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
{
@@ -72,7 +36,7 @@ public:
QUnhandledException(QUnhandledException &&other) noexcept;
QUnhandledException(const QUnhandledException &other) noexcept;
- void swap(QUnhandledException &other) noexcept { qSwap(d, other.d); }
+ void swap(QUnhandledException &other) noexcept { d.swap(other.d); }
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QUnhandledException)
QUnhandledException &operator=(const QUnhandledException &other) noexcept;
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 037207a5c0..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,37 +15,16 @@
// We mean it.
//
-#include <qglobal.h>
-
-#if (__has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)) && __has_include(<sanitizer/tsan_interface.h>)
-# include <sanitizer/tsan_interface.h>
-inline void _q_tsan_acquire(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 _q_tsan_release(void *addr, void *addr2 = nullptr)
-{
- if (addr2)
- __tsan_release(addr2);
- __tsan_release(addr);
-}
-#else
-inline void _q_tsan_acquire(void *, void * = nullptr) {}
-inline void _q_tsan_release(void *, void * = nullptr) {}
-#endif // building for TSAN and __has_include(<sanitizer/tsan_interface.h>)
+#include <qdeadlinetimer.h>
+#include <private/qglobal_p.h>
QT_BEGIN_NAMESPACE
namespace QtDummyFutex {
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 &)
@@ -90,115 +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
-
-// RISC-V does not supply __NR_futex
-# ifndef __NR_futex
-# define __NR_futex __NR_futex_time64
-# endif
-
-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
- {
- _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 <qt_windows.h>
-
-QT_BEGIN_NAMESPACE
-namespace QtWindowsFutex {
-#define QT_ALWAYS_USE_FUTEX
-constexpr inline bool futexAvailable() { return true; }
-
-template <typename Atomic>
-inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
-{
- _q_tsan_release(&futex);
- WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), INFINITE);
- _q_tsan_acquire(&futex);
-}
-template <typename Atomic>
-inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, qint64 nstimeout)
-{
- BOOL r = WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), DWORD(nstimeout / 1000 / 1000));
- 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 QtFutex = QtWindowsFutex;
-QT_END_NAMESPACE
+# 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 7aed38be16..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
@@ -91,7 +55,7 @@ public:
return *this;
}
-#if defined(Q_CLANG_QDOC)
+#if defined(Q_QDOC)
~QFuture() { }
QFuture(const QFuture<T> &) { }
QFuture<T> & operator=(const QFuture<T> &) { }
@@ -191,6 +155,14 @@ QT_WARNING_POP
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:
@@ -317,6 +289,8 @@ private:
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>>;
@@ -431,6 +405,16 @@ QFuture<T> QFuture<T>::onCanceled(QObject *context, Function &&handler)
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);
@@ -461,7 +445,7 @@ struct MetaTypeQFutureHelper<QFuture<T>>
namespace QtFuture {
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template<typename OutputSequence, typename InputIt,
typename ValueType = typename std::iterator_traits<InputIt>::value_type,
@@ -536,7 +520,20 @@ QFuture<QtFuture::WhenAnyResult<T>> whenAny(InputIt first, InputIt last);
template<typename... Futures>
QFuture<std::variant<std::decay_t<Futures>...>> whenAny(Futures &&... futures);
-#endif // Q_CLANG_QDOC
+#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 makeReadyVoidFuture();
+}
+#endif // QT_DEPRECATED_SINCE(6, 10)
} // namespace QtFuture
diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc
index d52db1c481..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
@@ -99,6 +75,15 @@
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.
+ 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
@@ -133,13 +118,21 @@
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::makeReadyFuture() and
+ 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::makeReadyFuture(),
+ \sa QPromise, QtFuture::connect(), QtFuture::makeReadyVoidFuture(),
+ QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(),
QtFuture::makeExceptionalFuture(), QFutureWatcher, {Qt Concurrent}
*/
@@ -397,7 +390,7 @@
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
@@ -412,7 +405,7 @@
\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
@@ -424,7 +417,7 @@
\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.
@@ -435,7 +428,7 @@
\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. Note that
@@ -451,7 +444,7 @@
*/
#if 0
-/*! \fn template <typename T> std::vector<T> QFuture<T>::takeResults()
+/*! \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
@@ -470,7 +463,7 @@
*/
#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
@@ -505,7 +498,7 @@
\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.
@@ -513,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.
@@ -521,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.
@@ -529,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.
@@ -638,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.
@@ -650,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.
@@ -669,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.
*/
@@ -943,7 +936,7 @@
\sa QtFuture::whenAny()
*/
-/*! \fn template<class Sender, class Signal> static QFuture<ArgsType<Signal>> QtFuture::connect(Sender *sender, Signal signal)
+/*! \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
@@ -982,10 +975,11 @@
\sa QFuture, QFuture::then()
*/
-/*! \fn template<typename T> static QFuture<std::decay_t<T>> QtFuture::makeReadyFuture(T &&value)
+/*! \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.
@@ -996,13 +990,20 @@
const int result = *f.takeResult(); // result == 42
\endcode
- \sa QFuture, QtFuture::makeExceptionalFuture()
+ 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.
@@ -1016,14 +1017,21 @@
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::makeExceptionalFuture()
+ 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.
@@ -1035,7 +1043,42 @@
const auto results = f.results(); // results == { 1, 2, 3 }
\endcode
- \sa QFuture, QtFuture::makeExceptionalFuture()
+ 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)
@@ -1055,7 +1098,8 @@
}
\endcode
- \sa QFuture, QException, QtFuture::makeReadyFuture()
+ \sa QFuture, QException, QtFuture::makeReadyVoidFuture(),
+ QtFuture::makeReadyValueFuture()
*/
/*! \fn template<typename T> static QFuture<T> QtFuture::makeExceptionalFuture(std::exception_ptr exception)
@@ -1080,7 +1124,44 @@
}
\endcode
- \sa QFuture, QException, QtFuture::makeReadyFuture()
+ \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)
@@ -1179,8 +1260,7 @@
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()
*/
@@ -1220,13 +1300,23 @@
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
- throughout the execution of the chain.
+ during setup of the chain.
\sa onFailed(), onCanceled()
*/
-/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onFailed(Function &&handler)
+/*! \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
@@ -1269,7 +1359,7 @@
\sa then(), onCanceled()
*/
-/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onFailed(QObject *context, 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
@@ -1286,15 +1376,18 @@
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
- throughout the execution of the chain.
+ 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> QFuture<T> QFuture<T>::onCanceled(Function &&handler)
+/*! \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
@@ -1339,7 +1432,7 @@
\sa then(), onFailed()
*/
-/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onCanceled(QObject *context, Function &&handler)
+/*! \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
@@ -1349,14 +1442,52 @@
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
- throughout the execution of the chain.
+ 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
diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h
index 869a791b15..79fc6d9a01 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
@@ -50,7 +14,9 @@
#include <QtCore/qfutureinterface.h>
#include <QtCore/qthreadpool.h>
#include <QtCore/qexception.h>
-#include <QtCore/qpointer.h>
+#include <QtCore/qpromise.h>
+
+#include <memory>
QT_BEGIN_NAMESPACE
@@ -82,10 +48,6 @@ 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>>;
@@ -153,7 +115,7 @@ auto createTuple(Arg &&arg, Args &&... 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 arg;
+ return std::forward<Arg>(arg);
} else {
return cutTuple(std::make_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...),
std::make_index_sequence<Size>());
@@ -282,17 +244,6 @@ 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>
-struct isTuple<std::tuple<T...>> : std::true_type
-{
-};
-template<class T>
-inline constexpr bool isTupleV = isTuple<T>::value;
-
template<class T>
inline constexpr bool isQFutureV = false;
@@ -322,6 +273,12 @@ using IsRandomAccessible =
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,
@@ -332,9 +289,8 @@ class Continuation
{
public:
template<typename F = Function>
- Continuation(F &&func, const QFuture<ParentResultType> &f,
- const QFutureInterface<ResultType> &p)
- : promise(p), parentFuture(f), function(std::forward<F>(func))
+ Continuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p)
+ : promise(std::move(p)), parentFuture(f), function(std::forward<F>(func))
{
}
virtual ~Continuation() = default;
@@ -342,15 +298,15 @@ public:
bool execute();
template<typename F = Function>
- static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &p,
+ 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> &p,
+ static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi,
QThreadPool *pool);
template<typename F = Function>
- static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &p,
+ static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi,
QObject *context);
private:
@@ -367,7 +323,7 @@ protected:
void runFunction();
protected:
- QFutureInterface<ResultType> promise;
+ QPromise<ResultType> promise;
QFuture<ParentResultType> parentFuture;
Function function;
};
@@ -377,9 +333,9 @@ class SyncContinuation final : public Continuation<Function, ResultType, ParentR
{
public:
template<typename F = Function>
- SyncContinuation(F &&func, const QFuture<ParentResultType> &f,
- const QFutureInterface<ResultType> &p)
- : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f, p)
+ SyncContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p)
+ : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f,
+ std::move(p))
{
}
@@ -395,12 +351,12 @@ class AsyncContinuation final : public QRunnable,
{
public:
template<typename F = Function>
- AsyncContinuation(F &&func, const QFuture<ParentResultType> &f,
- const QFutureInterface<ResultType> &p, QThreadPool *pool = nullptr)
- : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f, p),
+ 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;
@@ -429,15 +385,15 @@ class FailureHandler
public:
template<typename F = Function>
static void create(F &&function, QFuture<ResultType> *future,
- const QFutureInterface<ResultType> &promise);
+ const QFutureInterface<ResultType> &fi);
template<typename F = Function>
- static void create(F &&function, QFuture<ResultType> *future,
- QFutureInterface<ResultType> &promise, QObject *context);
+ static void create(F &&function, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi,
+ QObject *context);
template<typename F = Function>
- FailureHandler(F &&func, const QFuture<ResultType> &f, const QFutureInterface<ResultType> &p)
- : promise(p), parentFuture(f), handler(std::forward<F>(func))
+ FailureHandler(F &&func, const QFuture<ResultType> &f, QPromise<ResultType> &&p)
+ : promise(std::move(p)), parentFuture(f), handler(std::forward<F>(func))
{
}
@@ -450,7 +406,7 @@ private:
void handleAllExceptions();
private:
- QFutureInterface<ResultType> promise;
+ QPromise<ResultType> promise;
QFuture<ResultType> parentFuture;
Function handler;
};
@@ -460,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());
@@ -474,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);
@@ -489,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);
@@ -497,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>
@@ -516,17 +472,17 @@ bool Continuation<Function, ResultType, ParentResultType>::execute()
// 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;
}
}
@@ -539,18 +495,26 @@ bool Continuation<Function, ResultType, ParentResultType>::execute()
template<class Function>
struct ContinuationWrapper
{
- ContinuationWrapper(Function &&f) : function(QSharedPointer<Function>::create(std::move(f))) { }
- void operator()(const QFutureInterfaceBase &parentData) { (*function)(parentData); }
+ 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:
- QSharedPointer<Function> function;
+ Function function;
};
template<typename Function, typename ResultType, typename ParentResultType>
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);
@@ -564,22 +528,24 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
// 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);
}
}
- p.setLaunchAsync(launchAsync);
+ fi.setLaunchAsync(launchAsync);
- auto continuation = [func = std::forward<F>(func), p, pool,
+ 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) {
- continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
- std::forward<Function>(func), parent, p, pool);
+ 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, p);
+ std::forward<Function>(func), parent, std::move(promise_));
}
bool isLaunched = continuationJob->execute();
@@ -591,29 +557,26 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
continuationJob = nullptr;
}
};
- if constexpr (!std::is_copy_constructible_v<Function>)
- f->d.setContinuation(ContinuationWrapper(std::move(continuation)), p.d);
- else
- f->d.setContinuation(std::move(continuation), p.d);
+ f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
}
template<typename Function, typename ResultType, typename ParentResultType>
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);
- p.setLaunchAsync(true);
- p.setThreadPool(pool);
+ fi.setLaunchAsync(true);
+ fi.setThreadPool(pool);
- auto continuation = [func = std::forward<F>(func), p,
+ 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, p, pool);
+ 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.
@@ -622,37 +585,42 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
continuationJob = nullptr;
}
};
+ f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
+}
- if constexpr (!std::is_copy_constructible_v<Function>)
- f->d.setContinuation(ContinuationWrapper(std::move(continuation)), p.d);
- else
- f->d.setContinuation(std::move(continuation), p.d);
+// defined in qfutureinterface.cpp:
+Q_CORE_EXPORT void watchContinuationImpl(const QObject *context, QSlotObjectBase *slotObj,
+ QFutureInterfaceBase &fi);
+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> &p,
+ QFutureInterface<ResultType> &fi,
QObject *context)
{
Q_ASSERT(f);
-
- auto continuation = [func = std::forward<F>(func), p, context = QPointer<QObject>(context)](
- const QFutureInterfaceBase &parentData) mutable {
- Q_ASSERT(context);
- const auto parent = QFutureInterface<ParentResultType>(parentData).future();
- QMetaObject::invokeMethod(context, [func = std::forward<F>(func), p, parent]() mutable {
- SyncContinuation<Function, ResultType, ParentResultType> continuationJob(
- std::forward<Function>(func), parent, p);
- continuationJob.execute();
- });
+ 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();
};
- if constexpr (!std::is_copy_constructible_v<Function>)
- f->d.setContinuation(ContinuationWrapper(std::move(continuation)), p.d);
- else
- f->d.setContinuation(std::move(continuation), p.d);
+ QtPrivate::watchContinuation(context, std::move(continuation), f->d);
}
template<typename Function, typename ResultType, typename ParentResultType>
@@ -686,32 +654,27 @@ 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
@@ -719,49 +682,37 @@ void fulfillPromise(QFutureInterface<T> &promise, Function &&handler)
template<class Function, class ResultType>
template<class F>
void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future,
- const QFutureInterface<ResultType> &promise)
+ const QFutureInterface<ResultType> &fi)
{
Q_ASSERT(future);
- auto failureContinuation = [function = std::forward<F>(function),
- promise](const QFutureInterfaceBase &parentData) mutable {
+ 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, promise);
+ parent, std::move(promise_));
failureHandler.run();
};
- if constexpr (!std::is_copy_constructible_v<Function>)
- future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation)));
- else
- future->d.setContinuation(std::move(failureContinuation));
+ 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> &promise,
+ 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();
+ };
- auto failureContinuation =
- [function = std::forward<F>(function), promise,
- context = QPointer<QObject>(context)](const QFutureInterfaceBase &parentData) mutable {
- Q_ASSERT(context);
- const auto parent = QFutureInterface<ResultType>(parentData).future();
- QMetaObject::invokeMethod(
- context, [function = std::forward<F>(function), promise, parent]() mutable {
- FailureHandler<Function, ResultType> failureHandler(
- std::forward<Function>(function), parent, promise);
- failureHandler.run();
- });
- };
-
- if constexpr (!std::is_copy_constructible_v<Function>)
- future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation)));
- else
- future->d.setContinuation(std::move(failureContinuation));
+ QtPrivate::watchContinuation(context, std::move(failureContinuation), future->d);
}
template<class Function, class ResultType>
@@ -769,7 +720,7 @@ void FailureHandler<Function, ResultType>::run()
{
Q_ASSERT(parentFuture.isFinished());
- promise.reportStarted();
+ promise.start();
if (parentFuture.d.hasException()) {
using ArgType = typename QtPrivate::ArgResolver<Function>::First;
@@ -778,10 +729,12 @@ void FailureHandler<Function, ResultType>::run()
} 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>
@@ -794,21 +747,17 @@ void FailureHandler<Function, ResultType>::handleException()
} 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());
}
}
@@ -822,7 +771,7 @@ void FailureHandler<Function, ResultType>::handleAllExceptions()
try {
QtPrivate::fulfillPromise(promise, std::forward<Function>(handler));
} catch (...) {
- promise.reportException(std::current_exception());
+ promise.setException(std::current_exception());
}
}
}
@@ -834,68 +783,49 @@ class CanceledHandler
{
public:
template<class F = Function>
- static QFuture<ResultType> create(F &&handler, QFuture<ResultType> *future,
- QFutureInterface<ResultType> &promise)
+ static void create(F &&handler, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi)
{
Q_ASSERT(future);
- auto canceledContinuation = [promise, handler = std::forward<F>(handler)](
+ 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, promise);
+ run(std::forward<F>(handler), parentFuture, std::move(promise));
};
-
- if constexpr (!std::is_copy_constructible_v<Function>)
- future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation)));
- else
- future->d.setContinuation(std::move(canceledContinuation));
-
- return promise.future();
+ future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation)));
}
template<class F = Function>
- static QFuture<ResultType> create(F &&handler, QFuture<ResultType> *future,
- QFutureInterface<ResultType> &promise, QObject *context)
+ static void create(F &&handler, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi,
+ QObject *context)
{
Q_ASSERT(future);
-
- auto canceledContinuation = [promise, handler = std::forward<F>(handler),
- context = QPointer<QObject>(context)](
- const QFutureInterfaceBase &parentData) mutable {
- Q_ASSERT(context);
- auto parentFuture = QFutureInterface<ResultType>(parentData).future();
- QMetaObject::invokeMethod(
- context, [promise, parentFuture, handler = std::forward<F>(handler)]() mutable {
- run(std::forward<F>(handler), parentFuture, promise);
- });
+ 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));
};
- if constexpr (!std::is_copy_constructible_v<Function>)
- future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation)));
- else
- future->d.setContinuation(std::move(canceledContinuation));
-
- return promise.future();
+ QtPrivate::watchContinuation(context, std::move(canceledContinuation), future->d);
}
template<class F = Function>
- static void run(F &&handler, QFuture<ResultType> &parentFuture,
- QFutureInterface<ResultType> &promise)
+ static void run(F &&handler, QFuture<ResultType> &parentFuture, QPromise<ResultType> &&promise)
{
- promise.reportStarted();
+ promise.start();
if (parentFuture.isCanceled()) {
#ifndef QT_NO_EXCEPTIONS
if (parentFuture.d.hasException()) {
// Propagate the exception to the result future
- promise.reportException(parentFuture.d.exceptionStore().exception());
+ promise.setException(parentFuture.d.exceptionStore().exception());
} else {
try {
#endif
QtPrivate::fulfillPromise(promise, std::forward<F>(handler));
#ifndef QT_NO_EXCEPTIONS
} catch (...) {
- promise.reportException(std::current_exception());
+ promise.setException(std::current_exception());
}
}
#endif
@@ -903,10 +833,73 @@ public:
QtPrivate::fulfillPromise(promise, parentFuture);
}
- promise.reportFinished();
+ 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();
+ });
+ 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 {
@@ -920,6 +913,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>();
@@ -931,7 +929,7 @@ static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal)
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 {
QObject::disconnect(connections->first);
@@ -950,6 +948,12 @@ static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal)
});
}
+ if (!connections->first) {
+ promise.reportCanceled();
+ promise.reportFinished();
+ return promise.future();
+ }
+
connections->second =
QObject::connect(sender, &QObject::destroyed, sender, [promise, connections]() mutable {
QObject::disconnect(connections->first);
@@ -961,8 +965,37 @@ static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal)
return promise.future();
}
-template<typename T, typename = QtPrivate::EnableForNonVoid<T>>
-static QFuture<std::decay_t<T>> makeReadyFuture(T &&value)
+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();
@@ -972,30 +1005,26 @@ static QFuture<std::decay_t<T>> makeReadyFuture(T &&value)
return promise.future();
}
-#if defined(Q_CLANG_QDOC)
-static QFuture<void> makeReadyFuture()
-#else
-template<typename T = void>
-static QFuture<T> makeReadyFuture()
-#endif
-{
- QFutureInterface<T> promise;
- promise.reportStarted();
- promise.reportFinished();
+Q_CORE_EXPORT QFuture<void> makeReadyVoidFuture(); // implemented in qfutureinterface.cpp
- return promise.future();
+#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)
{
- QFutureInterface<T> promise;
- promise.reportStarted();
- promise.reportResults(values);
- promise.reportFinished();
-
- return promise.future();
+ return makeReadyRangeFuture(values);
}
+#endif // QT_DEPRECATED_SINCE(6, 10)
#ifndef QT_NO_EXCEPTIONS
@@ -1032,21 +1061,22 @@ struct WhenAllContext
{
using ValueType = typename ResultFutures::value_type;
- WhenAllContext(qsizetype size) : count(size) {}
+ explicit WhenAllContext(qsizetype size) : remaining(size) {}
template<typename T = ValueType>
void checkForCompletion(qsizetype index, T &&future)
{
futures[index] = std::forward<T>(future);
- Q_ASSERT(count > 0);
- if (--count <= 0) {
- promise.reportResult(futures);
- promise.reportFinished();
+ 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> count;
- QFutureInterface<ResultFutures> promise;
+ QAtomicInteger<qsizetype> remaining;
+ QPromise<ResultFutures> promise;
ResultFutures futures;
};
@@ -1059,24 +1089,25 @@ struct WhenAnyContext
void checkForCompletion(qsizetype, T &&result)
{
if (!ready.fetchAndStoreRelaxed(true)) {
- promise.reportResult(std::forward<T>(result));
- promise.reportFinished();
+ promise.addResult(std::forward<T>(result));
+ promise.finish();
}
}
QAtomicInt ready = false;
- QFutureInterface<ResultType> promise;
+ QPromise<ResultType> promise;
};
template<qsizetype Index, typename ContextType, typename... Ts>
-void addCompletionHandlersImpl(const QSharedPointer<ContextType> &context,
+void addCompletionHandlersImpl(const std::shared_ptr<ContextType> &context,
const std::tuple<Ts...> &t)
{
auto future = std::get<Index>(t);
using ResultType = typename ContextType::ValueType;
- future.then([context](const std::tuple_element_t<Index, std::tuple<Ts...>> &f) {
+ // 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, future]() {
+ }).onCanceled([context=context, future]() {
context->checkForCompletion(Index, ResultType { std::in_place_index<Index>, future });
});
@@ -1085,7 +1116,7 @@ void addCompletionHandlersImpl(const QSharedPointer<ContextType> &context,
}
template<typename ContextType, typename... Ts>
-void addCompletionHandlers(const QSharedPointer<ContextType> &context, const std::tuple<Ts...> &t)
+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);
@@ -1096,17 +1127,18 @@ QFuture<OutputSequence> whenAllImpl(InputIt first, InputIt last)
{
const qsizetype size = std::distance(first, last);
if (size == 0)
- return QtFuture::makeReadyFuture(OutputSequence());
+ return QtFuture::makeReadyValueFuture(OutputSequence());
- auto context = QSharedPointer<QtPrivate::WhenAllContext<OutputSequence>>::create(size);
+ const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(size);
context->futures.resize(size);
- context->promise.reportStarted();
+ context->promise.start();
qsizetype idx = 0;
for (auto it = first; it != last; ++it, ++idx) {
- it->then([context, idx](const ValueType &f) {
+ // 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, idx, f = *it] {
+ }).onCanceled([context=context, idx, f = *it] {
context->checkForCompletion(idx, f);
});
}
@@ -1117,9 +1149,9 @@ template<typename OutputSequence, typename... Futures>
QFuture<OutputSequence> whenAllImpl(Futures &&... futures)
{
constexpr qsizetype size = sizeof...(Futures);
- auto context = QSharedPointer<QtPrivate::WhenAllContext<OutputSequence>>::create(size);
+ const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(size);
context->futures.resize(size);
- context->promise.reportStarted();
+ context->promise.start();
QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward<Futures>(futures)...));
@@ -1135,18 +1167,19 @@ QFuture<QtFuture::WhenAnyResult<typename Future<ValueType>::type>> whenAnyImpl(I
const qsizetype size = std::distance(first, last);
if (size == 0) {
- return QtFuture::makeReadyFuture(
+ return QtFuture::makeReadyValueFuture(
QtFuture::WhenAnyResult { qsizetype(-1), QFuture<PackagedType>() });
}
- auto context = QSharedPointer<QtPrivate::WhenAnyContext<ResultType>>::create();
- context->promise.reportStarted();
+ const auto context = std::make_shared<QtPrivate::WhenAnyContext<ResultType>>();
+ context->promise.start();
qsizetype idx = 0;
for (auto it = first; it != last; ++it, ++idx) {
- it->then([context, idx](const ValueType &f) {
+ // 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, idx, f = *it] {
+ }).onCanceled([context=context, idx, f = *it] {
context->checkForCompletion(idx, QtFuture::WhenAnyResult { idx, f });
});
}
@@ -1158,8 +1191,8 @@ QFuture<std::variant<std::decay_t<Futures>...>> whenAnyImpl(Futures &&... future
{
using ResultType = std::variant<std::decay_t<Futures>...>;
- auto context = QSharedPointer<QtPrivate::WhenAnyContext<ResultType>>::create();
- context->promise.reportStarted();
+ const auto context = std::make_shared<QtPrivate::WhenAnyContext<ResultType>>();
+ context->promise.start();
QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward<Futures>(futures)...));
diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp
index f29f950a1f..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,9 +40,68 @@ 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))
{ }
@@ -102,12 +130,11 @@ 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()
@@ -134,6 +161,13 @@ void QFutureInterfaceBase::cancel(QFutureInterfaceBase::CancelMode mode)
break;
}
+ // Cancel the continuations chain
+ QFutureInterfaceBasePrivate *next = d->continuationData;
+ while (next) {
+ next->continuationState = QFutureInterfaceBasePrivate::Canceled;
+ next = next->continuationData;
+ }
+
d->waitCondition.wakeAll();
d->pausedWaitCondition.wakeAll();
@@ -177,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;
@@ -640,7 +674,6 @@ void QFutureInterfaceBase::reset()
{
d->m_progressValue = 0;
d->m_progress.reset();
- d->setState(QFutureInterfaceBase::NoState);
d->progressTime.invalidate();
d->isValid = false;
}
@@ -748,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);
}
@@ -758,38 +791,37 @@ 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));
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Started));
if (m_progress) {
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange,
- m_progress->minimum,
- m_progress->maximum));
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress,
- m_progressValue,
- m_progress->text));
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange,
+ m_progress->minimum,
+ m_progress->maximum));
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress,
+ m_progressValue,
+ m_progress->text));
} else {
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange,
- 0,
- 0));
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress,
- m_progressValue,
- QString()));
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange,
+ 0,
+ 0));
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress,
+ m_progressValue,
+ QString()));
}
}
@@ -798,36 +830,36 @@ void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface
while (it != data.m_results.end()) {
const int begin = it.resultIndex();
const int end = begin + it.batchSize();
- interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady,
- begin,
- end));
+ iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady,
+ begin,
+ end));
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)
@@ -837,7 +869,7 @@ void QFutureInterfaceBasePrivate::setState(QFutureInterfaceBase::State newState)
void QFutureInterfaceBase::setContinuation(std::function<void(const QFutureInterfaceBase &)> func)
{
- setContinuation(func, nullptr);
+ setContinuation(std::move(func), nullptr);
}
void QFutureInterfaceBase::setContinuation(std::function<void(const QFutureInterfaceBase &)> func,
@@ -845,44 +877,60 @@ void QFutureInterfaceBase::setContinuation(std::function<void(const QFutureInter
{
QMutexLocker lock(&d->continuationMutex);
- if (continuationFutureData)
- continuationFutureData->parentData = d;
-
// If the state is ready, run continuation immediately,
// otherwise save it for later.
if (isFinished()) {
lock.unlock();
func(*this);
- } else {
+ 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(*this);
+ 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
{
- if (isCanceled())
- return true;
-
- auto parent = d->parentData;
- while (parent) {
- // If the future is in Canceled state because it had an exception, we want to
- // continue checking the chain of parents for cancellation, otherwise if the exception
- // is handeled inside the chain, it won't be interrupted even though cancellation has
- // been requested.
- if ((parent->state.loadRelaxed() & Canceled) && !parent->hasException)
- return true;
- parent = parent->parentData;
- }
- return false;
+ return isCanceled() || d->continuationState == QFutureInterfaceBasePrivate::Canceled;
}
void QFutureInterfaceBase::setLaunchAsync(bool value)
@@ -895,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 3130c91420..180a59a94e 100644
--- a/src/corelib/thread/qfutureinterface.h
+++ b/src/corelib/thread/qfutureinterface.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 QFUTUREINTERFACE_H
#define QFUTUREINTERFACE_H
@@ -57,6 +21,7 @@ QT_BEGIN_NAMESPACE
template <typename T> class QFuture;
class QThreadPool;
+class QFutureInterfaceBase;
class QFutureInterfaceBasePrivate;
class QFutureWatcherBase;
class QFutureWatcherBasePrivate;
@@ -74,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
@@ -212,10 +181,17 @@ 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(const QFutureInterfaceBase &)> func);
void setContinuation(std::function<void(const QFutureInterfaceBase &)> func,
QFutureInterfaceBasePrivate *continuationFutureData);
+ void cleanContinuation();
void runContinuation() const;
void setLaunchAsync(bool value);
@@ -268,6 +244,8 @@ public:
inline QFuture<T> future(); // implemented in qfuture.h
+ 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);
@@ -333,7 +311,8 @@ inline bool QFutureInterface<T>::reportResult(const T *result, int index)
}
template<typename T>
-bool 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)
{
QMutexLocker<QMutex> locker{&mutex()};
if (queryState(Canceled) || queryState(Finished))
@@ -343,7 +322,7 @@ bool QFutureInterface<T>::reportAndMoveResult(T &&result, int index)
QtPrivate::ResultStoreBase &store = resultStoreBase();
const int oldResultCount = store.count();
- const int insertIndex = store.moveResult(index, std::forward<T>(result));
+ 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());
@@ -351,6 +330,12 @@ bool QFutureInterface<T>::reportAndMoveResult(T &&result, int index)
}
template<typename T>
+bool QFutureInterface<T>::reportAndMoveResult(T &&result, int index)
+{
+ return reportAndEmplaceResult(index, std::move(result));
+}
+
+template<typename T>
bool QFutureInterface<T>::reportResult(T &&result, int index)
{
return reportAndMoveResult(std::move(result), index);
@@ -379,7 +364,7 @@ inline bool QFutureInterface<T>::reportResults(const QList<T> &_results, int beg
if (store.filterMode()) {
this->reportResultsReady(resultCountBefore, store.count());
} else {
- this->reportResultsReady(insertIndex, insertIndex + _results.count());
+ this->reportResultsReady(insertIndex, insertIndex + _results.size());
}
return true;
}
@@ -484,7 +469,7 @@ template <>
class QFutureInterface<void> : public QFutureInterfaceBase
{
public:
- explicit QFutureInterface<void>(State initialState = NoState)
+ explicit QFutureInterface(State initialState = NoState)
: QFutureInterfaceBase(initialState)
{ }
diff --git a/src/corelib/thread/qfutureinterface_p.h b/src/corelib/thread/qfutureinterface_p.h
index 0c01a0f97d..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
@@ -65,9 +29,13 @@ 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_EVENT_DISABLE_COPY(QFutureCallOutEvent);
+ Q_DECL_EVENT_COMMON(QFutureCallOutEvent)
public:
enum CallOutType {
Started,
@@ -103,26 +71,9 @@ public:
int index1;
int index2;
QString text;
-
- QEvent *clone() const override
- {
- 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();
@@ -190,7 +141,7 @@ public:
QThreadPool *m_pool = nullptr;
// Wrapper for continuation
std::function<void(const QFutureInterfaceBase &)> continuation;
- QFutureInterfaceBasePrivate *parentData = nullptr;
+ QFutureInterfaceBasePrivate *continuationData = nullptr;
RefCount refCount = 1;
QAtomicInt state; // reads and writes can happen unprotected, both must be atomic
@@ -209,6 +160,9 @@ public:
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(); }
diff --git a/src/corelib/thread/qfuturesynchronizer.h b/src/corelib/thread/qfuturesynchronizer.h
index f73041f6a7..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();
}
}
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 f86f0ce6ba..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"
@@ -588,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
@@ -597,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
@@ -610,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.
diff --git a/src/corelib/thread/qfuturewatcher.h b/src/corelib/thread/qfuturewatcher.h
index 0e7490d9a5..89c5b408fa 100644
--- a/src/corelib/thread/qfuturewatcher.h
+++ b/src/corelib/thread/qfuturewatcher.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 QFUTUREWATCHER_H
#define QFUTUREWATCHER_H
diff --git a/src/corelib/thread/qfuturewatcher_p.h b/src/corelib/thread/qfuturewatcher_p.h
index f9a628e4fb..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
diff --git a/src/corelib/thread/qgenericatomic.h b/src/corelib/thread/qgenericatomic.h
index 1fe44c04bf..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
@@ -69,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 constexpr bool isReferenceCountingNative() noexcept
- { return BaseClass::isFetchAndAddNative(); }
- static inline 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
- // Architectures must implement them
- static inline constexpr bool isTestAndSetNative() noexcept;
- static inline 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 constexpr bool isFetchAndStoreNative() noexcept { return false; }
- static inline 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
- for (;;) {
- 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 constexpr bool isFetchAndAddNative() noexcept { return false; }
- static inline 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
- for (;;) {
- 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);
- for (;;) {
- 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);
- for (;;) {
- 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);
- for (;;) {
- 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 5fb80454df..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>
diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp
index ba97ef8a42..ec6c711a4f 100644
--- a/src/corelib/thread/qmutex.cpp
+++ b/src/corelib/thread/qmutex.cpp
@@ -1,50 +1,13 @@
-/****************************************************************************
-**
-** 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"
@@ -182,6 +145,23 @@ void QBasicMutex::destroyInternal(QMutexPrivate *d)
\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
@@ -315,6 +295,8 @@ QRecursiveMutex::~QRecursiveMutex()
*/
/*!
+ \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
@@ -332,16 +314,37 @@ QRecursiveMutex::~QRecursiveMutex()
\sa lock(), unlock()
*/
-bool QRecursiveMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
+
+/*!
+ \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 == -1) {
+ if (timeout.isForever()) {
mutex.lock();
} else {
success = mutex.tryLock(timeout);
@@ -349,6 +352,11 @@ bool QRecursiveMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
if (success)
owner.storeRelaxed(self);
+ else
+ tsanFlags |= QtTsan::TryLockFailed;
+
+ QtTsan::mutexPostLock(this, tsanFlags, 0);
+
return success;
}
@@ -412,6 +420,7 @@ bool QRecursiveMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
void QRecursiveMutex::unlock() noexcept
{
Q_ASSERT(owner.loadRelaxed() == QThread::currentThreadId());
+ QtTsan::mutexPreUnlock(this, 0u);
if (count > 0) {
count--;
@@ -419,6 +428,8 @@ void QRecursiveMutex::unlock() noexcept
owner.storeRelaxed(nullptr);
mutex.unlock();
}
+
+ QtTsan::mutexPostUnlock(this, 0u);
}
@@ -486,6 +497,40 @@ void QRecursiveMutex::unlock() noexcept
*/
/*!
+ \fn template <typename Mutex> QMutexLocker<Mutex>::QMutexLocker(QMutexLocker &&other) noexcept
+ \since 6.4
+
+ 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 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
@@ -495,6 +540,14 @@ void QRecursiveMutex::unlock() noexcept
*/
/*!
+ \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
@@ -604,27 +657,38 @@ void QBasicMutex::lockInternal() QT_MUTEX_LOCK_NOEXCEPT
/*!
\internal helper for lock(int)
*/
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
{
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(timeout < 0)) {
+ if (Q_UNLIKELY(deadlineTimer.isForever())) {
lockInternal();
return true;
}
- QDeadlineTimer deadlineTimer(timeout);
// 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;
- qint64 remainingTime = deadlineTimer.remainingTimeNSecs();
- Q_FOREVER {
- if (!futexWait(d_ptr, dummyFutexValue(), remainingTime))
+ for (;;) {
+ if (!futexWait(d_ptr, dummyFutexValue(), deadlineTimer))
return false;
// We got woken up, so must try to acquire the mutex. We must set
@@ -633,9 +697,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
return true;
- // calculate the remaining time
- remainingTime = deadlineTimer.remainingTimeNSecs();
- if (remainingTime <= 0)
+ if (deadlineTimer.hasExpired())
return false;
}
}
@@ -647,7 +709,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
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
@@ -662,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
@@ -715,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();
@@ -724,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.
@@ -793,7 +854,7 @@ 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,
@@ -802,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_;
@@ -847,12 +908,10 @@ void QMutexPrivate::derefWaiters(int value) noexcept
QT_END_NAMESPACE
-#if defined(Q_OS_LINUX) && defined(QT_ALWAYS_USE_FUTEX)
+#if defined(QT_ALWAYS_USE_FUTEX)
// nothing
-#elif defined(Q_OS_MAC)
+#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 19463da619..743b86939e 100644
--- a/src/corelib/thread/qmutex.h
+++ b/src/corelib/thread/qmutex.h
@@ -1,62 +1,21 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMUTEX_H
#define QMUTEX_H
#include <QtCore/qglobal.h>
#include <QtCore/qatomic.h>
-#include <new>
+#include <QtCore/qdeadlinetimer.h>
+#include <QtCore/qtsan_impl.h>
-#if __has_include(<chrono>)
-# include <chrono>
-# include <limits>
-#endif
-
-class tst_QMutex;
+#include <chrono>
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(thread) || defined(Q_QDOC)
-#if QT_CONFIG(thread) || defined(Q_CLANG_QDOC)
-
-#ifdef Q_OS_LINUX
+#if defined(Q_OS_LINUX) || defined(Q_OS_WIN) // these platforms use futex
# define QT_MUTEX_LOCK_NOEXCEPT noexcept
#else
# define QT_MUTEX_LOCK_NOEXCEPT
@@ -66,34 +25,6 @@ class QMutex;
class QRecursiveMutex;
class QMutexPrivate;
-#if __has_include(<chrono>)
-namespace QtPrivate
-{
- template<class Rep, class Period>
- static int convertToMilliseconds(std::chrono::duration<Rep, Period> duration)
- {
- // N4606 § 30.4.1.3.5 [thread.timedmutex.requirements] specifies that a
- // duration less than or equal to duration.zero() shall result in a
- // try_lock, unlike QMutex's tryLock with a negative duration which
- // results in a lock.
-
- if (duration <= duration.zero())
- return 0;
-
- // when converting from 'duration' to milliseconds, make sure that
- // the result is not shorter than 'duration':
- std::chrono::milliseconds wait = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
- if (wait < duration)
- wait += std::chrono::milliseconds(1);
- Q_ASSERT(wait >= duration);
- const auto ms = wait.count();
- const auto maxInt = (std::numeric_limits<int>::max)();
-
- return ms < maxInt ? int(ms) : maxInt;
- }
-}
-#endif
-
class Q_CORE_EXPORT QBasicMutex
{
Q_DISABLE_COPY_MOVE(QBasicMutex)
@@ -104,26 +35,47 @@ public:
// BasicLockable concept
inline void lock() QT_MUTEX_LOCK_NOEXCEPT {
+ QtTsan::mutexPreLock(this, 0u);
+
if (!fastTryLock())
lockInternal();
+
+ QtTsan::mutexPostLock(this, 0u, 0);
}
// BasicLockable concept
inline void unlock() noexcept {
Q_ASSERT(d_ptr.loadRelaxed()); //mutex must be locked
+
+ QtTsan::mutexPreUnlock(this, 0u);
+
if (!fastTryUnlock())
unlockInternal();
+
+ QtTsan::mutexPostUnlock(this, 0u);
}
bool tryLock() noexcept {
- return fastTryLock();
+ unsigned tsanFlags = QtTsan::TryLock;
+ QtTsan::mutexPreLock(this, tsanFlags);
+
+ const bool success = fastTryLock();
+
+ if (!success)
+ tsanFlags |= QtTsan::TryLockFailed;
+ QtTsan::mutexPostLock(this, tsanFlags, 0);
+
+ return success;
}
// Lockable concept
bool try_lock() noexcept { return tryLock(); }
private:
- inline bool fastTryLock() noexcept {
+ inline bool fastTryLock() noexcept
+ {
+ if (d_ptr.loadRelaxed() != nullptr)
+ return false;
return d_ptr.testAndSetAcquire(nullptr, dummyLocked());
}
inline bool fastTryUnlock() noexcept {
@@ -131,7 +83,10 @@ private:
}
void lockInternal() QT_MUTEX_LOCK_NOEXCEPT;
+ bool lockInternal(QDeadlineTimer timeout) QT_MUTEX_LOCK_NOEXCEPT;
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
bool lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT;
+#endif
void unlockInternal() noexcept;
void destroyInternal(QMutexPrivate *d);
@@ -168,26 +123,42 @@ public:
using QBasicMutex::tryLock;
bool tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
{
- if (fastTryLock())
- return true;
- return lockInternal(timeout);
+ return tryLock(QDeadlineTimer(timeout));
+ }
+
+ bool tryLock(QDeadlineTimer timeout) QT_MUTEX_LOCK_NOEXCEPT
+ {
+ unsigned tsanFlags = QtTsan::TryLock;
+ QtTsan::mutexPreLock(this, tsanFlags);
+
+ bool success = fastTryLock();
+
+ if (success) {
+ QtTsan::mutexPostLock(this, tsanFlags, 0);
+ return success;
+ }
+
+ success = lockInternal(timeout);
+
+ if (!success)
+ tsanFlags |= QtTsan::TryLockFailed;
+ QtTsan::mutexPostLock(this, tsanFlags, 0);
+
+ return success;
}
// TimedLockable concept
template <class Rep, class Period>
bool try_lock_for(std::chrono::duration<Rep, Period> duration)
{
- return tryLock(QtPrivate::convertToMilliseconds(duration));
+ return tryLock(QDeadlineTimer(duration));
}
// TimedLockable concept
template<class Clock, class Duration>
bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
{
- // Implemented in terms of try_lock_for to honor the similar
- // requirement in N4606 § 30.4.1.3 [thread.timedmutex.requirements]/12.
-
- return try_lock_for(timePoint - Clock::now());
+ return tryLock(QDeadlineTimer(timePoint));
}
};
@@ -208,8 +179,10 @@ public:
// BasicLockable concept
void lock() QT_MUTEX_LOCK_NOEXCEPT
- { tryLock(-1); }
- bool tryLock(int timeout = 0) QT_MUTEX_LOCK_NOEXCEPT;
+ { tryLock(QDeadlineTimer(QDeadlineTimer::Forever)); }
+ QT_CORE_INLINE_SINCE(6, 6)
+ bool tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT;
+ bool tryLock(QDeadlineTimer timer = {}) QT_MUTEX_LOCK_NOEXCEPT;
// BasicLockable concept
void unlock() noexcept;
@@ -220,66 +193,89 @@ public:
template <class Rep, class Period>
bool try_lock_for(std::chrono::duration<Rep, Period> duration)
{
- return tryLock(QtPrivate::convertToMilliseconds(duration));
+ return tryLock(QDeadlineTimer(duration));
}
// TimedLockable concept
template<class Clock, class Duration>
bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
{
- // Implemented in terms of try_lock_for to honor the similar
- // requirement in N4606 § 30.4.1.3 [thread.timedmutex.requirements]/12.
-
- return try_lock_for(timePoint - Clock::now());
+ return tryLock(QDeadlineTimer(timePoint));
}
};
+#if QT_CORE_INLINE_IMPL_SINCE(6, 6)
+bool QRecursiveMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
+{
+ return tryLock(QDeadlineTimer(timeout));
+}
+#endif
+
template <typename Mutex>
-class [[nodiscard]] QMutexLocker
+class QMutexLocker
{
public:
+ Q_NODISCARD_CTOR
inline explicit QMutexLocker(Mutex *mutex) QT_MUTEX_LOCK_NOEXCEPT
{
- m = mutex;
+ m_mutex = mutex;
if (Q_LIKELY(mutex)) {
mutex->lock();
- isLocked = true;
+ m_isLocked = true;
}
}
- inline ~QMutexLocker() {
- unlock();
+
+ Q_NODISCARD_CTOR
+ inline QMutexLocker(QMutexLocker &&other) noexcept
+ : m_mutex(std::exchange(other.m_mutex, nullptr)),
+ m_isLocked(std::exchange(other.m_isLocked, false))
+ {}
+
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QMutexLocker)
+
+ inline ~QMutexLocker()
+ {
+ if (m_isLocked)
+ unlock();
+ }
+
+ inline bool isLocked() const noexcept
+ {
+ return m_isLocked;
}
inline void unlock() noexcept
{
- if (!isLocked)
- return;
- m->unlock();
- isLocked = false;
+ Q_ASSERT(m_isLocked);
+ m_mutex->unlock();
+ m_isLocked = false;
}
inline void relock() QT_MUTEX_LOCK_NOEXCEPT
{
- if (isLocked)
- return;
- if (m) {
- m->lock();
- isLocked = true;
- }
+ Q_ASSERT(!m_isLocked);
+ m_mutex->lock();
+ m_isLocked = true;
+ }
+
+ inline void swap(QMutexLocker &other) noexcept
+ {
+ qt_ptr_swap(m_mutex, other.m_mutex);
+ std::swap(m_isLocked, other.m_isLocked);
}
Mutex *mutex() const
{
- return m;
+ return m_mutex;
}
private:
Q_DISABLE_COPY(QMutexLocker)
- Mutex *m;
- bool isLocked = false;
+ Mutex *m_mutex;
+ bool m_isLocked = false;
};
-#else // !QT_CONFIG(thread) && !Q_CLANG_QDOC
+#else // !QT_CONFIG(thread) && !Q_QDOC
class QMutex
{
@@ -313,9 +309,10 @@ private:
class QRecursiveMutex : public QMutex {};
template <typename Mutex>
-class [[nodiscard]] QMutexLocker
+class QMutexLocker
{
public:
+ Q_NODISCARD_CTOR
inline explicit QMutexLocker(Mutex *) noexcept {}
inline ~QMutexLocker() noexcept {}
@@ -329,7 +326,7 @@ private:
typedef QMutex QBasicMutex;
-#endif // !QT_CONFIG(thread) && !Q_CLANG_QDOC
+#endif // !QT_CONFIG(thread) && !Q_QDOC
QT_END_NAMESPACE
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 b7d0bdab9b..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,26 +23,27 @@
#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_UNIX)
-# if _POSIX_VERSION-0 >= 200112L || _XOPEN_VERSION-0 >= 600
# include <semaphore.h>
-# define QT_UNIX_SEMAPHORE
-# endif
#endif
struct timespec;
QT_BEGIN_NAMESPACE
+// 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
@@ -118,27 +82,13 @@ 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
};
-
-#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
#endif // QMUTEX_P_H
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 cf863aeebc..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("QMutexPrivate::QMutexPrivate: 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 dfa45472ca..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)),
@@ -86,6 +51,7 @@ public:
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))
@@ -152,42 +118,15 @@ 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() {}
@@ -199,8 +138,6 @@ public:
static bool relock(QBasicMutex *, QBasicMutex *) { return false; }
};
-using QBasicMutexLocker = QMutexLocker<QBasicMutex>;
-
#endif
diff --git a/src/corelib/thread/qpromise.h b/src/corelib/thread/qpromise.h
index 3cecbd8306..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,6 +13,13 @@ 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
{
@@ -64,23 +35,34 @@ public:
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))
+ 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, typename = QtPrivate::EnableIfSameOrConvertible<U, T>>
+ 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)
+ {
+ 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.reportResult(std::forward<U>(result), index);
+ 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)
@@ -106,10 +88,10 @@ 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
+#if defined(Q_QDOC) // documentation-only simplified signatures
bool addResult(const T &result, int index = -1) { }
bool addResult(T &&result, int index = -1) { }
#endif
diff --git a/src/corelib/thread/qpromise.qdoc b/src/corelib/thread/qpromise.qdoc
index f9040d4cd1..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
*/
@@ -83,7 +61,9 @@
\internal
Constructs a QPromise with a passed QFutureInterface \a other.
- Used internally for QtConcurrent::runWithPromise.
+ 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=()
*/
@@ -111,15 +91,38 @@
/*! \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)
- 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.
+ 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()).
- Returns \c true when \a result is added to 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
- \a result is rejected. addResult() rejects \a result if there's already
+ 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().
\note It is possible to specify an arbitrary index and request result at
@@ -127,6 +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> bool QPromise<T>::addResults(const QList<T> &results)
+ \since 6.6
+
+ 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)
diff --git a/src/corelib/thread/qreadwritelock.cpp b/src/corelib/thread/qreadwritelock.cpp
index 4c2a9c4251..e79bed2231 100644
--- a/src/corelib/thread/qreadwritelock.cpp
+++ b/src/corelib/thread/qreadwritelock.cpp
@@ -1,52 +1,13 @@
-/****************************************************************************
-**
-** 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"
@@ -66,21 +27,22 @@ QT_BEGIN_NAMESPACE
* - In any other case, d_ptr points to an actual QReadWriteLockPrivate.
*/
+using namespace QReadWriteLockStates;
namespace {
-using ms = std::chrono::milliseconds;
+using steady_clock = std::chrono::steady_clock;
-enum {
- StateMask = 0x3,
- StateLockedForRead = 0x1,
- StateLockedForWrite = 0x2,
-};
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.
@@ -138,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.
@@ -146,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;
@@ -169,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.
@@ -177,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.
@@ -200,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.
@@ -224,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))
@@ -249,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.
@@ -284,6 +246,7 @@ bool QReadWriteLock::tryLockForRead(int 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
@@ -294,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.
@@ -314,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.
@@ -338,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))
@@ -353,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.
@@ -447,42 +410,16 @@ void QReadWriteLock::unlock()
}
}
-/*! \internal Helper for QWaitCondition::wait */
-QReadWriteLock::StateForWaitCondition QReadWriteLock::stateForWaitCondition() const
-{
- 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(std::unique_lock<QtPrivate::mutex> &lock, int timeout)
+bool QReadWriteLockPrivate::lockForRead(std::unique_lock<std::mutex> &lock, QDeadlineTimer timeout)
{
Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function
- QElapsedTimer t;
- if (timeout > 0)
- t.start();
-
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_for(lock, ms{timeout - elapsed});
+ readerCond.wait_until(lock, timeout.deadline<steady_clock>());
} else {
waitingReaders++;
readerCond.wait(lock);
@@ -494,29 +431,22 @@ bool QReadWriteLockPrivate::lockForRead(std::unique_lock<QtPrivate::mutex> &lock
return true;
}
-bool QReadWriteLockPrivate::lockForWrite(std::unique_lock<QtPrivate::mutex> &lock, int timeout)
+bool QReadWriteLockPrivate::lockForWrite(std::unique_lock<std::mutex> &lock, QDeadlineTimer timeout)
{
Q_ASSERT(!mutex.try_lock()); // mutex must be locked when entering this function
- QElapsedTimer t;
- if (timeout > 0)
- t.start();
-
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 queued (probably because of us). Wake the waiting readers.
- readerCond.notify_all();
- }
- 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_for(lock, ms{timeout - elapsed});
+ writerCond.wait_until(lock, timeout.deadline<steady_clock>());
} else {
waitingWriters++;
writerCond.wait(lock);
@@ -544,7 +474,7 @@ static auto handleEquals(Qt::HANDLE handle)
return [handle](QReadWriteLockPrivate::Reader reader) { return reader.handle == handle; };
}
-bool QReadWriteLockPrivate::recursiveLockForRead(int timeout)
+bool QReadWriteLockPrivate::recursiveLockForRead(QDeadlineTimer timeout)
{
Q_ASSERT(recursive);
auto lock = qt_unique_lock(mutex);
@@ -566,7 +496,7 @@ bool QReadWriteLockPrivate::recursiveLockForRead(int timeout)
return true;
}
-bool QReadWriteLockPrivate::recursiveLockForWrite(int timeout)
+bool QReadWriteLockPrivate::recursiveLockForWrite(QDeadlineTimer timeout)
{
Q_ASSERT(recursive);
auto lock = qt_unique_lock(mutex);
@@ -615,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);
@@ -644,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 f9725f05af..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/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) {}
- QtPrivate::mutex mutex;
- QtPrivate::condition_variable writerCond;
- QtPrivate::condition_variable 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(std::unique_lock<QtPrivate::mutex> &lock, int timeout);
- bool lockForRead(std::unique_lock<QtPrivate::mutex> &lock, 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,7 +65,7 @@ public:
void release();
static QReadWriteLockPrivate *allocate();
- // Recusive mutex handling
+ // Recursive mutex handling
Qt::HANDLE currentWriter = {};
struct Reader {
@@ -96,11 +76,36 @@ public:
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);
+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 5982ae5454..14ed7c6b87 100644
--- a/src/corelib/thread/qresultstore.cpp
+++ b/src/corelib/thread/qresultstore.cpp
@@ -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
#include "qresultstore.h"
diff --git a/src/corelib/thread/qresultstore.h b/src/corelib/thread/qresultstore.h
index a2a14f3de2..30ce1fe904 100644
--- a/src/corelib/thread/qresultstore.h
+++ b/src/corelib/thread/qresultstore.h
@@ -1,47 +1,10 @@
-/****************************************************************************
-**
-** 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 QTCORE_RESULTSTORE_H
#define QTCORE_RESULTSTORE_H
#include <QtCore/qmap.h>
-#include <QtCore/qdebug.h>
#include <utility>
@@ -108,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);
}
@@ -122,7 +85,7 @@ public:
}
};
-class Q_CORE_EXPORT ResultStoreBase
+class Q_CORE_EXPORT ResultStoreBase final
{
public:
ResultStoreBase();
@@ -136,7 +99,8 @@ public:
ResultIteratorBase resultAt(int index) const;
bool contains(int index) const;
int count() const;
- // ### Qt 7: 'virtual' isn't required, can be removed
+ // ### 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:
@@ -170,6 +134,14 @@ protected:
}
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)
{
@@ -185,10 +157,9 @@ public:
template <typename T>
int moveResult(int index, T &&result)
{
- if (containsValidResultItem(index)) // reject if already present
- return -1;
+ static_assert(!std::is_reference_v<T>, "trying to move from an lvalue!");
- return addResult(index, static_cast<void *>(new T(std::move_if_noexcept(result))));
+ return emplaceResult<std::remove_cv_t<T>>(index, std::forward<T>(result));
}
template<typename T>
@@ -200,23 +171,23 @@ public:
if (containsValidResultItem(index)) // reject if already present
return -1;
- return addResults(index, new QList<T>(*results), results->count(), results->count());
+ return addResults(index, new QList<T>(*results), results->size(), results->size());
}
template<typename T>
int addResults(int index, const QList<T> *results, int totalCount)
{
// reject if results are empty, and nothing is filtered away
- if ((m_filterMode == false || results->count() == totalCount) && results->empty())
+ 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->count() != totalCount && 0 == results->count())
+ 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)
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 22d508d35c..f0dd0a582e 100644
--- a/src/corelib/thread/qrunnable.h
+++ b/src/corelib/thread/qrunnable.h
@@ -1,47 +1,16 @@
-/****************************************************************************
-**
-** 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
@@ -55,12 +24,114 @@ public:
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 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 0f1c7c2eb8..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}
*/
/*
@@ -178,10 +146,10 @@ 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
@@ -194,12 +162,12 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
if constexpr (futexHasWaiterCount) {
Q_ASSERT(n > 1);
ptr = futexHigh32(&u);
- curValue >>= 32;
+ 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 {
@@ -207,8 +175,6 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
}
curValue = u.loadAcquire();
- if (IsTimed)
- remainingTime = timer.remainingTimeNSecs();
// try to acquire
while (futexAvailCounter(curValue) >= n) {
@@ -218,13 +184,18 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
}
// not enough tokens available, put us to wait
- if (remainingTime == 0)
+ 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);
@@ -238,15 +209,21 @@ 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
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 (((curValue >> 32) & 0x7fffffffU) == 0x7fffffffU) {
+ quint32 waiterCount = (quint64(curValue) >> 32) & 0x7fffffffU;
+ if (waiterCount == 0x7fffffffU) {
qCritical() << "Waiter count overflow in QSemaphore";
return false;
}
@@ -272,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;
+};
- QMutex mutex;
- QWaitCondition cond;
+struct Layout2
+{
+ alignas(IdealMutexAlignment) std::mutex mutex;
+ alignas(IdealMutexAlignment) std::condition_variable cond;
+ qsizetype avail = 0;
+};
+
+// 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; }
};
/*!
@@ -322,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;
}
@@ -356,7 +356,12 @@ 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) {
@@ -378,7 +383,6 @@ void QSemaphore::release(int n)
quint32 oparg = 0;
quint32 cmp = FUTEX_OP_CMP_NE;
quint32 cmparg = 0;
- u.fetchAndAndRelease(futexNeedsWakeAllBit - 1);
futexWakeOp(*futexLow32(&u), n, INT_MAX, *futexHigh32(&u), FUTEX_OP(op, oparg, cmp, cmparg));
return;
}
@@ -390,20 +394,18 @@ void QSemaphore::release(int n)
// 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);
- if (futexHasWaiterCount) {
- futexWakeAll(*futexLow32(&u));
+ futexWakeAll(*futexLow32(&u));
+ if (futexHasWaiterCount)
futexWakeAll(*futexHigh32(&u));
- } else {
- futexWakeAll(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();
}
/*!
@@ -417,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;
}
@@ -437,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;
@@ -447,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
@@ -462,24 +466,40 @@ 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;
+ }
- // We're documented to accept any negative value as "forever"
- // but QDeadlineTimer only accepts -1.
- timeout = qMax(timeout, -1);
+ if (timer.hasExpired())
+ return tryAcquire(n);
+
+ 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;
@@ -511,7 +531,7 @@ bool QSemaphore::tryAcquire(int n, int timeout)
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 accquiring the resource successfully.
+ on acquiring the resource successfully.
\sa tryAcquire(), try_acquire(), try_acquire_until()
*/
diff --git a/src/corelib/thread/qsemaphore.h b/src/corelib/thread/qsemaphore.h
index b57a274104..dda722a582 100644
--- a/src/corelib/thread/qsemaphore.h
+++ b/src/corelib/thread/qsemaphore.h
@@ -1,47 +1,11 @@
-/****************************************************************************
-**
-** 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/qmutex.h> // for convertToMilliseconds()
+#include <QtCore/qdeadlinetimer.h>
#include <chrono>
@@ -50,7 +14,6 @@ QT_REQUIRE_CONFIG(thread);
QT_BEGIN_NAMESPACE
class QSemaphorePrivate;
-
class Q_CORE_EXPORT QSemaphore
{
public:
@@ -59,10 +22,14 @@ 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, QtPrivate::convertToMilliseconds(timeout)); }
+ { return tryAcquire(n, QDeadlineTimer(timeout)); }
+#endif
void release(int n = 1);
@@ -89,14 +56,25 @@ private:
};
};
+#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) {}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QSemaphoreReleaser)
@@ -109,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
@@ -118,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 4cfcab2258..0ad402b77c 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
*/
@@ -78,8 +66,9 @@ QThreadData::~QThreadData()
// 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();
+ QCoreApplicationPrivate::theMainThread.storeRelease(nullptr);
+ QCoreApplicationPrivate::theMainThreadId.storeRelaxed(nullptr);
+ QThreadData::clearCurrentThreadData();
}
// ~QThread() sets thread to nullptr, so if it isn't null here, it's
@@ -141,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);
}
@@ -157,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
*/
@@ -175,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;
@@ -195,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();
}
@@ -263,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
@@ -291,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}
*/
/*!
@@ -416,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.
@@ -537,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().
@@ -552,7 +598,10 @@ uint QThread::stackSize() const
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;
@@ -569,6 +618,58 @@ 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.
@@ -685,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()
*/
@@ -702,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
@@ -718,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
@@ -820,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)
@@ -892,6 +1033,11 @@ QThread *QThread::currentThread()
return QThreadData::current()->thread.loadAcquire();
}
+bool QThread::isCurrentThread() const
+{
+ return true;
+}
+
int QThread::idealThreadCount() noexcept
{
return 1;
@@ -913,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 = nullptr;
+Q_CONSTINIT static QThreadData *data = nullptr;
QThreadData *QThreadData::current(bool createIfNecessary)
{
@@ -924,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::theMainThread.loadAcquire()) {
QCoreApplicationPrivate::theMainThread.storeRelease(data->thread.loadRelaxed());
+ QCoreApplicationPrivate::theMainThreadId.storeRelaxed(data->threadId.loadRelaxed());
+ }
}
return data;
}
@@ -986,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)
@@ -1112,7 +1277,6 @@ bool QThread::isInterruptionRequested() const
\sa start()
*/
-#if QT_CONFIG(cxx11_future)
class QThreadCreateThread : public QThread
{
public:
@@ -1141,7 +1305,6 @@ QThread *QThread::createThreadImpl(std::future<void> &&future)
{
return new QThreadCreateThread(std::move(future));
}
-#endif // QT_CONFIG(cxx11_future)
/*!
\class QDaemonThread
@@ -1156,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 441736cf4e..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,10 +9,8 @@
#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
-#endif
+#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>
@@ -60,6 +22,7 @@ QT_BEGIN_NAMESPACE
class QThreadData;
class QThreadPrivate;
class QAbstractEventDispatcher;
+class QEventLoopLocker;
class Q_CORE_EXPORT QThread : public QObject
{
@@ -67,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();
@@ -105,10 +69,10 @@ public:
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>
[[nodiscard]] static QThread *create(Function &&f, Args &&... args);
-#endif
public Q_SLOTS:
void start(Priority = InheritPriority);
@@ -128,6 +92,7 @@ public:
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);
@@ -144,17 +109,15 @@ protected:
private:
Q_DECLARE_PRIVATE(QThread)
+ friend class QEventLoopLocker;
-#if QT_CONFIG(cxx11_future)
[[nodiscard]] static QThread *createThreadImpl(std::future<void> &&future);
-#endif
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)
{
@@ -169,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
@@ -191,14 +153,14 @@ inline Qt::HANDLE QThread::currentThreadId() noexcept
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) && !defined(Q_OS_ANDROID) // 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)) && !defined(Q_OS_ANDROID)
+ __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
diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h
index aa6183f7e2..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
@@ -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,31 +66,15 @@ 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
@@ -133,6 +82,85 @@ private:
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
@@ -142,7 +170,7 @@ public:
~QDaemonThread();
};
-class QThreadPrivate : public QObjectPrivate
+class Q_AUTOTEST_EXPORT QThreadPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QThread)
@@ -151,6 +179,7 @@ public:
~QThreadPrivate();
void setPriority(QThread::Priority prio);
+ Qt::HANDLE threadId() const;
mutable QMutex mutex;
QAtomicInt quitLockRef;
@@ -202,6 +231,19 @@ public:
}
}
+ 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,
@@ -220,8 +262,13 @@ public:
mutable QMutex mutex;
QThreadData *data;
+ QBindingStatus* m_bindingStatus;
bool running = false;
+ QBindingStatus* bindingStatus() { return m_bindingStatus; }
+ QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *) { return nullptr; }
+ void removeObjectWithPendingBindingStatusChange(QObject *) {}
+
static void setCurrentThread(QThread *) { }
static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data);
@@ -264,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;
@@ -297,7 +324,6 @@ public:
QAtomicPointer<void> threadId;
QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;
QList<void *> tls;
- FlaggedDebugSignatures flaggedSignatures;
bool quitNow;
bool canWait;
@@ -309,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 32cada5e9d..8916da09b2 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,6 +8,7 @@
#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>
@@ -55,7 +20,9 @@
# endif
#endif
-#include <private/qeventdispatcher_unix_p.h>
+#if !defined(Q_OS_WASM)
+# include <private/qeventdispatcher_unix_p.h>
+#endif
#include "qthreadstorage.h"
@@ -106,6 +73,8 @@
QT_BEGIN_NAMESPACE
+using namespace QtMiscUtils;
+
#if QT_CONFIG(thread)
static_assert(sizeof(pthread_t) <= sizeof(Qt::HANDLE));
@@ -113,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)
{
@@ -173,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);
}
@@ -223,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::theMainThread.loadAcquire()) {
QCoreApplicationPrivate::theMainThread.storeRelease(data->thread.loadRelaxed());
+ QCoreApplicationPrivate::theMainThreadId.storeRelaxed(data->threadId.loadRelaxed());
+ }
}
return data;
}
@@ -271,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);
@@ -309,7 +279,7 @@ void terminate_on_exception(T &&t)
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);
@@ -338,7 +308,7 @@ 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
@@ -351,7 +321,7 @@ void *QThreadPrivate::start(void *arg)
#endif
emit thr->started(QThread::QPrivateSignal());
-#if !defined(Q_OS_ANDROID)
+#ifdef PTHREAD_CANCEL_DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr);
pthread_testcancel();
#endif
@@ -379,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();
@@ -449,7 +420,7 @@ int QThread::idealThreadCount() noexcept
cores = (int)psd.psd_proc_cnt;
}
#elif (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD)
-# ifdef 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;
@@ -525,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)
@@ -735,7 +717,9 @@ void QThread::start(Priority priority)
else
pthread_attr_setthreadname(&attr, objectName().toLocal8Bit());
#else
- d->objectName = objectName();
+ // avoid interacting with the binding system
+ d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
+ : QString();
#endif
pthread_t threadId;
diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp
index 6249b94ea2..475a61bf65 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;
@@ -114,6 +89,7 @@ QThreadData *QThreadData::current(bool createIfNecessary)
if (!QCoreApplicationPrivate::theMainThread) {
QCoreApplicationPrivate::theMainThread = 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
*************************************************************************/
@@ -314,12 +257,11 @@ 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.
- qt_set_thread_name(HANDLE(-1), thr->d_func()->objectName.isEmpty()
- ? thr->metaObject()->className()
- : std::exchange(thr->d_func()->objectName, {}).toLocal8Bit().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,7 +384,9 @@ void QThread::start(Priority priority)
if (d->running)
return;
- d->objectName = objectName();
+ // avoid interacting with the binding system
+ d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
+ : QString();
d->running = true;
d->finished = false;
d->exited = false;
diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp
index 94b0aff451..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
*/
@@ -120,7 +89,7 @@ void QThreadPoolThread::run()
if (manager->queue.isEmpty())
break;
- QueuePage *page = manager->queue.first();
+ QueuePage *page = manager->queue.constFirst();
r = page->pop();
if (page->isFinished()) {
@@ -221,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;
@@ -233,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);
}
@@ -243,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;
@@ -274,16 +243,16 @@ bool QThreadPoolPrivate::tooManyThreadsActive() const
void QThreadPoolPrivate::startThread(QRunnable *runnable)
{
Q_ASSERT(runnable != nullptr);
- QScopedPointer<QThreadPoolThread> thread(new QThreadPoolThread(this));
+ auto thread = std::make_unique<QThreadPoolThread>(this);
if (objectName.isEmpty())
- objectName = QLatin1String("Thread (pooled)");
+ objectName = u"Thread (pooled)"_s;
thread->setObjectName(objectName);
- 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());
+ 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(threadPriority);
+ thread.release()->start(threadPriority);
}
/*!
@@ -302,7 +271,7 @@ void QThreadPoolPrivate::reset()
mutex.unlock();
- for (QThreadPoolThread *thread : qAsConst(allThreadsCopy)) {
+ for (QThreadPoolThread *thread : std::as_const(allThreadsCopy)) {
if (thread->isRunning()) {
thread->runnableReady.wakeAll();
thread->wait();
@@ -381,7 +350,7 @@ 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);
@@ -498,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())
@@ -508,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
@@ -535,20 +519,21 @@ void QThreadPool::start(QRunnable *runnable, int priority)
}
/*!
+ \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.
@@ -580,30 +565,21 @@ bool QThreadPool::tryStart(QRunnable *runnable)
}
/*!
+ \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->areAllThreadsActive())
- 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.
@@ -623,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;
@@ -649,6 +627,7 @@ void QThreadPool::setExpiryTimeout(int expiryTimeout)
int QThreadPool::maxThreadCount() const
{
Q_D(const QThreadPool);
+ QMutexLocker locker(&d->mutex);
return d->requestedMaxThreadCount;
}
@@ -718,12 +697,14 @@ 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;
}
@@ -744,12 +725,14 @@ uint QThreadPool::stackSize() const
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;
}
@@ -810,19 +793,19 @@ void QThreadPool::startOnReservedThread(QRunnable *runnable)
}
/*!
+ \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 functionToRun.
-*/
-void QThreadPool::startOnReservedThread(std::function<void()> functionToRun)
-{
- if (!functionToRun)
- return releaseThread();
+ to run \a callableToRun.
- startOnReservedThread(QRunnable::create(std::move(functionToRun)));
-}
+ \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
diff --git a/src/corelib/thread/qthreadpool.h b/src/corelib/thread/qthreadpool.h
index 853055cb30..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
@@ -72,11 +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);
@@ -104,6 +79,28 @@ public:
[[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 e82e4f16e7..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
@@ -170,6 +134,8 @@ public:
void stealAndRunRunnable(QRunnable *runnable);
void deletePageIfFinished(QueuePage *page);
+ static QThreadPool *qtGuiInstance();
+
mutable QMutex mutex;
QSet<QThreadPoolThread *> allThreads;
QQueue<QThreadPoolThread *> waitingThreads;
diff --git a/src/corelib/thread/qthreadstorage.cpp b/src/corelib/thread/qthreadstorage.cpp
index ee8273687b..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"
@@ -68,7 +32,7 @@ void qtsDebug(const char *fmt, ...)
# 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;
diff --git a/src/corelib/thread/qthreadstorage.h b/src/corelib/thread/qthreadstorage.h
index a7e93f03a5..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
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 3d0117ad03..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
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 26a7e03ba7..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,105 +17,21 @@
#include <QtCore/QWaitCondition>
#include <QtCore/QMutex>
#include <QtCore/QDeadlineTimer>
+#include <QtCore/private/qglobal_p.h>
#include <condition_variable>
#include <mutex>
-// There's no feature macro for C++11 std::mutex, so we use the C++14 one
-// for shared_mutex to detect it.
-// Needed for: MinGW without gthreads, Integrity
-#if __has_include(<shared_mutex>)
-# include <shared_mutex>
-#endif
-
QT_BEGIN_NAMESPACE
namespace QtPrivate {
-
-#if !defined(__cpp_lib_shared_timed_mutex)
-
-enum class cv_status { no_timeout, timeout };
-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>
- cv_status wait_for(std::unique_lock<QtPrivate::mutex> &lock,
- const std::chrono::duration<Rep, Period> &d)
- {
- return QWaitCondition::wait(lock.mutex(), QDeadlineTimer{d})
- ? cv_status::no_timeout
- : 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>
- 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})
- ? cv_status::no_timeout
- : 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 // C++11 threads
-
-using mutex = std::mutex;
-using condition_variable = std::condition_variable;
-
-#endif // C++11 threads
+// 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 01848a5004..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,60 +20,61 @@
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);
-#endif
+ auto destroy = qScopeGuard([&] { pthread_condattr_destroy(&condattr); });
+ if (SteadyClockClockId != CLOCK_REALTIME)
+ pthread_condattr_setclock(&condattr, SteadyClockClockId);
#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
@@ -125,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);
}
@@ -147,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;
@@ -161,10 +114,11 @@ 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);
}
@@ -173,32 +127,38 @@ public:
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)
@@ -213,7 +173,7 @@ bool QWaitCondition::wait(QMutex *mutex, QDeadlineTimer deadline)
if (!mutex)
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();
@@ -233,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 1529cda089..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"
@@ -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);
};
@@ -104,19 +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)
@@ -160,13 +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;
QWaitConditionEvent *wce = d->pre();
mutex->unlock();
- bool returnValue = d->wait(wce, time);
+ bool returnValue = d->wait(wce, deadline);
mutex->lock();
d->post(wce, returnValue);
@@ -174,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;
}
@@ -194,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();
@@ -205,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);
@@ -227,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.
+*/