summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread/qthread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/thread/qthread.cpp')
-rw-r--r--src/corelib/thread/qthread.cpp282
1 files changed, 211 insertions, 71 deletions
diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp
index 302d34d512..a9115c5b39 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
*/
@@ -141,8 +129,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 +146,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 +181,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 +201,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 +273,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 +300,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}
*/
/*!
@@ -314,9 +324,20 @@ QThreadPrivate::~QThreadPrivate()
/*!
\fn int QThread::idealThreadCount()
- Returns the ideal number of threads that can be run on the system. This is done querying
- the number of processor cores, both real and logical, in the system. This function returns 1
- if the number of processor cores could not be detected.
+ Returns the ideal number of threads that this process can run in parallel.
+ This is done by querying the number of logical processors available to this
+ process (if supported by this OS) or the total number of logical processors
+ in the system. This function returns 1 if neither value could be
+ determined.
+
+ \note On operating systems that support setting a thread's affinity to a
+ subset of all logical processors, the value returned by this function may
+ change between threads and over time.
+
+ \note On operating systems that support CPU hotplugging and hot-unplugging,
+ the value returned by this function may also change over time (and note
+ that CPUs can be turned on and off by software, without a physical,
+ hardware change).
*/
/*!
@@ -438,6 +459,15 @@ QThread::QThread(QThreadPrivate &dd, QObject *parent)
isFinished() returns \c false) will result in a program
crash. Wait for the finished() signal before deleting the
QThread.
+
+ Since Qt 6.3, it is allowed to delete a QThread instance created by
+ a call to QThread::create() even if the corresponding thread is
+ still running. In such a case, Qt will post an interruption request
+ to that thread (via requestInterruption()); will ask the thread's
+ event loop (if any) to quit (via quit()); and will block until the
+ thread has finished.
+
+ \sa create(), isInterruptionRequested(), exec(), quit()
*/
QThread::~QThread()
{
@@ -517,6 +547,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().
@@ -532,7 +580,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;
@@ -549,6 +600,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.
@@ -665,16 +768,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()
*/
@@ -682,11 +797,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
@@ -698,11 +812,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
@@ -893,8 +1006,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)
{
@@ -966,8 +1093,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)
@@ -986,7 +1116,10 @@ void QThread::setEventDispatcher(QAbstractEventDispatcher *eventDispatcher)
/*!
\fn bool QThread::wait(unsigned long time)
+
\overload
+ \a time is the time to wait in milliseconds.
+ If \a time is ULONG_MAX, then the wait will never timeout.
*/
#if QT_CONFIG(thread)
@@ -1089,7 +1222,6 @@ bool QThread::isInterruptionRequested() const
\sa start()
*/
-#if QT_CONFIG(cxx11_future)
class QThreadCreateThread : public QThread
{
public:
@@ -1098,6 +1230,13 @@ public:
{
}
+ ~QThreadCreateThread()
+ {
+ requestInterruption();
+ quit();
+ wait();
+ }
+
private:
void run() override
{
@@ -1111,7 +1250,6 @@ QThread *QThread::createThreadImpl(std::future<void> &&future)
{
return new QThreadCreateThread(std::move(future));
}
-#endif // QT_CONFIG(cxx11_future)
/*!
\class QDaemonThread
@@ -1126,7 +1264,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()