2022-05-10 10:06:48 +00:00
|
|
|
// 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
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
#include "qthread.h"
|
2024-10-15 17:14:10 +00:00
|
|
|
#include "qthread_p.h"
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
#include <private/qcoreapplication_p.h>
|
2015-07-01 07:57:33 +00:00
|
|
|
#include <private/qcore_unix_p.h>
|
2024-10-15 17:14:10 +00:00
|
|
|
#include "qdebug.h"
|
2024-12-21 14:01:43 +00:00
|
|
|
#include "qloggingcategory.h"
|
2024-10-15 17:14:10 +00:00
|
|
|
#include "qthreadstorage.h"
|
2023-01-27 20:34:46 +00:00
|
|
|
#include <private/qtools_p.h>
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2024-10-15 17:14:10 +00:00
|
|
|
#if defined(Q_OS_WASM)
|
|
|
|
# include <private/qeventdispatcher_wasm_p.h>
|
2012-03-23 14:43:44 +00:00
|
|
|
#else
|
2023-07-31 13:42:35 +00:00
|
|
|
# include <private/qeventdispatcher_unix_p.h>
|
2024-10-15 17:14:10 +00:00
|
|
|
# if defined(Q_OS_DARWIN)
|
|
|
|
# include <private/qeventdispatcher_cf_p.h>
|
|
|
|
# elif !defined(QT_NO_GLIB)
|
|
|
|
# include <private/qeventdispatcher_glib_p.h>
|
|
|
|
# endif
|
2023-07-31 13:42:35 +00:00
|
|
|
#endif
|
2015-10-15 13:24:54 +00:00
|
|
|
|
2016-08-07 18:58:54 +00:00
|
|
|
#ifdef __GLIBCXX__
|
|
|
|
#include <cxxabi.h>
|
|
|
|
#endif
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
#include <sched.h>
|
|
|
|
#include <errno.h>
|
2024-11-02 17:28:12 +00:00
|
|
|
#if __has_include(<pthread_np.h>)
|
|
|
|
# include <pthread_np.h>
|
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-12-02 19:58:33 +00:00
|
|
|
#if defined(Q_OS_FREEBSD)
|
|
|
|
# include <sys/cpuset.h>
|
|
|
|
#elif defined(Q_OS_BSD4)
|
|
|
|
# include <sys/sysctl.h>
|
2011-04-27 10:05:43 +00:00
|
|
|
#endif
|
|
|
|
#ifdef Q_OS_VXWORKS
|
2024-05-22 08:47:06 +00:00
|
|
|
# include <vxCpuLib.h>
|
|
|
|
# include <cpuset.h>
|
2011-04-27 10:05:43 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef Q_OS_HPUX
|
|
|
|
#include <sys/pstat.h>
|
|
|
|
#endif
|
|
|
|
|
2012-03-28 07:50:52 +00:00
|
|
|
#if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
|
2012-02-03 12:41:47 +00:00
|
|
|
#include <sys/prctl.h>
|
|
|
|
#endif
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
#if defined(Q_OS_LINUX) && !defined(SCHED_IDLE)
|
|
|
|
// from linux/sched.h
|
|
|
|
# define SCHED_IDLE 5
|
|
|
|
#endif
|
|
|
|
|
2017-08-17 06:31:19 +00:00
|
|
|
#if defined(Q_OS_DARWIN) || !defined(Q_OS_ANDROID) && !defined(Q_OS_OPENBSD) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING-0 >= 0)
|
2011-04-27 10:05:43 +00:00
|
|
|
#define QT_HAS_THREAD_PRIORITY_SCHEDULING
|
|
|
|
#endif
|
|
|
|
|
2017-10-24 22:05:49 +00:00
|
|
|
#if defined(Q_OS_QNX)
|
|
|
|
#include <sys/neutrino.h>
|
|
|
|
#endif
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
Add thread Quality of Service API
With heterogenous CPU topology (and earlier, per-core clocks) OS
developers have increasingly added "Quality of Service" APIs to thread
scheduling, giving users a way to tell the OS what type of work the
thread is expected to do.
In the case of Apple they have a more extensive selection of
service levels. Unlike what we currently see from Windows, the API there
is actually much broader than just High, 'Default' and Eco, and covers
more concrete uses like "Utility", "Background", "User-initiated",
defined as -1 (Default), and a range from 0x09 (Background)
to 0x21 (User-interactive)[0].
Currently there is no equivalent API for Linux though there is some push
from various interested parties to add one (e.g. [1]).
As mentioned, on Windows there is really only 3 levels, though it's
defined as "do/don't throttle this thread" and "do as you wish".
For Android I cannot really find anything equivalent beyond the thread
priority.
Discussed here:
https://lists.qt-project.org/pipermail/development/2024-September/045694.html
[0] https://developer.apple.com/documentation/foundation/qualityofservice
[1] https://www.youtube.com/watch?v=RfgPWpTwTQo (Linux Plumbers Conference)
Fixes: QTBUG-93946
Change-Id: Iabeaa7b61cec0bebd5c6a4bcf75a8e60dc0348dc
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
2023-09-19 18:13:27 +00:00
|
|
|
[[maybe_unused]]
|
|
|
|
Q_STATIC_LOGGING_CATEGORY(lcQThread, "qt.core.thread", QtWarningMsg)
|
|
|
|
|
2023-01-27 20:34:46 +00:00
|
|
|
using namespace QtMiscUtils;
|
|
|
|
|
2016-12-29 16:11:24 +00:00
|
|
|
#if QT_CONFIG(thread)
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2020-06-11 09:35:19 +00:00
|
|
|
static_assert(sizeof(pthread_t) <= sizeof(Qt::HANDLE));
|
2016-04-29 00:47:57 +00:00
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
enum { ThreadPriorityResetFlag = 0x80000000 };
|
|
|
|
|
2024-11-02 17:28:12 +00:00
|
|
|
// 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.
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
//
|
|
|
|
// 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.
|
2024-11-02 17:28:12 +00:00
|
|
|
static constexpr bool UsingPThreadTimedJoin = QT_CONFIG(pthread_clockjoin)
|
2024-11-02 18:25:23 +00:00
|
|
|
|| (QT_CONFIG(pthread_timedjoin) && QWaitConditionClockId == CLOCK_REALTIME);
|
2024-11-02 17:28:12 +00:00
|
|
|
#if !QT_CONFIG(pthread_clockjoin)
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
int pthread_clockjoin_np(...) { return ENOSYS; } // pretend
|
|
|
|
#endif
|
2024-11-02 17:28:12 +00:00
|
|
|
#if !QT_CONFIG(pthread_timedjoin)
|
|
|
|
int pthread_timedjoin_np(...) { return ENOSYS; } // pretend
|
|
|
|
#endif
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
|
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.
|
|
|
|
//
|
|
|
|
// For the platforms where this C++11 feature is not properly implemented yet,
|
|
|
|
// we fall back to a pthread_setspecific() call and do not perform late
|
|
|
|
// clean-up, because then the order of registration of those pthread_specific_t
|
|
|
|
// keys matters and Glib uses them too.
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
#endif // QT_CONFIG(broken_threadlocal_dtors)
|
|
|
|
|
2022-03-17 11:07:44 +00:00
|
|
|
Q_CONSTINIT static thread_local QThreadData *currentThreadData = nullptr;
|
2020-07-02 08:53:10 +00:00
|
|
|
|
|
|
|
static void destroy_current_thread_data(void *p)
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2020-07-02 08:53:10 +00:00
|
|
|
QThreadData *data = static_cast<QThreadData *>(p);
|
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();
|
|
|
|
|
2020-07-02 08:53:10 +00:00
|
|
|
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.
|
2020-07-02 08:53:10 +00:00
|
|
|
QThreadPrivate *thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread));
|
2024-10-15 16:41:05 +00:00
|
|
|
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))
|
2024-10-15 16:41:05 +00:00
|
|
|
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));
|
2024-10-15 16:41:05 +00:00
|
|
|
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.
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
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
|
2020-07-02 08:53:10 +00:00
|
|
|
data->deref();
|
|
|
|
|
|
|
|
// ... but we must reset it to zero before returning so we aren't
|
2020-07-03 08:13:58 +00:00
|
|
|
// leaving a dangling pointer.
|
|
|
|
currentThreadData = nullptr;
|
2020-07-02 08:53:10 +00:00
|
|
|
}
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
// Utility functions for getting, setting and clearing thread specific data.
|
|
|
|
static QThreadData *get_thread_data()
|
|
|
|
{
|
2020-07-02 08:53:10 +00:00
|
|
|
return currentThreadData;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2024-11-05 23:50:12 +00:00
|
|
|
#if QT_CONFIG(broken_threadlocal_dtors)
|
|
|
|
// The destructors registered with pthread_key_create() below are NOT run from
|
|
|
|
// exit(), so we must also use atexit().
|
|
|
|
static void destroy_main_thread_data()
|
|
|
|
{
|
|
|
|
if (QThreadData *d = get_thread_data())
|
|
|
|
destroy_current_thread_data(d);
|
|
|
|
}
|
|
|
|
Q_DESTRUCTOR_FUNCTION(destroy_main_thread_data)
|
|
|
|
#endif
|
|
|
|
|
2024-11-14 22:10:06 +00:00
|
|
|
static void set_thread_data(QThreadData *data) noexcept
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2024-11-05 23:50:12 +00:00
|
|
|
if (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
|
|
|
if constexpr (QT_CONFIG(broken_threadlocal_dtors)) {
|
|
|
|
static pthread_key_t tls_key;
|
|
|
|
struct TlsKey {
|
2024-11-14 22:10:06 +00:00
|
|
|
TlsKey() noexcept { pthread_key_create(&tls_key, destroy_current_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
|
|
|
~TlsKey() { pthread_key_delete(tls_key); }
|
|
|
|
};
|
|
|
|
static TlsKey currentThreadCleanup;
|
|
|
|
pthread_setspecific(tls_key, data);
|
|
|
|
} else {
|
|
|
|
struct Cleanup {
|
|
|
|
~Cleanup() { destroy_current_thread_data(currentThreadData); }
|
|
|
|
};
|
|
|
|
static thread_local Cleanup currentThreadCleanup;
|
|
|
|
}
|
|
|
|
}
|
2020-07-02 08:53:10 +00:00
|
|
|
currentThreadData = data;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2016-06-09 16:13:08 +00:00
|
|
|
template <typename T>
|
2022-12-26 18:21:49 +00:00
|
|
|
static typename std::enable_if<std::is_integral_v<T>, Qt::HANDLE>::type to_HANDLE(T id)
|
2016-06-09 16:13:08 +00:00
|
|
|
{
|
|
|
|
return reinterpret_cast<Qt::HANDLE>(static_cast<intptr_t>(id));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-12-26 18:21:49 +00:00
|
|
|
static typename std::enable_if<std::is_integral_v<T>, T>::type from_HANDLE(Qt::HANDLE id)
|
2016-06-09 16:13:08 +00:00
|
|
|
{
|
|
|
|
return static_cast<T>(reinterpret_cast<intptr_t>(id));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-12-26 18:21:49 +00:00
|
|
|
static typename std::enable_if<std::is_pointer_v<T>, Qt::HANDLE>::type to_HANDLE(T id)
|
2016-06-09 16:13:08 +00:00
|
|
|
{
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2022-12-26 18:21:49 +00:00
|
|
|
static typename std::enable_if<std::is_pointer_v<T>, T>::type from_HANDLE(Qt::HANDLE id)
|
2016-06-09 16:13:08 +00:00
|
|
|
{
|
|
|
|
return static_cast<T>(id);
|
|
|
|
}
|
|
|
|
|
2013-01-18 06:39:00 +00:00
|
|
|
void QThreadData::clearCurrentThreadData()
|
|
|
|
{
|
2024-11-14 22:10:06 +00:00
|
|
|
set_thread_data(nullptr);
|
2013-01-18 06:39:00 +00:00
|
|
|
}
|
|
|
|
|
2024-11-14 22:18:37 +00:00
|
|
|
QThreadData *QThreadData::currentThreadData() noexcept
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2024-11-14 22:18:37 +00:00
|
|
|
return get_thread_data();
|
|
|
|
}
|
2024-11-14 22:10:06 +00:00
|
|
|
|
2024-11-14 22:18:37 +00:00
|
|
|
QThreadData *QThreadData::createCurrentThreadData()
|
|
|
|
{
|
|
|
|
Q_ASSERT(!currentThreadData());
|
2024-11-14 22:10:06 +00:00
|
|
|
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;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
2024-11-14 22:10:06 +00:00
|
|
|
return data.release();
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QAdoptedThread::init()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
QThreadPrivate
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern "C" {
|
2020-10-20 09:03:48 +00:00
|
|
|
typedef void *(*QtThreadCallback)(void *);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 16:11:24 +00:00
|
|
|
#endif // QT_CONFIG(thread)
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2018-02-13 13:37:05 +00:00
|
|
|
QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *data)
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2018-02-13 13:37:05 +00:00
|
|
|
Q_UNUSED(data);
|
2016-05-20 06:01:59 +00:00
|
|
|
#if defined(Q_OS_DARWIN)
|
2015-07-01 07:57:33 +00:00
|
|
|
bool ok = false;
|
|
|
|
int value = qEnvironmentVariableIntValue("QT_EVENT_DISPATCHER_CORE_FOUNDATION", &ok);
|
|
|
|
if (ok && value > 0)
|
2018-02-13 13:37:05 +00:00
|
|
|
return new QEventDispatcherCoreFoundation;
|
2015-07-01 07:57:33 +00:00
|
|
|
else
|
2018-02-13 13:37:05 +00:00
|
|
|
return new QEventDispatcherUNIX;
|
2021-08-18 12:54:11 +00:00
|
|
|
#elif defined(Q_OS_WASM)
|
|
|
|
return new QEventDispatcherWasm();
|
2015-11-23 06:13:00 +00:00
|
|
|
#elif !defined(QT_NO_GLIB)
|
2019-06-26 05:56:49 +00:00
|
|
|
const bool isQtMainThread = data->thread.loadAcquire() == QCoreApplicationPrivate::mainThread();
|
2012-08-06 22:10:11 +00:00
|
|
|
if (qEnvironmentVariableIsEmpty("QT_NO_GLIB")
|
2018-02-13 16:11:50 +00:00
|
|
|
&& (isQtMainThread || qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB"))
|
2011-04-27 10:05:43 +00:00
|
|
|
&& QEventDispatcherGlib::versionSupported())
|
2018-02-13 13:37:05 +00:00
|
|
|
return new QEventDispatcherGlib;
|
2011-04-27 10:05:43 +00:00
|
|
|
else
|
2018-02-13 13:37:05 +00:00
|
|
|
return new QEventDispatcherUNIX;
|
2015-07-01 07:57:33 +00:00
|
|
|
#else
|
2018-02-13 13:37:05 +00:00
|
|
|
return new QEventDispatcherUNIX;
|
2012-03-23 14:43:44 +00:00
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 16:11:24 +00:00
|
|
|
#if QT_CONFIG(thread)
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2023-03-17 16:53:51 +00:00
|
|
|
#if (defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_QNX))
|
2018-01-31 18:08:06 +00:00
|
|
|
static void setCurrentThreadName(const char *name)
|
2012-08-15 16:03:50 +00:00
|
|
|
{
|
|
|
|
# if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
|
|
|
|
prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0);
|
2023-03-17 16:53:51 +00:00
|
|
|
# elif defined(Q_OS_DARWIN)
|
2012-08-15 16:03:50 +00:00
|
|
|
pthread_setname_np(name);
|
|
|
|
# elif defined(Q_OS_QNX)
|
2018-01-31 18:08:06 +00:00
|
|
|
pthread_setname_np(pthread_self(), name);
|
2012-08-15 16:03:50 +00:00
|
|
|
# endif
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-12-22 14:01:27 +00:00
|
|
|
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 (...) {
|
2024-12-21 15:08:32 +00:00
|
|
|
std::terminate();
|
2021-12-22 14:01:27 +00:00
|
|
|
}
|
|
|
|
#endif // QT_NO_EXCEPTIONS
|
|
|
|
}
|
|
|
|
} // unnamed namespace
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
void *QThreadPrivate::start(void *arg)
|
|
|
|
{
|
2023-10-12 15:23:35 +00:00
|
|
|
#ifdef PTHREAD_CANCEL_DISABLE
|
2019-11-22 13:46:58 +00:00
|
|
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr);
|
2012-05-01 19:22:20 +00:00
|
|
|
#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);
|
2013-03-15 18:48:59 +00:00
|
|
|
|
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);
|
|
|
|
|
2024-10-15 16:41:05 +00:00
|
|
|
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([&] {
|
2016-08-07 18:58:54 +00:00
|
|
|
{
|
|
|
|
QMutexLocker locker(&thr->d_func()->mutex);
|
2013-03-15 18:48:59 +00:00
|
|
|
|
2016-08-07 18:58:54 +00:00
|
|
|
// do we need to reset the thread priority?
|
2021-12-16 12:50:21 +00:00
|
|
|
if (thr->d_func()->priority & ThreadPriorityResetFlag) {
|
|
|
|
thr->d_func()->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag));
|
2016-08-07 18:58:54 +00:00
|
|
|
}
|
Add thread Quality of Service API
With heterogenous CPU topology (and earlier, per-core clocks) OS
developers have increasingly added "Quality of Service" APIs to thread
scheduling, giving users a way to tell the OS what type of work the
thread is expected to do.
In the case of Apple they have a more extensive selection of
service levels. Unlike what we currently see from Windows, the API there
is actually much broader than just High, 'Default' and Eco, and covers
more concrete uses like "Utility", "Background", "User-initiated",
defined as -1 (Default), and a range from 0x09 (Background)
to 0x21 (User-interactive)[0].
Currently there is no equivalent API for Linux though there is some push
from various interested parties to add one (e.g. [1]).
As mentioned, on Windows there is really only 3 levels, though it's
defined as "do/don't throttle this thread" and "do as you wish".
For Android I cannot really find anything equivalent beyond the thread
priority.
Discussed here:
https://lists.qt-project.org/pipermail/development/2024-September/045694.html
[0] https://developer.apple.com/documentation/foundation/qualityofservice
[1] https://www.youtube.com/watch?v=RfgPWpTwTQo (Linux Plumbers Conference)
Fixes: QTBUG-93946
Change-Id: Iabeaa7b61cec0bebd5c6a4bcf75a8e60dc0348dc
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
2023-09-19 18:13:27 +00:00
|
|
|
#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
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2021-09-27 11:54:41 +00:00
|
|
|
// threadId is set in QThread::start()
|
2024-10-25 00:52:52 +00:00
|
|
|
Q_ASSERT(data->threadId.loadRelaxed() == QThread::currentThreadId());
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2016-08-07 18:58:54 +00:00
|
|
|
data->ref();
|
|
|
|
data->quitNow = thr->d_func()->exited;
|
|
|
|
}
|
2012-02-03 12:41:47 +00:00
|
|
|
|
2018-07-06 20:33:11 +00:00
|
|
|
data->ensureEventDispatcher();
|
2020-12-09 15:31:41 +00:00
|
|
|
data->eventDispatcher.loadRelaxed()->startingUp();
|
2016-08-07 18:58:54 +00:00
|
|
|
|
2023-03-17 16:53:51 +00:00
|
|
|
#if (defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_QNX))
|
2016-08-07 18:58:54 +00:00
|
|
|
{
|
2018-01-31 18:08:06 +00:00
|
|
|
// 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.
|
2022-01-10 17:10:48 +00:00
|
|
|
if (Q_LIKELY(thr->d_func()->objectName.isEmpty()))
|
2018-01-31 18:08:06 +00:00
|
|
|
setCurrentThreadName(thr->metaObject()->className());
|
2016-08-07 18:58:54 +00:00
|
|
|
else
|
2022-01-10 17:10:48 +00:00
|
|
|
setCurrentThreadName(std::exchange(thr->d_func()->objectName, {}).toLocal8Bit());
|
2016-08-07 18:58:54 +00:00
|
|
|
}
|
2012-02-03 12:41:47 +00:00
|
|
|
#endif
|
|
|
|
|
2016-08-07 18:58:54 +00:00
|
|
|
emit thr->started(QThread::QPrivateSignal());
|
2023-10-12 15:23:35 +00:00
|
|
|
#ifdef PTHREAD_CANCEL_DISABLE
|
2019-11-22 13:46:58 +00:00
|
|
|
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr);
|
2016-08-07 18:58:54 +00:00
|
|
|
pthread_testcancel();
|
2012-05-01 19:22:20 +00:00
|
|
|
#endif
|
2016-08-07 18:58:54 +00:00
|
|
|
thr->run();
|
2021-12-22 14:01:27 +00:00
|
|
|
});
|
2011-04-27 10:05:43 +00:00
|
|
|
|
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);
|
2019-11-22 13:46:58 +00:00
|
|
|
return nullptr;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2024-10-15 16:41:05 +00:00
|
|
|
void QThreadPrivate::finish()
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2021-12-22 14:01:27 +00:00
|
|
|
terminate_on_exception([&] {
|
2024-10-15 16:41:05 +00:00
|
|
|
QThreadPrivate *d = this;
|
|
|
|
QThread *thr = q_func();
|
2016-08-07 18:58:54 +00:00
|
|
|
|
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
|
|
|
|
|
2016-08-07 18:58:54 +00:00
|
|
|
QMutexLocker locker(&d->mutex);
|
|
|
|
|
2024-05-01 01:49:27 +00:00
|
|
|
d->threadState = QThreadPrivate::Finishing;
|
2011-04-27 10:05:43 +00:00
|
|
|
locker.unlock();
|
2016-08-07 18:58:54 +00:00
|
|
|
emit thr->finished(QThread::QPrivateSignal());
|
2019-11-22 13:46:58 +00:00
|
|
|
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
|
|
|
|
|
|
|
void *data = &d->data->tls;
|
2016-08-07 18:58:54 +00:00
|
|
|
QThreadStorageData::finish((void **)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
|
|
|
});
|
|
|
|
|
|
|
|
if constexpr (QT_CONFIG(broken_threadlocal_dtors))
|
2024-10-15 16:41:05 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2024-10-15 16:41:05 +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([&] {
|
2024-10-15 16:41:05 +00:00
|
|
|
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;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2019-06-10 09:08:29 +00:00
|
|
|
QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
|
2016-08-07 18:58:54 +00:00
|
|
|
if (eventDispatcher) {
|
2019-11-22 13:46:58 +00:00
|
|
|
d->data->eventDispatcher = nullptr;
|
2016-08-07 18:58:54 +00:00
|
|
|
locker.unlock();
|
|
|
|
eventDispatcher->closingDown();
|
|
|
|
delete eventDispatcher;
|
|
|
|
locker.relock();
|
|
|
|
}
|
|
|
|
|
2024-04-30 17:12:34 +00:00
|
|
|
d->interruptionRequested.store(false, std::memory_order_relaxed);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
d->wakeAll();
|
2021-12-22 14:01:27 +00:00
|
|
|
});
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
** QThread
|
|
|
|
*************************************************************************/
|
|
|
|
|
Read a unique thread identifier from CPU registers
This is, depending on the implementation of pthread, significantly
cheaper than using a pthread library call.
Even if we don't know the assembler for an architecture, taking
the address of the thread_local variable is still faster.
As QThread::currentThreadId() is documented to be used internally
and not meant for application code, we don't have to care about
what exact value we return. Internally, we use it only to compare
thread IDs for equality, which this implementation is sufficient
for, even if a thread ID is re-used when one of the threads
terminate and a new thread starts (since the other thread is still
executing code). Besides, pthread_self documents [0] that a thread
ID may be reused, and that the returned pthread_t cannot be
portably compared using operator==(); using pthread_equal would
require adding a Qt thread-ID type that implements this correctly,
and would make things even slower.
[0] http://man7.org/linux/man-pages/man3/pthread_self.3.html
Change-Id: Id08e79b9b9c88976561f7cd36c66d43771fc4f24
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2019-08-19 11:33:31 +00:00
|
|
|
/*
|
2020-05-29 05:50:13 +00:00
|
|
|
CI tests fails on ARM architectures if we try to use the assembler, so
|
Read a unique thread identifier from CPU registers
This is, depending on the implementation of pthread, significantly
cheaper than using a pthread library call.
Even if we don't know the assembler for an architecture, taking
the address of the thread_local variable is still faster.
As QThread::currentThreadId() is documented to be used internally
and not meant for application code, we don't have to care about
what exact value we return. Internally, we use it only to compare
thread IDs for equality, which this implementation is sufficient
for, even if a thread ID is re-used when one of the threads
terminate and a new thread starts (since the other thread is still
executing code). Besides, pthread_self documents [0] that a thread
ID may be reused, and that the returned pthread_t cannot be
portably compared using operator==(); using pthread_equal would
require adding a Qt thread-ID type that implements this correctly,
and would make things even slower.
[0] http://man7.org/linux/man-pages/man3/pthread_self.3.html
Change-Id: Id08e79b9b9c88976561f7cd36c66d43771fc4f24
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2019-08-19 11:33:31 +00:00
|
|
|
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
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2016-06-09 16:13:08 +00:00
|
|
|
return to_HANDLE(pthread_self());
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(QT_LINUXBASE) && !defined(_SC_NPROCESSORS_ONLN)
|
|
|
|
// LSB doesn't define _SC_NPROCESSORS_ONLN.
|
|
|
|
# define _SC_NPROCESSORS_ONLN 84
|
|
|
|
#endif
|
|
|
|
|
2019-04-07 10:32:51 +00:00
|
|
|
#ifdef Q_OS_WASM
|
|
|
|
int QThreadPrivate::idealThreadCount = 1;
|
|
|
|
#endif
|
|
|
|
|
2019-04-02 08:54:54 +00:00
|
|
|
int QThread::idealThreadCount() noexcept
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2014-12-18 02:47:52 +00:00
|
|
|
int cores = 1;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2012-12-22 04:47:08 +00:00
|
|
|
#if defined(Q_OS_HPUX)
|
2011-04-27 10:05:43 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2021-12-02 19:58:33 +00:00
|
|
|
#elif (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD)
|
2022-06-21 21:03:25 +00:00
|
|
|
# if defined(Q_OS_FREEBSD) && !defined(CPU_COUNT_S)
|
2021-12-02 19:58:33 +00:00
|
|
|
# define CPU_COUNT_S(setsize, cpusetp) ((int)BIT_COUNT(setsize, cpusetp))
|
|
|
|
// match the Linux API for simplicity
|
|
|
|
using cpu_set_t = cpuset_t;
|
|
|
|
auto sched_getaffinity = [](pid_t, size_t cpusetsize, cpu_set_t *mask) {
|
|
|
|
return cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, cpusetsize, mask);
|
|
|
|
};
|
|
|
|
# endif
|
|
|
|
|
|
|
|
// get the number of threads we're assigned, not the total in the system
|
|
|
|
QVarLengthArray<cpu_set_t, 1> cpuset(1);
|
|
|
|
int size = 1;
|
|
|
|
if (Q_UNLIKELY(sched_getaffinity(0, sizeof(cpu_set_t), cpuset.data()) < 0)) {
|
|
|
|
for (size = 2; size <= 4; size *= 2) {
|
|
|
|
cpuset.resize(size);
|
|
|
|
if (sched_getaffinity(0, sizeof(cpu_set_t) * size, cpuset.data()) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (size > 4)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
cores = CPU_COUNT_S(sizeof(cpu_set_t) * size, cpuset.data());
|
2011-04-27 10:05:43 +00:00
|
|
|
#elif defined(Q_OS_BSD4)
|
2021-12-02 19:58:33 +00:00
|
|
|
// OpenBSD, NetBSD, BSD/OS, Darwin (macOS, iOS, etc.)
|
2011-04-27 10:05:43 +00:00
|
|
|
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)
|
2013-01-30 08:20:09 +00:00
|
|
|
#if (__INTEGRITY_MAJOR_VERSION >= 10)
|
|
|
|
// Integrity V10+ does support multicore CPUs
|
|
|
|
Value processorCount;
|
|
|
|
if (GetProcessorCount(CurrentTask(), &processorCount) == 0)
|
|
|
|
cores = processorCount;
|
|
|
|
else
|
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
// 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++;
|
|
|
|
}
|
|
|
|
}
|
2018-12-12 09:00:05 +00:00
|
|
|
#elif defined(Q_OS_WASM)
|
2019-04-07 10:32:51 +00:00
|
|
|
cores = QThreadPrivate::idealThreadCount;
|
2011-04-27 10:05:43 +00:00
|
|
|
#else
|
2021-12-02 19:58:33 +00:00
|
|
|
// the rest: Solaris, AIX, Tru64
|
2011-04-27 10:05:43 +00:00
|
|
|
cores = (int)sysconf(_SC_NPROCESSORS_ONLN);
|
2014-12-17 00:22:10 +00:00
|
|
|
if (cores == -1)
|
|
|
|
return 1;
|
2014-12-18 02:47:52 +00:00
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
return cores;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QThread::yieldCurrentThread()
|
|
|
|
{
|
|
|
|
sched_yield();
|
|
|
|
}
|
|
|
|
|
2016-12-29 16:11:24 +00:00
|
|
|
#endif // QT_CONFIG(thread)
|
2017-01-02 16:23:14 +00:00
|
|
|
|
2023-02-23 02:34:14 +00:00
|
|
|
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;
|
2023-11-07 19:20:01 +00:00
|
|
|
QT_EINTR_LOOP(r, nanosleep(&amount, &amount));
|
2023-02-23 02:34:14 +00:00
|
|
|
}
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
void QThread::sleep(unsigned long secs)
|
|
|
|
{
|
2023-02-27 19:36:45 +00:00
|
|
|
sleep(std::chrono::seconds{secs});
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QThread::msleep(unsigned long msecs)
|
|
|
|
{
|
2023-02-27 19:36:45 +00:00
|
|
|
sleep(std::chrono::milliseconds{msecs});
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QThread::usleep(unsigned long usecs)
|
|
|
|
{
|
2023-02-27 19:36:45 +00:00
|
|
|
sleep(std::chrono::microseconds{usecs});
|
|
|
|
}
|
|
|
|
|
|
|
|
void QThread::sleep(std::chrono::nanoseconds nsec)
|
|
|
|
{
|
|
|
|
qt_nanosleep(durationToTimespec(nsec));
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 16:11:24 +00:00
|
|
|
#if QT_CONFIG(thread)
|
2017-01-02 16:23:14 +00:00
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
#ifdef QT_HAS_THREAD_PRIORITY_SCHEDULING
|
2017-10-24 22:05:49 +00:00
|
|
|
#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
|
2011-04-27 10:05:43 +00:00
|
|
|
// 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;
|
|
|
|
|
2013-01-09 11:12:50 +00:00
|
|
|
int prio_min;
|
|
|
|
int prio_max;
|
|
|
|
#if defined(Q_OS_VXWORKS) && defined(VXWORKS_DKM)
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
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
|
2017-10-24 22:05:49 +00:00
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
void QThread::start(Priority priority)
|
|
|
|
{
|
|
|
|
Q_D(QThread);
|
|
|
|
QMutexLocker locker(&d->mutex);
|
|
|
|
|
2024-05-01 01:49:27 +00:00
|
|
|
if (d->threadState == QThreadPrivate::Finishing)
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
d->wait(locker, QDeadlineTimer::Forever);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2024-05-01 01:49:27 +00:00
|
|
|
if (d->threadState == QThreadPrivate::Running)
|
2011-04-27 10:05:43 +00:00
|
|
|
return;
|
|
|
|
|
2024-05-01 01:49:27 +00:00
|
|
|
d->threadState = QThreadPrivate::Running;
|
2011-04-27 10:05:43 +00:00
|
|
|
d->returnCode = 0;
|
|
|
|
d->exited = false;
|
2024-04-30 17:12:34 +00:00
|
|
|
d->interruptionRequested.store(false, std::memory_order_relaxed);
|
2024-07-09 14:48:01 +00:00
|
|
|
d->terminated = false;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
pthread_attr_t attr;
|
|
|
|
pthread_attr_init(&attr);
|
2024-11-02 17:28:12 +00:00
|
|
|
if constexpr (!UsingPThreadTimedJoin)
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
Add thread Quality of Service API
With heterogenous CPU topology (and earlier, per-core clocks) OS
developers have increasingly added "Quality of Service" APIs to thread
scheduling, giving users a way to tell the OS what type of work the
thread is expected to do.
In the case of Apple they have a more extensive selection of
service levels. Unlike what we currently see from Windows, the API there
is actually much broader than just High, 'Default' and Eco, and covers
more concrete uses like "Utility", "Background", "User-initiated",
defined as -1 (Default), and a range from 0x09 (Background)
to 0x21 (User-interactive)[0].
Currently there is no equivalent API for Linux though there is some push
from various interested parties to add one (e.g. [1]).
As mentioned, on Windows there is really only 3 levels, though it's
defined as "do/don't throttle this thread" and "do as you wish".
For Android I cannot really find anything equivalent beyond the thread
priority.
Discussed here:
https://lists.qt-project.org/pipermail/development/2024-September/045694.html
[0] https://developer.apple.com/documentation/foundation/qualityofservice
[1] https://www.youtube.com/watch?v=RfgPWpTwTQo (Linux Plumbers Conference)
Fixes: QTBUG-93946
Change-Id: Iabeaa7b61cec0bebd5c6a4bcf75a8e60dc0348dc
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
2023-09-19 18:13:27 +00:00
|
|
|
#ifdef Q_OS_DARWIN
|
|
|
|
if (d->serviceLevel != QThread::QualityOfService::Auto)
|
|
|
|
pthread_attr_set_qos_class_np(&attr, d->nativeQualityOfServiceClass(), 0);
|
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
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);
|
2021-12-16 12:50:21 +00:00
|
|
|
d->priority = qToUnderlying(priority) | ThreadPriorityResetFlag;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
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) {
|
2019-05-29 16:15:45 +00:00
|
|
|
qErrnoWarning(code, "QThread::start: Thread stack size error");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// we failed to set the stacksize, and as the documentation states,
|
|
|
|
// the thread will fail to run...
|
2024-05-01 01:49:27 +00:00
|
|
|
d->threadState = QThreadPrivate::NotStarted;
|
2011-04-27 10:05:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-03 12:29:23 +00:00
|
|
|
#ifdef Q_OS_INTEGRITY
|
|
|
|
if (Q_LIKELY(objectName().isEmpty()))
|
|
|
|
pthread_attr_setthreadname(&attr, metaObject()->className());
|
|
|
|
else
|
|
|
|
pthread_attr_setthreadname(&attr, objectName().toLocal8Bit());
|
2022-01-10 17:10:48 +00:00
|
|
|
#else
|
2022-01-20 12:19:28 +00:00
|
|
|
// avoid interacting with the binding system
|
|
|
|
d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
|
|
|
|
: QString();
|
2019-06-03 12:29:23 +00:00
|
|
|
#endif
|
2022-01-10 17:10:48 +00:00
|
|
|
|
2016-06-09 16:13:08 +00:00
|
|
|
pthread_t threadId;
|
|
|
|
int code = pthread_create(&threadId, &attr, QThreadPrivate::start, this);
|
2011-04-27 10:05:43 +00:00
|
|
|
if (code == EPERM) {
|
|
|
|
// caller does not have permission to set the scheduling
|
|
|
|
// parameters/policy
|
2012-05-01 19:22:20 +00:00
|
|
|
#if defined(QT_HAS_THREAD_PRIORITY_SCHEDULING)
|
2011-04-27 10:05:43 +00:00
|
|
|
pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED);
|
2012-05-01 19:22:20 +00:00
|
|
|
#endif
|
2016-06-09 16:13:08 +00:00
|
|
|
code = pthread_create(&threadId, &attr, QThreadPrivate::start, this);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
2019-06-10 09:08:29 +00:00
|
|
|
d->data->threadId.storeRelaxed(to_HANDLE(threadId));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
pthread_attr_destroy(&attr);
|
|
|
|
|
|
|
|
if (code) {
|
2019-05-29 16:15:45 +00:00
|
|
|
qErrnoWarning(code, "QThread::start: Thread creation error");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2024-05-01 01:49:27 +00:00
|
|
|
d->threadState = QThreadPrivate::NotStarted;
|
2019-06-10 09:08:29 +00:00
|
|
|
d->data->threadId.storeRelaxed(nullptr);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QThread::terminate()
|
|
|
|
{
|
2013-03-04 09:16:42 +00:00
|
|
|
#if !defined(Q_OS_ANDROID)
|
2011-04-27 10:05:43 +00:00
|
|
|
Q_D(QThread);
|
|
|
|
QMutexLocker locker(&d->mutex);
|
|
|
|
|
2024-01-11 10:09:26 +00:00
|
|
|
const auto id = d->data->threadId.loadRelaxed();
|
|
|
|
if (!id)
|
2011-04-27 10:05:43 +00:00
|
|
|
return;
|
|
|
|
|
2024-07-09 14:48:01 +00:00
|
|
|
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();
|
2024-01-11 10:09:26 +00:00
|
|
|
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();
|
2024-07-09 14:48:01 +00:00
|
|
|
d->terminated = false; // allow to try again
|
2019-05-29 16:15:45 +00:00
|
|
|
qErrnoWarning(code, "QThread::start: Thread termination error");
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
2012-05-01 19:22:20 +00:00
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
static void wakeAllInternal(QThreadPrivate *d)
|
|
|
|
{
|
|
|
|
d->threadState = QThreadPrivate::Finished;
|
|
|
|
if (d->waiters)
|
|
|
|
d->thread_done.wakeAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void QThreadPrivate::wakeAll()
|
|
|
|
{
|
2024-11-02 17:28:12 +00:00
|
|
|
if (data->isAdopted || !UsingPThreadTimedJoin)
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
wakeAllInternal(this);
|
|
|
|
}
|
|
|
|
|
2024-10-23 17:11:30 +00:00
|
|
|
bool QThreadPrivate::wait(QMutexLocker<QMutex> &locker, QDeadlineTimer deadline)
|
|
|
|
{
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
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());
|
2024-11-02 17:28:12 +00:00
|
|
|
if constexpr (QT_CONFIG(pthread_clockjoin))
|
|
|
|
r = pthread_clockjoin_np(thrId, nullptr, SteadyClockClockId, pts);
|
|
|
|
else
|
|
|
|
r = pthread_timedjoin_np(thrId, nullptr, pts);
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
Q_ASSERT(r == 0 || r == ETIMEDOUT);
|
|
|
|
pthread_cleanup_pop(1);
|
|
|
|
|
|
|
|
Q_ASSERT(waiters >= 0);
|
|
|
|
return r != ETIMEDOUT;
|
|
|
|
};
|
2024-10-23 17:11:30 +00:00
|
|
|
Q_ASSERT(threadState != QThreadPrivate::Finished);
|
|
|
|
Q_ASSERT(locker.isLocked());
|
2011-04-27 10:05:43 +00:00
|
|
|
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
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 (;;) {
|
2024-11-02 17:28:12 +00:00
|
|
|
if (UsingPThreadTimedJoin && mustJoin && !data->isAdopted) {
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
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;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
pthread_cleanup_pop(1);
|
2021-09-24 11:14:05 +00:00
|
|
|
|
QThread/Unix: implement joining of the launched thread, if we can
This is only implemented for OSes that provide a way to perform a timed
join. For all other OSes, we stick to the previous implementation, which
as the comment indicates, may run for arbitrarily long time after wait()
has returned, running user code (e.g., pthread_setspecific() and
thread_local destructors).
Instead, if we perform the joining, we are assured by pthread and the OS
that the thread has exited and no user code remains running.
Unfortunately, this only applies to non-adopted threads, because we
can't pthread_join() a thread we didn't start.
As of this writing, this code only applies to Linux/glibc. MUSL, Bionic
and several BSDs have pthread_timedjoin, but that takes a CLOCK_REALTIME
absolute time, which means it's subject to time jumps, while
QWaitCondition can sometimes use the monotonic clock in those systems.
Change-Id: I692e24d7411742447e10fffd650fe84f6a9cdedd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-10-23 17:17:26 +00:00
|
|
|
return result;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QThread::setTerminationEnabled(bool enabled)
|
|
|
|
{
|
|
|
|
QThread *thr = currentThread();
|
2019-11-22 13:46:58 +00:00
|
|
|
Q_ASSERT_X(thr != nullptr, "QThread::setTerminationEnabled()",
|
2011-04-27 10:05:43 +00:00
|
|
|
"Current thread was not started with QThread.");
|
|
|
|
|
2020-06-27 12:18:09 +00:00
|
|
|
Q_UNUSED(thr);
|
2013-03-04 09:16:42 +00:00
|
|
|
#if defined(Q_OS_ANDROID)
|
2012-05-01 19:22:20 +00:00
|
|
|
Q_UNUSED(enabled);
|
|
|
|
#else
|
2019-11-22 13:46:58 +00:00
|
|
|
pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, nullptr);
|
2011-04-27 10:05:43 +00:00
|
|
|
if (enabled)
|
|
|
|
pthread_testcancel();
|
2012-05-01 19:22:20 +00:00
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
2013-03-15 18:48:59 +00:00
|
|
|
// Caller must lock the mutex
|
|
|
|
void QThreadPrivate::setPriority(QThread::Priority threadPriority)
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2013-03-15 18:48:59 +00:00
|
|
|
priority = threadPriority;
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// copied from start() with a few modifications:
|
|
|
|
|
|
|
|
#ifdef QT_HAS_THREAD_PRIORITY_SCHEDULING
|
|
|
|
int sched_policy;
|
|
|
|
sched_param param;
|
|
|
|
|
2019-06-10 09:08:29 +00:00
|
|
|
if (pthread_getschedparam(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()), &sched_policy, ¶m) != 0) {
|
2011-04-27 10:05:43 +00:00
|
|
|
// 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;
|
2019-06-10 09:08:29 +00:00
|
|
|
int status = pthread_setschedparam(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()), sched_policy, ¶m);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
# 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
|
2019-06-10 09:08:29 +00:00
|
|
|
pthread_getschedparam(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()), &sched_policy, ¶m);
|
2011-04-27 10:05:43 +00:00
|
|
|
param.sched_priority = sched_get_priority_min(sched_policy);
|
2019-06-10 09:08:29 +00:00
|
|
|
pthread_setschedparam(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()), sched_policy, ¶m);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
# else
|
|
|
|
Q_UNUSED(status);
|
|
|
|
# endif // SCHED_IDLE
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
Add thread Quality of Service API
With heterogenous CPU topology (and earlier, per-core clocks) OS
developers have increasingly added "Quality of Service" APIs to thread
scheduling, giving users a way to tell the OS what type of work the
thread is expected to do.
In the case of Apple they have a more extensive selection of
service levels. Unlike what we currently see from Windows, the API there
is actually much broader than just High, 'Default' and Eco, and covers
more concrete uses like "Utility", "Background", "User-initiated",
defined as -1 (Default), and a range from 0x09 (Background)
to 0x21 (User-interactive)[0].
Currently there is no equivalent API for Linux though there is some push
from various interested parties to add one (e.g. [1]).
As mentioned, on Windows there is really only 3 levels, though it's
defined as "do/don't throttle this thread" and "do as you wish".
For Android I cannot really find anything equivalent beyond the thread
priority.
Discussed here:
https://lists.qt-project.org/pipermail/development/2024-September/045694.html
[0] https://developer.apple.com/documentation/foundation/qualityofservice
[1] https://www.youtube.com/watch?v=RfgPWpTwTQo (Linux Plumbers Conference)
Fixes: QTBUG-93946
Change-Id: Iabeaa7b61cec0bebd5c6a4bcf75a8e60dc0348dc
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
2023-09-19 18:13:27 +00:00
|
|
|
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
|
|
|
|
|
2016-12-29 16:11:24 +00:00
|
|
|
#endif // QT_CONFIG(thread)
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|