qtbase/src/corelib/thread/qthread_unix.cpp

1085 lines
35 KiB
C++
Raw Normal View History

// 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
// Qt-Security score:significant reason:default
#include "qthread.h"
#include "qthread_p.h"
#include <private/qcoreapplication_p.h>
#include <private/qcore_unix_p.h>
#include "qdebug.h"
#include "qloggingcategory.h"
#include "qthreadstorage.h"
#include <private/qtools_p.h>
#if defined(Q_OS_WASM)
# include <private/qeventdispatcher_wasm_p.h>
#else
# include <private/qeventdispatcher_unix_p.h>
# if defined(Q_OS_DARWIN)
# include <private/qeventdispatcher_cf_p.h>
# elif !defined(QT_NO_GLIB)
# include <private/qeventdispatcher_glib_p.h>
# endif
#endif
#ifdef __GLIBCXX__
#include <cxxabi.h>
#endif
#include <sched.h>
#include <errno.h>
#if __has_include(<pthread_np.h>)
# include <pthread_np.h>
#endif
#if defined(Q_OS_FREEBSD)
# include <sys/cpuset.h>
#elif defined(Q_OS_BSD4)
# include <sys/sysctl.h>
#endif
#ifdef Q_OS_VXWORKS
# include <vxCpuLib.h>
# include <cpuset.h>
#endif
#ifdef Q_OS_HPUX
#include <sys/pstat.h>
#endif
#if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
#include <sys/prctl.h>
#endif
#if defined(Q_OS_LINUX) && !defined(SCHED_IDLE)
// from linux/sched.h
# define SCHED_IDLE 5
#endif
#if defined(Q_OS_DARWIN) || !defined(Q_OS_ANDROID) && !defined(Q_OS_OPENBSD) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0)
#define QT_HAS_THREAD_PRIORITY_SCHEDULING
#endif
#if defined(Q_OS_QNX)
#include <sys/neutrino.h>
#endif
QT_BEGIN_NAMESPACE
[[maybe_unused]]
Q_STATIC_LOGGING_CATEGORY(lcQThread, "qt.core.thread", QtWarningMsg)
using namespace QtMiscUtils;
#if QT_CONFIG(thread)
static_assert(sizeof(pthread_t) <= sizeof(Qt::HANDLE));
enum { ThreadPriorityResetFlag = 0x80000000 };
// If we have a way to perform a timed pthread_join(), we will do it if its
// clock is not worse than the one QWaitCondition is using. This ensures that
// QThread::wait() only returns after pthread_join() or equivalent has
// returned, ensuring that the thread has definitely exited.
//
// Because only one thread can call this family of functions at a time, we
// count how many threads are waiting and all but one of them wait on a
// QWaitCondition, with the joining thread having the responsibility for waking
// up all others when the joining concludes. If the joining times out, the
// thread in charge wakes up one of the other waiters (if there's any) to
// assume responsibility for joining.
//
// If we don't have a way to perform timed pthread_join(), then we don't try
// joining a all. All waiting threads will wait for the launched thread to
// call QWaitCondition::wakeAll(). Note in this case it is possible for the
// waiting threads to conclude the launched thread has exited before it has.
//
// To support this scenario, we start the thread in detached state.
static constexpr bool UsingPThreadTimedJoin = QT_CONFIG(pthread_clockjoin)
|| (QT_CONFIG(pthread_timedjoin) && QWaitConditionClockId == CLOCK_REALTIME);
#if !QT_CONFIG(pthread_clockjoin)
int pthread_clockjoin_np(...) { return ENOSYS; } // pretend
#endif
#if !QT_CONFIG(pthread_timedjoin)
int pthread_timedjoin_np(...) { return ENOSYS; } // pretend
#endif
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
#if QT_CONFIG(broken_threadlocal_dtors)
// On most modern platforms, the C runtime has a helper function that helps the
// C++ runtime run the thread_local non-trivial destructors when threads exit
// and that code ensures that they are run in the correct order on program exit
// too ([basic.start.term]/2: "The destruction of all constructed objects with
// thread storage duration within that thread strongly happens before
// destroying any object with static storage duration."). In the absence of
// this function, the ordering can be wrong depending on when the first
// non-trivial thread_local object was created relative to other statics.
// Moreover, this can be racy and having our own thread_local early in
// QThreadPrivate::start() made it even more so. See QTBUG-129846 for analysis.
//
QThread/Unix: revert to pthread destruction instead of thread_local Amends 65093a84c2b94b1543fd4593bc45d491951d28d4, which changed how we destroyed the main thread's QThreadData. This merges the call to destroy_current_thread_data() for both types of Unix systems: those with broken thread_local destructors and those with working ones. It turns out that the function got called too early for us in those working systems (see updated comment). The clean up of the QThreadData is split into two different mechanisms: * for any auxiliary thread, when it exits, PThread will call back to destroy_current_thread_data() * for the thread that called ::exit(), PThread won't, but ::exit() will invoke set_thread_data()::TlsKey's destructor This is different from the situation that existed prior to commit 65093a84c2b94b1543fd4593bc45d491951d28d4: first, there's no code in qcoreapplication.cpp for this (all in qthread_unix.cpp). Second one may call ::exit() from any thread, whether that is the thread that called main(), the thread Qt thinks is theMainThread, or any other. This commit moves the tst_QCoreApplication check for no extant objects to a new test. I've chosen to add a new test instead of running a helper binary via QProcess because we do have a couple of !QT_CONFIG(process) platforms in the CI, and this is too important. Credit to OSS-Fuzz for finding this, though it is not itself a fuzzying problem (all tests of a given structure were crashing on exit). Fixes: QTBUG-132381 Task-number: QTBUG-130895 Task-number: QTBUG-129927 Task-number: QTBUG-129846 Task-number: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.9 6.8 Change-Id: Ie294dce7263b4189f89ffffd9155ec71d31b89d9 Reviewed-by: Robert Löhning <robert.loehning@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-12-20 00:33:16 +00:00
// There's a good correlation between this C++11 feature and our ability to
// call QThreadPrivate::cleanup() from destroy_thread_data().
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
//
// https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/libsupc%2B%2B/atexit_thread.cc;hb=releases/gcc-14.2.0#l133
// https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/libcxxabi/src/cxa_thread_atexit.cpp#L118-L120
QThread/Unix: revert to pthread destruction instead of thread_local Amends 65093a84c2b94b1543fd4593bc45d491951d28d4, which changed how we destroyed the main thread's QThreadData. This merges the call to destroy_current_thread_data() for both types of Unix systems: those with broken thread_local destructors and those with working ones. It turns out that the function got called too early for us in those working systems (see updated comment). The clean up of the QThreadData is split into two different mechanisms: * for any auxiliary thread, when it exits, PThread will call back to destroy_current_thread_data() * for the thread that called ::exit(), PThread won't, but ::exit() will invoke set_thread_data()::TlsKey's destructor This is different from the situation that existed prior to commit 65093a84c2b94b1543fd4593bc45d491951d28d4: first, there's no code in qcoreapplication.cpp for this (all in qthread_unix.cpp). Second one may call ::exit() from any thread, whether that is the thread that called main(), the thread Qt thinks is theMainThread, or any other. This commit moves the tst_QCoreApplication check for no extant objects to a new test. I've chosen to add a new test instead of running a helper binary via QProcess because we do have a couple of !QT_CONFIG(process) platforms in the CI, and this is too important. Credit to OSS-Fuzz for finding this, though it is not itself a fuzzying problem (all tests of a given structure were crashing on exit). Fixes: QTBUG-132381 Task-number: QTBUG-130895 Task-number: QTBUG-129927 Task-number: QTBUG-129846 Task-number: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.9 6.8 Change-Id: Ie294dce7263b4189f89ffffd9155ec71d31b89d9 Reviewed-by: Robert Löhning <robert.loehning@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-12-20 00:33:16 +00:00
#endif
//
// Thus, the destruction of QThreadData is split into 3 steps:
// - finish()
// - cleanup()
// - deref & delete
//
// The reason for the first split is that user content may run as a result of
// the finished() signal, in thread_local destructors or similar, so we don't
// want to destroy the event dispatcher too soon. If we did, the event
// dispatcher could get recreated.
//
// For auxiliary threads started with QThread, finish() is run as soon as run()
// returns, while cleanup() and the deref happen at pthread_set_specific
// destruction time (except for broken_threadlocal_dtors, see above).
//
// For auxiliary threads started with something else and adopted as a
// QAdoptedThread, there's only one choice: all three steps happen at at
// pthread_set_specific destruction time.
//
// Finally, for the thread that called ::exit() (which in most cases happens by
// returning from the main() function), finish() and cleanup() happen at
// function-local static destructor time, and the deref & delete happens later,
QLibrary/Darwin: don't use RTLD_NODELETE, just don't dlclose() As the comment says, the OS does support the flag and a perusal of the source code for dyld[1] shows it does handle the flag, setting an internal flag called "leaveMapped", which is different from the "neverUnload" flag. My guess is that it literally just leaves the memory for the plugin mapped, but removes all links to it in the dyld internal data structures. That results in the Objective C runtime crashing when running content from NODELETE plugins that has been dlclose()d. This probably explains why lldb stops seeing those symbols too: (lldb) f frame #0: 0x0000000103872eb4 QtCore`QLibraryPrivate::unload_sys(this=0x000060000179c210) at qlibrary_unix.cpp:258:21 -> 258 if (doTryUnload && dlclose(pHnd.loadAcquire())) { (lldb) target modules list libqcocoa.dylib [ 0] 81EB58D5-8D31-333A-9E8C-F2385F6EFCF4 0x00000001043de000 libqcocoa.dylib (lldb) image lookup -vn QCocoaEventDispatcher::QCocoaEventDispatcher 2 matches found in libqcocoa.dylib: Address: libqcocoa.dylib[0x0000000000036480] (libqcocoa.dylib.__TEXT.__text + 190368) Summary: libqcocoa.dylib`QCocoaEventDispatcher::QCocoaEventDispatcher(QObject*) at qcocoaeventdispatcher.mm:776 ... (lldb) n (lldb) target modules list libqcocoa.dylib [ 0] 81EB58D5-8D31-333A-9E8C-F2385F6EFCF4 libqcocoa.dylib[0x0000000000000000] libqcocoa.dylib (lldb) image lookup -vn QCocoaEventDispatcher::QCocoaEventDispatcher [1] https://github.com/apple-oss-distributions/dyld Task-number: QTBUG-135044 Task-number: QTBUG-134080 Task-number: QTBUG-133861 Task-number: QTBUG-132697 Task-number: QTBUG-102984 Task-number: QTBUG-132381 Pick-to: 6.9 6.9.0 6.8 Change-Id: I7da3b3615a6ace7c72d1fffd5cf560d8f8a4e1bb Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
2025-03-25 05:14:32 +00:00
// at global static destruction time. That way, we delete the event dispatcher
// before QLibraryStore's clean up runs and unloads remaining plugins. This
// strategy was chosen because of crashes observed while running the event
// dispatcher's destructor, and though the cause of the crash was something
// else (QFactoryLoader always loads with PreventUnloadHint set), other plugins
// may still attempt to access QThreadData in their global destructors.
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
Q_CONSTINIT static thread_local QThreadData *currentThreadData = nullptr;
static void destroy_current_thread_data(QThreadData *data)
{
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
QThread *thread = data->thread.loadAcquire();
#ifdef Q_OS_APPLE
// apparent runtime bug: the trivial has been cleared and we end up
// recreating the QThreadData
currentThreadData = data;
#endif
if (data->isAdopted) {
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
// If this is an adopted thread, then QThreadData owns the QThread and
// this is very likely the last reference. These pointers cannot be
// null and there is no race.
QThreadPrivate *thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread));
thread_p->finish();
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
if constexpr (!QT_CONFIG(broken_threadlocal_dtors))
thread_p->cleanup();
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
} else if constexpr (!QT_CONFIG(broken_threadlocal_dtors)) {
// We may be racing the QThread destructor in another thread. With
// two-phase clean-up enabled, there's also no race because it will
// stop in a call to QThread::wait() until we call cleanup().
QThreadPrivate *thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread));
thread_p->cleanup();
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
} else {
// We may be racing the QThread destructor in another thread and it may
// have begun destruction; we must not dereference the QThread pointer.
}
}
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
static void deref_current_thread_data(QThreadData *data)
{
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
// the QThread object may still have a reference, so this may not delete
data->deref();
// ... but we must reset it to zero before returning so we aren't
// leaving a dangling pointer.
currentThreadData = nullptr;
}
static void destroy_auxiliary_thread_data(void *p)
{
auto data = static_cast<QThreadData *>(p);
destroy_current_thread_data(data);
deref_current_thread_data(data);
}
// Utility functions for getting, setting and clearing thread specific data.
static QThreadData *get_thread_data()
{
return currentThreadData;
}
namespace {
struct QThreadDataDestroyer
{
pthread_key_t key;
QThreadDataDestroyer() noexcept
{
pthread_key_create(&key, &destroy_auxiliary_thread_data);
}
~QThreadDataDestroyer()
{
// running global static destructors upon ::exit()
if (QThreadData *data = get_thread_data())
deref_current_thread_data(data);
pthread_key_delete(key);
}
struct EarlyMainThread {
EarlyMainThread() { QThreadStoragePrivate::init(); }
~EarlyMainThread()
{
// running function-local destructors upon ::exit()
if (QThreadData *data = get_thread_data())
destroy_current_thread_data(data);
}
};
};
}
#if QT_SUPPORTS_INIT_PRIORITY
Q_DECL_INIT_PRIORITY(10)
#endif
static QThreadDataDestroyer threadDataDestroyer; // intentional non-trivial init & destruction
static void set_thread_data(QThreadData *data) noexcept
{
QThread/Unix: do clean up the QAdoptedThread for the main thread Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 ("QThread/Unix: make QThreadPrivate::finish() be called much later") introduced this problem. Commit 4fabde349f16b59f37568da2a4c050c6dd53a34e split the thread termination in two phases, but did not fix this. This re-applies commit 950b35cf97ad398f97883efd2a18ee97994a8a9c ("Clear the current thread data for the main thread"), which was reverted in commit 7dc622290bb8e81af634034f443e25be0d6d48a3 ("Make sure QThreadData and QAdoptedThread object is destroyed at app exit"), both from Qt 5.1. Between Qt 5.1 and 6.7, the responsibility of clearing the QAdoptedThread for the main thread was split: it could occur either in ~QCoreApplicationData if exit() was called in that thread or in ~QThreadData() if it wasn't (e.g., when the Qt "main thread" is not main()'s thread): * frame #0: 0x0000000101db8a28 QtCore`QAdoptedThread::~QAdoptedThread(this=0x000060000176c070) at qthread.cpp:139:1 frame #1: 0x0000000101db81eb QtCore`QThreadData::~QThreadData(this=0x0000600002468000) at qthread.cpp:82:5 frame #2: 0x0000000101db8379 QtCore`QThreadData::~QThreadData(this=0x0000600002468000) at qthread.cpp:57:1 frame #3: 0x0000000101db841c QtCore`QThreadData::deref(this=0x0000600002468000) at qthread.cpp:108:9 frame #4: 0x0000000101f4ec79 QtCore`destroy_current_thread_data(p=0x0000600002468000) at qthread_unix.cpp:104:11 This commit centralizes and gives ~QThreadData() the exclusive responsibility. That requires not resetting QThreadData::threadId so ~QThreadData can know it is theMainThread. Fixes: QTBUG-130895 Task-number: QTBUG-129927 Task-number: QTBUG-129846 Task-number: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie3f3cbdc5523837b505cfffd95fba5e6498b5069 Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
2024-11-05 23:50:12 +00:00
if (data) {
// As noted above: one global static for the thread that called
// ::exit() (which may not be a Qt thread) and the pthread_key_t for
// all others.
static QThreadDataDestroyer::EarlyMainThread currentThreadCleanup;
pthread_setspecific(threadDataDestroyer.key, data);
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
}
currentThreadData = data;
}
template <typename T>
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<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<std::is_pointer_v<T>, Qt::HANDLE>::type to_HANDLE(T id)
{
return id;
}
template <typename T>
static typename std::enable_if<std::is_pointer_v<T>, T>::type from_HANDLE(Qt::HANDLE id)
{
return static_cast<T>(id);
}
void QThreadData::clearCurrentThreadData()
{
set_thread_data(nullptr);
}
QThreadData *QThreadData::currentThreadData() noexcept
{
return get_thread_data();
}
QThreadData *QThreadData::createCurrentThreadData()
{
Q_ASSERT(!currentThreadData());
std::unique_ptr data = std::make_unique<QThreadData>();
// This needs to be called prior to new QAdoptedThread() to avoid
// recursion (see qobject.cpp).
set_thread_data(data.get());
QT_TRY {
data->thread.storeRelease(new QAdoptedThread(data.get()));
} QT_CATCH(...) {
clearCurrentThreadData();
QT_RETHROW;
}
return data.release();
}
void QAdoptedThread::init()
{
}
/*
QThreadPrivate
*/
extern "C" {
typedef void *(*QtThreadCallback)(void *);
}
#endif // QT_CONFIG(thread)
QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *data)
{
Q_UNUSED(data);
#if defined(Q_OS_DARWIN)
bool ok = false;
int value = qEnvironmentVariableIntValue("QT_EVENT_DISPATCHER_CORE_FOUNDATION", &ok);
if (ok && value > 0)
return new QEventDispatcherCoreFoundation;
else
return new QEventDispatcherUNIX;
#elif defined(Q_OS_WASM)
return new QEventDispatcherWasm();
#elif !defined(QT_NO_GLIB)
const bool isQtMainThread = data->thread.loadAcquire() == QCoreApplicationPrivate::mainThread();
if (qEnvironmentVariableIsEmpty("QT_NO_GLIB")
&& (isQtMainThread || qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB"))
&& QEventDispatcherGlib::versionSupported())
return new QEventDispatcherGlib;
else
return new QEventDispatcherUNIX;
#else
return new QEventDispatcherUNIX;
#endif
}
#if QT_CONFIG(thread)
#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_DARWIN)
pthread_setname_np(name);
# elif defined(Q_OS_QNX)
pthread_setname_np(pthread_self(), name);
# endif
}
#endif
namespace {
template <typename T>
void terminate_on_exception(T &&t)
{
#ifndef QT_NO_EXCEPTIONS
try {
#endif
std::forward<T>(t)();
#ifndef QT_NO_EXCEPTIONS
#ifdef __GLIBCXX__
// POSIX thread cancellation under glibc is implemented by throwing an exception
// of this type. Do what libstdc++ is doing and handle it specially in order not to
// abort the application if user's code calls a cancellation function.
} catch (abi::__forced_unwind &) {
throw;
#endif // __GLIBCXX__
} catch (...) {
std::terminate();
}
#endif // QT_NO_EXCEPTIONS
}
} // unnamed namespace
void *QThreadPrivate::start(void *arg)
{
#ifdef PTHREAD_CANCEL_DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr);
#endif
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
QThread *thr = reinterpret_cast<QThread *>(arg);
QThreadData *data = QThreadData::get2(thr);
// If a QThread is restarted, reuse the QBindingStatus, too
data->reuseBindingStatusForNewNativeThread();
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
// this ensures the thread-local is created as early as possible
set_thread_data(data);
pthread_cleanup_push([](void *arg) { static_cast<QThread *>(arg)->d_func()->finish(); }, arg);
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
terminate_on_exception([&] {
{
QMutexLocker locker(&thr->d_func()->mutex);
// do we need to reset the thread priority?
if (thr->d_func()->priority & ThreadPriorityResetFlag) {
thr->d_func()->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag));
}
#ifndef Q_OS_DARWIN // For Darwin we set it as an attribute when starting the thread
if (thr->d_func()->serviceLevel != QThread::QualityOfService::Auto)
thr->d_func()->setQualityOfServiceLevel(thr->d_func()->serviceLevel);
#endif
// threadId is set in QThread::start()
Q_ASSERT(data->threadId.loadRelaxed() == QThread::currentThreadId());
data->ref();
data->quitNow = thr->d_func()->exited;
}
data->ensureEventDispatcher();
data->eventDispatcher.loadRelaxed()->startingUp();
#if (defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_QNX))
{
// Sets the name of the current thread. We can only do this
// when the thread is starting, as we don't have a cross
// platform way of setting the name of an arbitrary thread.
if (Q_LIKELY(thr->d_func()->objectName.isEmpty()))
setCurrentThreadName(thr->metaObject()->className());
else
setCurrentThreadName(std::exchange(thr->d_func()->objectName, {}).toLocal8Bit());
}
#endif
emit thr->started(QThread::QPrivateSignal());
#ifdef PTHREAD_CANCEL_DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr);
pthread_testcancel();
#endif
thr->run();
});
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
// This calls finish(); later, the currentThreadCleanup thread-local
// destructor will call cleanup().
pthread_cleanup_pop(1);
return nullptr;
}
void QThreadPrivate::finish()
{
terminate_on_exception([&] {
QThreadPrivate *d = this;
QThread *thr = q_func();
QThread/Unix: fix normal exit/terminate() race The QThreadPrivate::finish() cleanup code is not prepared to be subjected to POSIX cancellation: if, like on glibc, thread cancellation is implemented by stack ununwinding, we get exceptions in dtors and therefore std::terminate(), and otherwise, we leak resources. It would be very hard to make the code robust against this, as it would require all cleanup to be wrapped in pthread_cleanup_push/pop, with the added problem that these functions need to appear in the same lexical scope. Another alternative would be to move all cleanup code into a thread_local destructor, but it's not clear whether code running as part of thread_local destruction would be exempt from cancellation, and it would be a major rewrite. The simplest method is to disable cancellation for the remainder of the thread lifetime in the shutdown code, just like the startup code only enables cancellation after initial setup, so do that. [ChangeLog][Important Behavior Changes][QThread] On Unix, fixed a race of QThread::terminate() with normal thread exit (running off the end of run()) which could corrupt QThread's internal cleanup code. The fix involves disabling thread cancellation for the remainder of the thread's lifetime once control reaches QThread's cleanup code. If you rely on a PTHREAD_CANCELED return status, be aware that this change may mask late cancellations. Likewise, slots connected to QThread::finished() using Qt::DirectConnection are now run in a regime where thread cancellation is already disabled. If you need cancellation in that situation to work, you need to define your own finished()-like signal and emit that at the end of run(). Fixes: QTBUG-127008 Pick-to: 6.8 Change-Id: I23030eefdfcebf0a6d6796db5cbbbf0812ae12c0 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-07-08 16:25:42 +00:00
// Disable cancellation; we're already in the finishing touches of this
// thread, and we don't want cleanup to be disturbed by
// abi::__forced_unwind being thrown from all kinds of functions.
#ifdef PTHREAD_CANCEL_DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr);
#endif
QMutexLocker locker(&d->mutex);
d->threadState = QThreadPrivate::Finishing;
locker.unlock();
emit thr->finished(QThread::QPrivateSignal());
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
QThreadStoragePrivate::finish(&d->data->tls);
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
});
if constexpr (QT_CONFIG(broken_threadlocal_dtors))
cleanup();
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
}
void QThreadPrivate::cleanup()
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
{
terminate_on_exception([&] {
QThreadPrivate *d = this;
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
// Disable cancellation again: we did it above, but some user code
// running between finish() and cleanup() may have turned them back on.
#ifdef PTHREAD_CANCEL_DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr);
#endif
QMutexLocker locker(&d->mutex);
d->priority = QThread::InheritPriority;
QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
if (eventDispatcher) {
d->data->eventDispatcher = nullptr;
locker.unlock();
eventDispatcher->closingDown();
delete eventDispatcher;
locker.relock();
}
d->interruptionRequested.store(false, std::memory_order_relaxed);
d->wakeAll();
});
}
/**************************************************************************
** QThread
*************************************************************************/
/*
CI tests fails on ARM architectures if we try to use the assembler, so
stick to the pthread version there. The assembler would be
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344k/Babeihid.html
asm volatile ("mrc p15, 0, %0, c13, c0, 3" : "=r" (tid));
and
// see glibc/sysdeps/aarch64/nptl/tls.h
asm volatile ("mrs %0, tpidr_el0" : "=r" (tid));
for 32 and 64bit versions, respectively.
*/
Qt::HANDLE QThread::currentThreadIdImpl() noexcept
{
return to_HANDLE(pthread_self());
}
#if defined(QT_LINUXBASE) && !defined(_SC_NPROCESSORS_ONLN)
// LSB doesn't define _SC_NPROCESSORS_ONLN.
# define _SC_NPROCESSORS_ONLN 84
#endif
#ifdef Q_OS_WASM
int QThreadPrivate::idealThreadCount = 1;
#endif
#if QT_CONFIG(trivial_auto_var_init_pattern) && defined(Q_CC_GNU_ONLY)
// Don't pre-fill the automatic-storage arrays used in this function
// (important for the FreeBSD & Linux code using a VLA).
__attribute__((optimize("trivial-auto-var-init=uninitialized")))
#endif
int QThread::idealThreadCount() noexcept
{
int cores = 1;
#if defined(Q_OS_HPUX)
// HP-UX
struct pst_dynamic psd;
if (pstat_getdynamic(&psd, sizeof(psd), 1, 0) == -1) {
perror("pstat_getdynamic");
} else {
cores = (int)psd.psd_proc_cnt;
}
#elif (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD)
QT_WARNING_PUSH
# if defined(Q_CC_CLANG) && Q_CC_CLANG >= 1800
QT_WARNING_DISABLE_CLANG("-Wvla-cxx-extension")
# endif
// get the number of threads we're assigned, not the total in the system
constexpr qsizetype MaxCpuCount = 1024 * 1024;
constexpr qsizetype MaxCpuSetArraySize = MaxCpuCount / sizeof(cpu_set_t) / 8;
qsizetype size = 1;
do {
cpu_set_t cpuset[size];
if (sched_getaffinity(0, sizeof(cpu_set_t) * size, cpuset) == 0) {
cores = CPU_COUNT_S(sizeof(cpu_set_t) * size, cpuset);
break;
}
size *= 4;
} while (size < MaxCpuSetArraySize);
QT_WARNING_POP
#elif defined(Q_OS_BSD4)
// OpenBSD, NetBSD, BSD/OS, Darwin (macOS, iOS, etc.)
size_t len = sizeof(cores);
int mib[2];
mib[0] = CTL_HW;
mib[1] = HW_NCPU;
if (sysctl(mib, 2, &cores, &len, NULL, 0) != 0) {
perror("sysctl");
}
#elif defined(Q_OS_INTEGRITY)
#if (__INTEGRITY_MAJOR_VERSION >= 10)
// Integrity V10+ does support multicore CPUs
Value processorCount;
if (GetProcessorCount(CurrentTask(), &processorCount) == 0)
cores = processorCount;
else
#endif
// as of aug 2008 Integrity only supports one single core CPU
cores = 1;
#elif defined(Q_OS_VXWORKS)
cpuset_t cpus = vxCpuEnabledGet();
cores = 0;
// 128 cores should be enough for everyone ;)
for (int i = 0; i < 128 && !CPUSET_ISZERO(cpus); ++i) {
if (CPUSET_ISSET(cpus, i)) {
CPUSET_CLR(cpus, i);
cores++;
}
}
#elif defined(Q_OS_WASM)
cores = QThreadPrivate::idealThreadCount;
#else
// the rest: Solaris, AIX, Tru64
cores = (int)sysconf(_SC_NPROCESSORS_ONLN);
if (cores == -1)
return 1;
#endif
return cores;
}
void QThread::yieldCurrentThread()
{
sched_yield();
}
#endif // QT_CONFIG(thread)
static void qt_nanosleep(timespec amount)
{
// 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)
{
sleep(std::chrono::seconds{secs});
}
void QThread::msleep(unsigned long msecs)
{
sleep(std::chrono::milliseconds{msecs});
}
void QThread::usleep(unsigned long usecs)
{
sleep(std::chrono::microseconds{usecs});
}
void QThread::sleep(std::chrono::nanoseconds nsec)
{
qt_nanosleep(durationToTimespec(nsec));
}
#if QT_CONFIG(thread)
#ifdef QT_HAS_THREAD_PRIORITY_SCHEDULING
#if defined(Q_OS_QNX)
static bool calculateUnixPriority(int priority, int *sched_policy, int *sched_priority)
{
// On QNX, NormalPriority is mapped to 10. A QNX system could use a value different
// than 10 for the "normal" priority but it's difficult to achieve this so we'll
// assume that no one has ever created such a system. This makes the mapping from
// Qt priorities to QNX priorities lopsided. There's usually more space available
// to map into above the "normal" priority than below it. QNX also has a privileged
// priority range (for threads that assist the kernel). We'll assume that no Qt
// thread needs to use priorities in that range.
int priority_norm = 10;
// _sched_info::priority_priv isn't documented. You'd think that it's the start of the
// privileged priority range but it's actually the end of the unpriviledged range.
struct _sched_info info;
if (SchedInfo_r(0, *sched_policy, &info) != EOK)
return false;
if (priority == QThread::IdlePriority) {
*sched_priority = info.priority_min;
return true;
}
if (priority_norm < info.priority_min)
priority_norm = info.priority_min;
if (priority_norm > info.priority_priv)
priority_norm = info.priority_priv;
int to_min, to_max;
int from_min, from_max;
int prio;
if (priority < QThread::NormalPriority) {
to_min = info.priority_min;
to_max = priority_norm;
from_min = QThread::LowestPriority;
from_max = QThread::NormalPriority;
} else {
to_min = priority_norm;
to_max = info.priority_priv;
from_min = QThread::NormalPriority;
from_max = QThread::TimeCriticalPriority;
}
prio = ((priority - from_min) * (to_max - to_min)) / (from_max - from_min) + to_min;
prio = qBound(to_min, prio, to_max);
*sched_priority = prio;
return true;
}
#else
// Does some magic and calculate the Unix scheduler priorities
// sched_policy is IN/OUT: it must be set to a valid policy before calling this function
// sched_priority is OUT only
static bool calculateUnixPriority(int priority, int *sched_policy, int *sched_priority)
{
#ifdef SCHED_IDLE
if (priority == QThread::IdlePriority) {
*sched_policy = SCHED_IDLE;
*sched_priority = 0;
return true;
}
const int lowestPriority = QThread::LowestPriority;
#else
const int lowestPriority = QThread::IdlePriority;
#endif
const int highestPriority = QThread::TimeCriticalPriority;
int prio_min;
int prio_max;
#if defined(Q_OS_VXWORKS)
// for other scheduling policies than SCHED_RR or SCHED_FIFO
prio_min = SCHED_FIFO_LOW_PRI;
prio_max = SCHED_FIFO_HIGH_PRI;
if ((*sched_policy == SCHED_RR) || (*sched_policy == SCHED_FIFO))
#endif
{
prio_min = sched_get_priority_min(*sched_policy);
prio_max = sched_get_priority_max(*sched_policy);
}
if (prio_min == -1 || prio_max == -1)
return false;
int prio;
// crudely scale our priority enum values to the prio_min/prio_max
prio = ((priority - lowestPriority) * (prio_max - prio_min) / highestPriority) + prio_min;
prio = qMax(prio_min, qMin(prio_max, prio));
*sched_priority = prio;
return true;
}
#endif
#endif
void QThread::start(Priority priority)
{
Q_D(QThread);
QMutexLocker locker(&d->mutex);
if (d->threadState == QThreadPrivate::Finishing)
d->wait(locker, QDeadlineTimer::Forever);
if (d->threadState == QThreadPrivate::Running)
return;
d->threadState = QThreadPrivate::Running;
d->returnCode = 0;
d->exited = false;
d->interruptionRequested.store(false, std::memory_order_relaxed);
d->terminated = false;
pthread_attr_t attr;
pthread_attr_init(&attr);
if constexpr (!UsingPThreadTimedJoin)
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
#ifdef Q_OS_DARWIN
if (d->serviceLevel != QThread::QualityOfService::Auto)
pthread_attr_set_qos_class_np(&attr, d->nativeQualityOfServiceClass(), 0);
#endif
d->priority = priority;
#if defined(QT_HAS_THREAD_PRIORITY_SCHEDULING)
switch (priority) {
case InheritPriority:
{
pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
break;
}
default:
{
int sched_policy;
if (pthread_attr_getschedpolicy(&attr, &sched_policy) != 0) {
// failed to get the scheduling policy, don't bother
// setting the priority
qWarning("QThread::start: Cannot determine default scheduler policy");
break;
}
int prio;
if (!calculateUnixPriority(priority, &sched_policy, &prio)) {
// failed to get the scheduling parameters, don't
// bother setting the priority
qWarning("QThread::start: Cannot determine scheduler priority range");
break;
}
sched_param sp;
sp.sched_priority = prio;
if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) != 0
|| pthread_attr_setschedpolicy(&attr, sched_policy) != 0
|| pthread_attr_setschedparam(&attr, &sp) != 0) {
// could not set scheduling hints, fallback to inheriting them
// we'll try again from inside the thread
pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
d->priority = qToUnderlying(priority) | ThreadPriorityResetFlag;
}
break;
}
}
#endif // QT_HAS_THREAD_PRIORITY_SCHEDULING
if (d->stackSize > 0) {
#if defined(_POSIX_THREAD_ATTR_STACKSIZE) && (_POSIX_THREAD_ATTR_STACKSIZE-0 > 0)
int code = pthread_attr_setstacksize(&attr, d->stackSize);
#else
int code = ENOSYS; // stack size not supported, automatically fail
#endif // _POSIX_THREAD_ATTR_STACKSIZE
if (code) {
qErrnoWarning(code, "QThread::start: Thread stack size error");
// we failed to set the stacksize, and as the documentation states,
// the thread will fail to run...
d->threadState = QThreadPrivate::NotStarted;
return;
}
}
#ifdef Q_OS_INTEGRITY
if (Q_LIKELY(objectName().isEmpty()))
pthread_attr_setthreadname(&attr, metaObject()->className());
else
pthread_attr_setthreadname(&attr, objectName().toLocal8Bit());
#else
// avoid interacting with the binding system
d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
: QString();
#endif
pthread_t threadId;
int code = pthread_create(&threadId, &attr, QThreadPrivate::start, this);
if (code == EPERM) {
// caller does not have permission to set the scheduling
// parameters/policy
#if defined(QT_HAS_THREAD_PRIORITY_SCHEDULING)
pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
#endif
code = pthread_create(&threadId, &attr, QThreadPrivate::start, this);
}
d->data->threadId.storeRelaxed(to_HANDLE(threadId));
pthread_attr_destroy(&attr);
if (code) {
qErrnoWarning(code, "QThread::start: Thread creation error");
d->threadState = QThreadPrivate::NotStarted;
d->data->threadId.storeRelaxed(nullptr);
}
}
void QThread::terminate()
{
#if !defined(Q_OS_ANDROID)
Q_D(QThread);
QMutexLocker locker(&d->mutex);
const auto id = d->data->threadId.loadRelaxed();
if (!id)
return;
if (d->terminated) // don't try again, avoids killing the wrong thread on threadId reuse (ABA)
return;
d->terminated = true;
QThread/Unix: refactor to split QThreadPrivate::finish() in two phases Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 moved the finish() functionality from immediately after run() returns to the time of thread-local destruction, to make sure that user destructors didn't run after our cleaning up. But as a side effect, it made other user code run too late, after some thread-local statics had been destroyed. This is a common practice, which causes the destructor for worker to run too late: worker->moveToThread(thread); ... QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); This commit splits the cleanup in two phases: QThreadPrivate::finish(), which runs immediately after run() and will call back out to user code (finished() signal and delivery of deleteLater()), and cleanup() that cleans up the QThread{Private,Data} state and destroys the event dispatcher. That destruction is the only call out to user code. I've removed the complex mix of pre-C++11 pthread_setspecific() content and C++11 thread_local variables in favor of using one or the other, not both. We prefer the thread-local for future-proofing and simplicity, on platforms where we can verify this C++11 feature works, and because it allows us to clean up QThreadData and the event dispatcher as late as possible. (There's some code that runs even later, such as pthread TLS destructors, used by Glib's GMainLoop) Unfortunately, we can't use it everywhere. The commit above had already noticed QNX has a problem and recent bug reports have shown other platforms (Solaris, MUSL libc) that, 13 years after the ratification of the standard, still have broken support, so we use pthread for them and we call cleanup() from within finish() (that is, no late cleaning-up, retaining the status quo from Qt 4 and 5). See QTBUG-129846 for an analysis. Drive-by moving the resetting of thread priority to after finished() is emitted. [ChangeLog][QtCore][QThread] Restored the Qt 6.7 timing of when the finished() signal is emitted relative to the destruction of thread_local variables. Qt 6.8.0 contained a change that moved this signal to a later time on most Unix systems, which has caused problems with the order in which those variables were accessed. The destruction of the event dispatcher is kept at this late stage, wherever possible. Fixes: QTBUG-129927 Fixes: QTBUG-129846 Fixes: QTBUG-130341 Task-number: QTBUG-117996 Pick-to: 6.8 Change-Id: Ie5e40dd18faa05d8f777fffdf7dc30fc4fe0c7e9 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2024-10-15 16:17:39 +00:00
const bool selfCancelling = d->data == get_thread_data();
if (selfCancelling) {
// Posix doesn't seem to specify whether the stack of cancelled threads
// is unwound, and there's nothing preventing a QThread from
// terminate()ing itself, so drop the mutex before calling
// pthread_cancel():
locker.unlock();
}
if (int code = pthread_cancel(from_HANDLE<pthread_t>(id))) {
if (selfCancelling)
locker.relock();
d->terminated = false; // allow to try again
qErrnoWarning(code, "QThread::start: Thread termination error");
}
#endif
}
static void wakeAllInternal(QThreadPrivate *d)
{
d->threadState = QThreadPrivate::Finished;
if (d->waiters)
d->thread_done.wakeAll();
}
inline void QThreadPrivate::wakeAll()
{
if (data->isAdopted || !UsingPThreadTimedJoin)
wakeAllInternal(this);
}
bool QThreadPrivate::wait(QMutexLocker<QMutex> &locker, QDeadlineTimer deadline)
{
constexpr int HasJoinerBit = int(0x8000'0000); // a.k.a. sign bit
struct timespec ts, *pts = nullptr;
if (!deadline.isForever()) {
ts = deadlineToAbstime(deadline);
pts = &ts;
}
auto doJoin = [&] {
// pthread_join() & family are cancellation points
struct CancelState {
QThreadPrivate *d;
QMutexLocker<QMutex> *locker;
int joinResult = ETIMEDOUT;
static void run(void *arg) { static_cast<CancelState *>(arg)->run(); }
void run()
{
locker->relock();
if (joinResult == ETIMEDOUT && d->waiters)
d->thread_done.wakeOne();
else if (joinResult == 0)
wakeAllInternal(d);
d->waiters &= ~HasJoinerBit;
}
} nocancel = { this, &locker };
int &r = nocancel.joinResult;
// we're going to perform the join, so don't let other threads do it
waiters |= HasJoinerBit;
locker.unlock();
pthread_cleanup_push(&CancelState::run, &nocancel);
pthread_t thrId = from_HANDLE<pthread_t>(data->threadId.loadRelaxed());
if constexpr (QT_CONFIG(pthread_clockjoin))
r = pthread_clockjoin_np(thrId, nullptr, SteadyClockClockId, pts);
else
r = pthread_timedjoin_np(thrId, nullptr, pts);
Q_ASSERT(r == 0 || r == ETIMEDOUT);
pthread_cleanup_pop(1);
Q_ASSERT(waiters >= 0);
return r != ETIMEDOUT;
};
Q_ASSERT(threadState != QThreadPrivate::Finished);
Q_ASSERT(locker.isLocked());
bool result = false;
// both branches call cancellation points
++waiters;
bool mustJoin = (waiters & HasJoinerBit) == 0;
pthread_cleanup_push([](void *ptr) {
--(*static_cast<decltype(waiters) *>(ptr));
}, &waiters);
for (;;) {
if (UsingPThreadTimedJoin && mustJoin && !data->isAdopted) {
result = doJoin();
break;
}
if (!thread_done.wait(locker.mutex(), deadline))
break; // timed out
result = threadState == QThreadPrivate::Finished;
if (result)
break; // success
mustJoin = (waiters & HasJoinerBit) == 0;
}
pthread_cleanup_pop(1);
return result;
}
void QThread::setTerminationEnabled(bool enabled)
{
QThread *thr = currentThread();
Q_ASSERT_X(thr != nullptr, "QThread::setTerminationEnabled()",
"Current thread was not started with QThread.");
Q_UNUSED(thr);
#if defined(Q_OS_ANDROID)
Q_UNUSED(enabled);
#else
pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, nullptr);
if (enabled)
pthread_testcancel();
#endif
}
// Caller must lock the mutex
void QThreadPrivate::setPriority(QThread::Priority threadPriority)
{
priority = threadPriority;
// copied from start() with a few modifications:
#ifdef QT_HAS_THREAD_PRIORITY_SCHEDULING
int sched_policy;
sched_param param;
if (pthread_getschedparam(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()), &sched_policy, &param) != 0) {
// failed to get the scheduling policy, don't bother setting
// the priority
qWarning("QThread::setPriority: Cannot get scheduler parameters");
return;
}
int prio;
if (!calculateUnixPriority(priority, &sched_policy, &prio)) {
// failed to get the scheduling parameters, don't
// bother setting the priority
qWarning("QThread::setPriority: Cannot determine scheduler priority range");
return;
}
param.sched_priority = prio;
int status = pthread_setschedparam(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()), sched_policy, &param);
# ifdef SCHED_IDLE
// were we trying to set to idle priority and failed?
if (status == -1 && sched_policy == SCHED_IDLE && errno == EINVAL) {
// reset to lowest priority possible
pthread_getschedparam(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()), &sched_policy, &param);
param.sched_priority = sched_get_priority_min(sched_policy);
pthread_setschedparam(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()), sched_policy, &param);
}
# else
Q_UNUSED(status);
# endif // SCHED_IDLE
#endif
}
void QThreadPrivate::setQualityOfServiceLevel(QThread::QualityOfService qosLevel)
{
[[maybe_unused]]
Q_Q(QThread);
serviceLevel = qosLevel;
#ifdef Q_OS_DARWIN
qCDebug(lcQThread) << "Setting thread QoS class to" << serviceLevel << "for thread" << q;
pthread_set_qos_class_self_np(nativeQualityOfServiceClass(), 0);
#endif
}
#ifdef Q_OS_DARWIN
qos_class_t QThreadPrivate::nativeQualityOfServiceClass() const
{
// @note Consult table[0] to see what the levels mean
// [0] https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/PrioritizeWorkAtTheTaskLevel.html#//apple_ref/doc/uid/TP40013929-CH35-SW5
// There are more levels but they have two other documented ones,
// QOS_CLASS_BACKGROUND, which is below UTILITY, but has no guarantees
// for scheduling (ie. the OS could choose to never give it CPU time),
// and QOS_CLASS_USER_INITIATED, documented as being intended for
// user-initiated actions, such as loading a text document.
switch (serviceLevel) {
case QThread::QualityOfService::Auto:
return QOS_CLASS_DEFAULT;
case QThread::QualityOfService::High:
return QOS_CLASS_USER_INTERACTIVE;
case QThread::QualityOfService::Eco:
return QOS_CLASS_UTILITY;
}
Q_UNREACHABLE_RETURN(QOS_CLASS_DEFAULT);
}
#endif
#endif // QT_CONFIG(thread)
QT_END_NAMESPACE