From 057a54364dc38e773c0f70aa97952d3b22056936 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 26 Jun 2024 11:19:26 +0200 Subject: [PATCH] Android: Synchronize startAvd()'s future This change fixes a possible deadlock on Creator shutdown after clicking "Start AVD" button from the Android device settings dialog, and quickly closing the started emulator window, the settings dialog and Creator (within 2 seconds). In case of no synchronization we may be starting new processes in separate thread after the QApplication has already been destroyed. Blocking run of processes requires constructing an event loop, which ends up with the following warnings (and a deadlock): "QEventLoop: Cannot be used without QApplication" "QObject::setParent: Cannot set parent, new parent is in a different thread". Add returned future to the global future synchronizer. Avoid long synchronization of startAvd() on shutdown by adding an optional future argument to the startAvd() and pass it to the waitForAvd(). Otherwise, we would need to wait up to 2 minutes for synchronization to finish. Change-Id: Ia1d71d309cb2dcdb5e31decfdfdea6df9a117ed5 Reviewed-by: Alessandro Portale --- src/plugins/android/androidavdmanager.cpp | 4 ++-- src/plugins/android/androidavdmanager.h | 2 +- src/plugins/android/androiddevice.cpp | 7 +++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index 3b5c6ccf916..c9453bb9084 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -20,10 +20,10 @@ namespace Android::Internal::AndroidAvdManager { static Q_LOGGING_CATEGORY(avdManagerLog, "qtc.android.avdManager", QtWarningMsg) -QString startAvd(const QString &name) +QString startAvd(const QString &name, const std::optional> &future) { if (!findAvd(name).isEmpty() || startAvdAsync(name)) - return waitForAvd(name); + return waitForAvd(name, future); return {}; } diff --git a/src/plugins/android/androidavdmanager.h b/src/plugins/android/androidavdmanager.h index 5a1f188038c..7f1b094bdd2 100644 --- a/src/plugins/android/androidavdmanager.h +++ b/src/plugins/android/androidavdmanager.h @@ -8,7 +8,7 @@ namespace Android::Internal::AndroidAvdManager { -QString startAvd(const QString &name); +QString startAvd(const QString &name, const std::optional> &future = {}); bool startAvdAsync(const QString &avdName); QString findAvd(const QString &avdName); QString waitForAvd(const QString &avdName, const std::optional> &future = {}); diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index 4972c653c9d..2e24eae73a8 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -111,15 +111,14 @@ static void startAvd(const IDevice::Ptr &device, QWidget *parent) const AndroidDevice *androidDev = static_cast(device.get()); const QString name = androidDev->avdName(); qCDebug(androidDeviceLog, "Starting Android AVD id \"%s\".", qPrintable(name)); - auto future = Utils::asyncRun([name, device] { - const QString serialNumber = AndroidAvdManager::startAvd(name); + Utils::futureSynchronizer()->addFuture(Utils::asyncRun([name, device](QPromise &promise) { + const QString serialNumber = AndroidAvdManager::startAvd(name, promise.future()); // Mark the AVD as ReadyToUse once we know it's started if (!serialNumber.isEmpty()) { DeviceManager *const devMgr = DeviceManager::instance(); devMgr->setDeviceState(device->id(), IDevice::DeviceReadyToUse); } - }); - // TODO: use future! + })); } static void setEmulatorArguments(QWidget *parent)