diff options
Diffstat (limited to 'src/corelib')
| -rw-r--r-- | src/corelib/Qt6AndroidGradleHelpers.cmake | 4 | ||||
| -rw-r--r-- | src/corelib/Qt6AndroidMacros.cmake | 3 | ||||
| -rw-r--r-- | src/corelib/kernel/qcoreapplication.cpp | 16 | ||||
| -rw-r--r-- | src/corelib/thread/qthread_p.h | 10 | ||||
| -rw-r--r-- | src/corelib/thread/qthread_unix.cpp | 219 | ||||
| -rw-r--r-- | src/corelib/thread/qthread_win.cpp | 5 |
6 files changed, 141 insertions, 116 deletions
diff --git a/src/corelib/Qt6AndroidGradleHelpers.cmake b/src/corelib/Qt6AndroidGradleHelpers.cmake index fc8e009b9da..136a61abbcd 100644 --- a/src/corelib/Qt6AndroidGradleHelpers.cmake +++ b/src/corelib/Qt6AndroidGradleHelpers.cmake @@ -6,7 +6,7 @@ function(_qt_internal_android_get_template_path out_var target template_name) if(template_name STREQUAL "") message(FATAL_ERROR "Template name is empty." - " This is the Qt issue, please report a bug at https://bugreports.qt.io.") + " This is a Qt issue, please report a bug at https://bugreports.qt.io.") endif() _qt_internal_android_template_dir(template_directory) @@ -36,7 +36,7 @@ function(_qt_internal_android_get_template_path out_var target template_name) if(template_path STREQUAL "") message(FATAL_ERROR "'${template_name}' is not found." - " This is the Qt issue, please report a bug at https://bugreports.qt.io.") + " This is a Qt issue, please report a bug at https://bugreports.qt.io.") endif() set(${out_var} "${template_path}" PARENT_SCOPE) diff --git a/src/corelib/Qt6AndroidMacros.cmake b/src/corelib/Qt6AndroidMacros.cmake index be362ba1925..ddc531a89f4 100644 --- a/src/corelib/Qt6AndroidMacros.cmake +++ b/src/corelib/Qt6AndroidMacros.cmake @@ -1790,10 +1790,11 @@ function(_qt_internal_android_app_runner_arguments target out_runner_path out_ar set(${out_runner_path} "${runner_dir}/qt-android-runner.py" PARENT_SCOPE) _qt_internal_android_get_target_android_build_dir(android_build_dir ${target}) + _qt_internal_android_get_target_deployment_dir(android_deployment_dir ${target}) _qt_internal_android_get_platform_tools_path(platform_tools) set(${out_arguments} "--adb" "${platform_tools}/adb" - "--build-path" "${android_build_dir}" + "--build-path" "${android_deployment_dir}" "--apk" "${android_build_dir}/${target}.apk" PARENT_SCOPE ) diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index afc85fe36fb..19f5596880a 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -2913,7 +2913,6 @@ void QCoreApplication::requestPermissionImpl(const QPermission &requestedPermiss void *args[] = { nullptr, const_cast<QPermission *>(&permission) }; slotObject->call(const_cast<QObject *>(context.data()), args); } - deleteLater(); } private: @@ -2921,9 +2920,11 @@ void QCoreApplication::requestPermissionImpl(const QPermission &requestedPermiss QPointer<const QObject> context; }; - PermissionReceiver *receiver = new PermissionReceiver(std::move(slotObj), context); + // ### use unique_ptr once PermissionCallback is a move_only function + auto receiver = std::make_shared<PermissionReceiver>(std::move(slotObj), context); - QPermissions::Private::requestPermission(requestedPermission, [=](Qt::PermissionStatus status) { + QPermissions::Private::requestPermission(requestedPermission, + [=, receiver = std::move(receiver)](Qt::PermissionStatus status) mutable { if (status == Qt::PermissionStatus::Undetermined) { Q_ASSERT_X(false, "QPermission", "Internal error: requestPermission() should never return Undetermined"); @@ -2933,10 +2934,11 @@ void QCoreApplication::requestPermissionImpl(const QPermission &requestedPermiss if (QCoreApplication::self) { QPermission permission = requestedPermission; permission.m_status = status; - QMetaObject::invokeMethod(receiver, - &PermissionReceiver::finalizePermissionRequest, - Qt::QueuedConnection, - permission); + auto receiverObject = receiver.get(); + QMetaObject::invokeMethod(receiverObject, + [receiver = std::move(receiver), permission] { + receiver->finalizePermissionRequest(permission); + }, Qt::QueuedConnection); } }); } diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index 51d893a051d..41c36e99d3b 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -265,10 +265,16 @@ public: QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *obj); void removeObjectWithPendingBindingStatusChange(QObject *obj); -#ifndef Q_OS_INTEGRITY private: +#ifdef Q_OS_INTEGRITY + // On INTEGRITY we set the thread name before starting it, so just fake a string + struct FakeString { + bool isEmpty() const { return true; } + const char *toLocal8Bit() const { return nullptr; } + } objectName; +#else // Used in QThread(Private)::start to avoid racy access to QObject::objectName, - // unset afterwards. On INTEGRITY we set the thread name before starting it. + // unset afterwards. QString objectName; #endif }; diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index bdab8dfb028..f53605367d7 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -24,10 +24,6 @@ # endif #endif -#ifdef __GLIBCXX__ -#include <cxxabi.h> -#endif - #include <sched.h> #include <errno.h> #if __has_include(<pthread_np.h>) @@ -344,59 +340,95 @@ QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *dat #if QT_CONFIG(thread) -#if (defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_QNX)) -static void setCurrentThreadName(const char *name) +template <typename String> +static void setCurrentThreadName(QThread *thr, String &objectName) { -# if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE) - prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); + auto setit = [](const char *name) { +# if defined(Q_OS_LINUX) + prctl(PR_SET_NAME, name); # elif defined(Q_OS_DARWIN) - pthread_setname_np(name); + pthread_setname_np(name); # elif defined(Q_OS_QNX) - pthread_setname_np(pthread_self(), name); + pthread_setname_np(pthread_self(), name); +# else + Q_UNUSED(name) # endif + }; + if (Q_LIKELY(objectName.isEmpty())) + setit(thr->metaObject()->className()); + else + setit(std::exchange(objectName, {}).toLocal8Bit()); } -#endif -namespace { -#if defined(__GLIBCXX__) && !defined(QT_NO_EXCEPTIONS) -template <typename T> -void terminate_on_exception(T &&t) +// Handling of exceptions and cancellations for start(), finish() and cleanup() +// +// These routines expect that the user code throw no exceptions. Exiting +// start() with an exception should cause std::terminate to be called. Thread +// cancellations are allowed: if one is detected, the implementation is +// expected to cleanly call QThreadPrivate::finish(), emit the necessary +// signals and notifications, and clean up after itself. [Note there's a small +// race between QThread::start() returning and QThreadPrivate::start() turning +// cancellations off, during which time no finish() is called.] +// +// These routines implement application termination by unexpected exceptions by +// simply not having any try/catch block at all. As start() is called directly +// from the C library's PThread runtime, there should be no active C++ +// try/catch block (### if we ever change this to std::thread, the assumption +// needs to be rechecked, though both libc++ and libstdc++ at the time of +// writing are try/catch-free). [except.handle]/8 says: +// +// > If no matching handler is found, the function std::terminate is invoked; +// > whether or not the stack is unwound before this invocation of std::terminate +// > is implementation-defined. +// +// Both major implementations of Unix C++ Standard Libraries terminate without +// unwinding, which is useful to detect the unhandled exception in post-mortem +// debugging. This code adds no try/catch to retain that ability. +// +// Because of that, we could have marked these functions noexcept and ignored +// exception safety. We don't because of PThread cancellations. The GNU libc +// implements PThread cancellations using stack unwinding, so a cancellation +// *will* unwind the stack and *will* execute our C++ destructors, unlike +// exceptions. Therefore, our code in start() must be exception-safe after we +// turn cancellations back on, and until we turn them off again in finish(). +// +// Everywhere else, PThread cancellations are handled without unwinding the +// stack. + +static void setCancellationEnabled(bool enable) { - try { - std::forward<T>(t)(); - } catch (abi::__forced_unwind &) { - // 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. - throw; - } catch (...) { - std::terminate(); +#ifdef PTHREAD_CANCEL_DISABLE + if (enable) { + // may unwind the stack, see above + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr); + pthread_testcancel(); + } else { + // this doesn't unwind the stack + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr); } -} #else -template <typename T> -void terminate_on_exception(T &&t) noexcept -{ - std::forward<T>(t)(); + Q_UNUSED(enable) +#endif } -#endif // defined(__GLIBCXX__) && !defined(QT_NO_EXCEPTIONS) -} // unnamed namespace void *QThreadPrivate::start(void *arg) { -#ifdef PTHREAD_CANCEL_DISABLE - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr); -#endif + setCancellationEnabled(false); + QThread *thr = reinterpret_cast<QThread *>(arg); QThreadData *data = QThreadData::get2(thr); - // If a QThread is restarted, reuse the QBindingStatus, too - data->reuseBindingStatusForNewNativeThread(); // this ensures the thread-local is created as early as possible set_thread_data(data); + // If a QThread is restarted, reuse the QBindingStatus, too + data->reuseBindingStatusForNewNativeThread(); + pthread_cleanup_push([](void *arg) { static_cast<QThread *>(arg)->d_func()->finish(); }, arg); - terminate_on_exception([&] { + { // pthread cancellation protection + + // The functions called in this block do not usually throw (but have + // qWarning/qCDebug, which may throw std::bad_alloc). { QMutexLocker locker(&thr->d_func()->mutex); @@ -404,10 +436,6 @@ void *QThreadPrivate::start(void *arg) if (thr->d_func()->priority & ThreadPriorityResetFlag) { thr->d_func()->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag)); } -#ifndef Q_OS_DARWIN // For Darwin we set it as an attribute when starting the thread - if (thr->d_func()->serviceLevel != QThread::QualityOfService::Auto) - thr->d_func()->setQualityOfServiceLevel(thr->d_func()->serviceLevel); -#endif // threadId is set in QThread::start() Q_ASSERT(data->threadId.loadRelaxed() == QThread::currentThreadId()); @@ -416,28 +444,25 @@ void *QThreadPrivate::start(void *arg) data->quitNow = thr->d_func()->exited; } + // 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. + setCurrentThreadName(thr, thr->d_func()->objectName); + + // Re-enable cancellations before calling out to user code in run(), + // allowing the event dispatcher to abort this thread starting (exceptions + // aren't allowed to do that). This will also deliver a pending + // cancellation queued either by a slot connected to started() or by + // another thread using QThread::terminate(). + setCancellationEnabled(true); + data->ensureEventDispatcher(); data->eventDispatcher.loadRelaxed()->startingUp(); -#if (defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_QNX)) - { - // Sets the name of the current thread. We can only do this - // when the thread is starting, as we don't have a cross - // platform way of setting the name of an arbitrary thread. - if (Q_LIKELY(thr->d_func()->objectName.isEmpty())) - setCurrentThreadName(thr->metaObject()->className()); - else - setCurrentThreadName(std::exchange(thr->d_func()->objectName, {}).toLocal8Bit()); - } -#endif - emit thr->started(QThread::QPrivateSignal()); -#ifdef PTHREAD_CANCEL_DISABLE - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr); - pthread_testcancel(); -#endif + thr->run(); - }); + } // This calls finish(); later, the currentThreadCleanup thread-local // destructor will call cleanup(). @@ -447,26 +472,22 @@ void *QThreadPrivate::start(void *arg) void QThreadPrivate::finish() { - terminate_on_exception([&] { - QThreadPrivate *d = this; - QThread *thr = q_func(); + QThreadPrivate *d = this; + QThread *thr = q_func(); - // 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 + // 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. + setCancellationEnabled(false); - QMutexLocker locker(&d->mutex); + QMutexLocker locker(&d->mutex); - d->threadState = QThreadPrivate::Finishing; - locker.unlock(); - emit thr->finished(QThread::QPrivateSignal()); - QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + d->threadState = QThreadPrivate::Finishing; + locker.unlock(); + emit thr->finished(QThread::QPrivateSignal()); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QThreadStoragePrivate::finish(&d->data->tls); - }); + QThreadStoragePrivate::finish(&d->data->tls); if constexpr (QT_CONFIG(broken_threadlocal_dtors)) cleanup(); @@ -474,31 +495,27 @@ void QThreadPrivate::finish() void QThreadPrivate::cleanup() { - terminate_on_exception([&] { - QThreadPrivate *d = this; + QThreadPrivate *d = this; - // 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 + // Disable cancellation again: we did it above, but some user code + // running between finish() and cleanup() may have turned them back on. + setCancellationEnabled(false); - QMutexLocker locker(&d->mutex); - d->priority = QThread::InheritPriority; + QMutexLocker locker(&d->mutex); + d->priority = QThread::InheritPriority; - QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed(); - if (eventDispatcher) { - d->data->eventDispatcher = nullptr; - locker.unlock(); - eventDispatcher->closingDown(); - delete eventDispatcher; - locker.relock(); - } + QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed(); + if (eventDispatcher) { + d->data->eventDispatcher = nullptr; + locker.unlock(); + eventDispatcher->closingDown(); + delete eventDispatcher; + locker.relock(); + } - d->interruptionRequested.store(false, std::memory_order_relaxed); + d->interruptionRequested.store(false, std::memory_order_relaxed); - d->wakeAll(); - }); + d->wakeAll(); } @@ -770,10 +787,14 @@ void QThread::start(Priority priority) pthread_attr_init(&attr); if constexpr (!UsingPThreadTimedJoin) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (d->serviceLevel != QThread::QualityOfService::Auto) { #ifdef Q_OS_DARWIN - if (d->serviceLevel != QThread::QualityOfService::Auto) pthread_attr_set_qos_class_np(&attr, d->nativeQualityOfServiceClass(), 0); +#else + // No such functionality on other OSes. We promise "no effect", so don't + // print a warning either. #endif + } d->priority = priority; @@ -995,13 +1016,7 @@ void QThread::setTerminationEnabled(bool enabled) "Current thread was not started with QThread."); Q_UNUSED(thr); -#if defined(Q_OS_ANDROID) - Q_UNUSED(enabled); -#else - pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, nullptr); - if (enabled) - pthread_testcancel(); -#endif + setCancellationEnabled(enabled); } // Caller must lock the mutex diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp index f53e488a8d7..f5a7756f2b2 100644 --- a/src/corelib/thread/qthread_win.cpp +++ b/src/corelib/thread/qthread_win.cpp @@ -145,13 +145,14 @@ unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(voi { QThread *thr = reinterpret_cast<QThread *>(arg); QThreadData *data = QThreadData::get2(thr); - // If a QThread is restarted, reuse the QBindingStatus, too - data->reuseBindingStatusForNewNativeThread(); data->ref(); set_thread_data(data); data->threadId.storeRelaxed(QThread::currentThreadId()); + // If a QThread is restarted, reuse the QBindingStatus, too + data->reuseBindingStatusForNewNativeThread(); + QThread::setTerminationEnabled(false); { |
