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.cpp367
1 files changed, 285 insertions, 82 deletions
diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp
index 3453fd0fc1..ea76a2ccad 100644
--- a/src/corelib/thread/qthread.cpp
+++ b/src/corelib/thread/qthread.cpp
@@ -1,48 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qthread.h"
#include "qthreadstorage.h"
#include "qmutex.h"
#include "qreadwritelock.h"
#include "qabstracteventdispatcher.h"
+#include "qbindingstorage.h"
#include <qeventloop.h>
@@ -54,6 +19,29 @@
QT_BEGIN_NAMESPACE
/*
+ QPostEventList
+*/
+
+void QPostEventList::addEvent(const QPostEvent &ev)
+{
+ int priority = ev.priority;
+ if (isEmpty() ||
+ constLast().priority >= priority ||
+ insertionOffset >= size()) {
+ // optimization: we can simply append if the last event in
+ // the queue has higher or equal priority
+ append(ev);
+ } else {
+ // insert event in descending priority order, using upper
+ // bound for a given priority (to ensure proper ordering
+ // of events with the same priority)
+ QPostEventList::iterator at = std::upper_bound(begin() + insertionOffset, end(), ev);
+ insert(at, ev);
+ }
+}
+
+
+/*
QThreadData
*/
@@ -77,9 +65,10 @@ QThreadData::~QThreadData()
// crashing during QCoreApplicationData's global static cleanup we need to
// safeguard the main thread here.. This fix is a bit crude, but it solves
// the problem...
- if (this->thread.loadAcquire() == QCoreApplicationPrivate::theMainThread.loadAcquire()) {
- QCoreApplicationPrivate::theMainThread.storeRelease(nullptr);
- QThreadData::clearCurrentThreadData();
+ if (threadId.loadAcquire() == QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
+ QCoreApplicationPrivate::theMainThread.storeRelease(nullptr);
+ QCoreApplicationPrivate::theMainThreadId.storeRelaxed(nullptr);
+ QThreadData::clearCurrentThreadData();
}
// ~QThread() sets thread to nullptr, so if it isn't null here, it's
@@ -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();
}
@@ -253,7 +264,7 @@ QThreadPrivate::~QThreadPrivate()
\section1 Managing Threads
- QThread will notifiy you via a signal when the thread is
+ QThread will notify you via a signal when the thread is
started() and finished(), or you can use isFinished() and
isRunning() to query the state of the thread.
@@ -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}
*/
/*!
@@ -314,9 +325,20 @@ QThreadPrivate::~QThreadPrivate()
/*!
\fn int QThread::idealThreadCount()
- Returns the ideal number of threads that can be run on the system. This is done querying
- the number of processor cores, both real and logical, in the system. This function returns 1
- if the number of processor cores could not be detected.
+ Returns the ideal number of threads that this process can run in parallel.
+ This is done by querying the number of logical processors available to this
+ process (if supported by this OS) or the total number of logical processors
+ in the system. This function returns 1 if neither value could be
+ determined.
+
+ \note On operating systems that support setting a thread's affinity to a
+ subset of all logical processors, the value returned by this function may
+ change between threads and over time.
+
+ \note On operating systems that support CPU hotplugging and hot-unplugging,
+ the value returned by this function may also change over time (and note
+ that CPUs can be turned on and off by software, without a physical,
+ hardware change).
*/
/*!
@@ -405,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.
@@ -438,6 +477,15 @@ QThread::QThread(QThreadPrivate &dd, QObject *parent)
isFinished() returns \c false) will result in a program
crash. Wait for the finished() signal before deleting the
QThread.
+
+ Since Qt 6.3, it is allowed to delete a QThread instance created by
+ a call to QThread::create() even if the corresponding thread is
+ still running. In such a case, Qt will post an interruption request
+ to that thread (via requestInterruption()); will ask the thread's
+ event loop (if any) to quit (via quit()); and will block until the
+ thread has finished.
+
+ \sa create(), isInterruptionRequested(), exec(), quit()
*/
QThread::~QThread()
{
@@ -483,10 +531,18 @@ bool QThread::isRunning() const
}
/*!
- Sets the maximum stack size for the thread to \a stackSize. If \a
- stackSize is greater than zero, the maximum stack size is set to
- \a stackSize bytes, otherwise the maximum stack size is
- automatically determined by the operating system.
+ Sets the stack size for the thread to \a stackSize. If \a stackSize is
+ zero, the operating system or runtime will choose a default value.
+ Otherwise, the thread's stack size will be the value provided (which may be
+ rounded up or down).
+
+ On most operating systems, the amount of memory allocated to serve the
+ stack will initially be smaller than \a stackSize and will grow as the
+ thread uses the stack. This parameter sets the maximum size it will be
+ allowed to grow to (that is, it sets the size of the virtual memory space
+ the stack is allowed to occupy).
+
+ This function can only be called before the thread is started.
\warning Most operating systems place minimum and maximum limits
on thread stack sizes. The thread will fail to start if the stack
@@ -517,6 +573,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 +606,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 +626,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 +794,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 +823,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 +838,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
@@ -800,6 +939,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)
@@ -872,6 +1041,11 @@ QThread *QThread::currentThread()
return QThreadData::current()->thread.loadAcquire();
}
+bool QThread::isCurrentThread() const
+{
+ return true;
+}
+
int QThread::idealThreadCount() noexcept
{
return 1;
@@ -893,8 +1067,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)
{
@@ -904,8 +1092,10 @@ QThreadData *QThreadData::current(bool createIfNecessary)
data->threadId.storeRelaxed(Qt::HANDLE(data->thread.loadAcquire()));
data->deref();
data->isAdopted = true;
- if (!QCoreApplicationPrivate::theMainThread.loadAcquire())
+ if (!QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
QCoreApplicationPrivate::theMainThread.storeRelease(data->thread.loadRelaxed());
+ QCoreApplicationPrivate::theMainThreadId.storeRelaxed(data->threadId.loadRelaxed());
+ }
}
return data;
}
@@ -966,8 +1156,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 +1179,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)
@@ -1019,11 +1215,11 @@ bool QThread::event(QEvent *event)
void QThread::requestInterruption()
{
- if (this == QCoreApplicationPrivate::theMainThread.loadAcquire()) {
+ Q_D(QThread);
+ if (d->threadId() == QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
qWarning("QThread::requestInterruption has no effect on the main thread");
return;
}
- Q_D(QThread);
QMutexLocker locker(&d->mutex);
if (!d->running || d->finished || d->isInFinish)
return;
@@ -1089,7 +1285,6 @@ bool QThread::isInterruptionRequested() const
\sa start()
*/
-#if QT_CONFIG(cxx11_future)
class QThreadCreateThread : public QThread
{
public:
@@ -1098,6 +1293,13 @@ public:
{
}
+ ~QThreadCreateThread()
+ {
+ requestInterruption();
+ quit();
+ wait();
+ }
+
private:
void run() override
{
@@ -1111,7 +1313,6 @@ QThread *QThread::createThreadImpl(std::future<void> &&future)
{
return new QThreadCreateThread(std::move(future));
}
-#endif // QT_CONFIG(cxx11_future)
/*!
\class QDaemonThread
@@ -1126,7 +1327,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()