diff options
Diffstat (limited to 'src/corelib/thread/qthread.cpp')
-rw-r--r-- | src/corelib/thread/qthread.cpp | 282 |
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() |