diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 789115e404e..028d85acc01 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -368,14 +368,16 @@ Q_CONSTINIT uint QCoreApplicationPrivate::attribs = (1 << Qt::AA_SynthesizeMouseForUnhandledTouchEvents) | (1 << Qt::AA_SynthesizeMouseForUnhandledTabletEvents); -struct QCoreApplicationData { +struct QCoreApplicationData +{ QCoreApplicationData() noexcept { applicationNameSet = false; applicationVersionSet = false; } ~QCoreApplicationData() { -#ifndef QT_NO_QOBJECT +#if !defined(QT_NO_QOBJECT) && defined(Q_OS_WIN) // cleanup the QAdoptedThread created for the main() thread + // (for Unix systems, see qthread_unix.cpp:set_thread_data()) if (auto *t = QCoreApplicationPrivate::theMainThread.loadAcquire()) { QThreadData *data = QThreadData::get2(t); data->deref(); // deletes the data and the adopted thread diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index ecfe139bba2..c1f212b1425 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -165,12 +165,20 @@ static QThreadData *get_thread_data() return currentThreadData; } +#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 + static void set_thread_data(QThreadData *data) { - // Only activate the late cleanup for auxiliary threads. We can't use - // QThread::isMainThread() here because theMainThreadId will not have been - // set yet. - if (data && QCoreApplicationPrivate::theMainThreadId.loadAcquire()) { + if (data) { if constexpr (QT_CONFIG(broken_threadlocal_dtors)) { static pthread_key_t tls_key; struct TlsKey { @@ -851,7 +859,6 @@ void QThread::terminate() static void wakeAllInternal(QThreadPrivate *d) { d->threadState = QThreadPrivate::Finished; - d->data->threadId.storeRelaxed(nullptr); if (d->waiters) d->thread_done.wakeAll(); } @@ -867,16 +874,16 @@ bool QThread::wait(QDeadlineTimer deadline) Q_D(QThread); QMutexLocker locker(&d->mutex); - if (isCurrentThread()) { - qWarning("QThread::wait: Thread tried to wait on itself"); - return false; - } - if (d->threadState == QThreadPrivate::NotStarted) return true; if (d->threadState == QThreadPrivate::Finished) return true; + if (isCurrentThread()) { + qWarning("QThread::wait: Thread tried to wait on itself"); + return false; + } + return d->wait(locker, deadline); } @@ -949,7 +956,6 @@ bool QThreadPrivate::wait(QMutexLocker &locker, QDeadlineTimer deadline) } pthread_cleanup_pop(1); - Q_ASSERT(!result || data->threadId.loadRelaxed() == nullptr); return result; } diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp index 5dc445d44f2..a031e6a54d0 100644 --- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp +++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp @@ -1065,9 +1065,22 @@ static void createQObjectOnDestruction() // QThread) after the last QObject has been destroyed (especially after // QCoreApplication has). -#if !defined(QT_QGUIAPPLICATIONTEST) && !defined(Q_OS_WIN) && !defined(Q_OS_VXWORKS) - // QCoreApplicationData's global static destructor has run and cleaned up - // the QAdoptedThread. +#if defined(QT_QGUIAPPLICATIONTEST) + // If we've linked to QtGui, we make no representations about there being + // global static (not Q_GLOBAL_STATIC) variables that are QObject. +#elif QT_CONFIG(broken_threadlocal_dtors) + // With broken thread-local destructors, we cannot guarantee the ordering + // between thread_local destructors and static-lifetime destructors (hence + // why they're broken). + // + // On Unix systems, we use a Q_DESTRUCTOR_FUNCTION in qthread_unix.cpp to + // work around the issue, but that means it cannot have run yet. + // + // This variable is set on Windows too, even though the nature of the + // problem is different. +#else + // The thread_local destructor in qthread_unix.cpp has run so the + // QAdoptedThread must have been cleaned up. if (theMainThreadIsSet()) qFatal("theMainThreadIsSet() returned true; some QObject must have leaked"); #endif