From bfd3b88bb890460a5b0213258f12e0ab05b84351 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 3 Jun 2024 13:03:00 +0200 Subject: [PATCH] RemoteLinux: Do not announce connection attempts from non-UI threads This can lead to a deadlock. Example scenario: - Have a kit with an unreachable device as the build device. - Open a project with a .user file not matching the current QtC. - The target setup page will open, showing a candidate widget for each kit. These widgets contains path choosers, which nowadays start a dedicated thread to validate their input. - The path validation thread for the Linux kit arrives at setupShell() and tries to announce the connection attempt. To that end, it invokes a method of Core::ICore::infoBar, using Qt::BlockingQueuedConnection, as otherwise the call would either not be tread-safe or arrive too late. - As the InfoBar lives in the main thread, control is now passed back to that one, and the target setup page continues its work, which is to gather candidates for importing a build from. This involves perusing potential build directories for all kits. In case of the Linux kit, QDirIterator is backed by a remote call to find(), which triggers a call to runInShell(), which tries to acquire the mutex that is already held by the path validation thread, which in turn is waiting for the UI thread. - Qt Creator now hangs indefinitely. Skipping the announcements for non-UI threads limits the usefulness of this feature, but I don't see a better way. Change-Id: I816c83358f543aa9a6e6e97eee7fa8ad95e66ea8 Reviewed-by: Marcus Tillmanns --- src/plugins/remotelinux/linuxdevice.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index d2779e2ebb8..97cb7f9ae72 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -1231,25 +1231,20 @@ RunResult LinuxDevicePrivate::runInShell(const CommandLine &cmd, const QByteArra void LinuxDevicePrivate::announceConnectionAttempt() { - const auto announce = [id = announceId(), name = q->displayName()] { - Core::ICore::infoBar()->addInfo( - InfoBarEntry(id, - Tr::tr("Establishing initial connection to device \"%1\". " - "This might take a moment.") - .arg(name))); + const QString message = Tr::tr("Establishing initial connection to device \"%1\". " + "This might take a moment.").arg(q->displayName()); + qCDebug(linuxDeviceLog) << message; + if (isMainThread()) { + Core::ICore::infoBar()->addInfo(InfoBarEntry(announceId(), message)); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); // Yes, twice. - }; - if (QThread::currentThread() == qApp->thread()) - announce(); - else - QMetaObject::invokeMethod(Core::ICore::infoBar(), announce, Qt::BlockingQueuedConnection); + } } void LinuxDevicePrivate::unannounceConnectionAttempt() { - QMetaObject::invokeMethod(Core::ICore::infoBar(), - [id = announceId()] { Core::ICore::infoBar()->removeInfo(id); }); + if (isMainThread()) + Core::ICore::infoBar()->removeInfo(announceId()); } bool LinuxDevicePrivate::checkDisconnectedWithWarning()