Android/ProjectExplorer: Fix crash when removing multiple Android Qts

That were configured for a project.

The crash is triggered by a messy combination of the Android automatic
kit creation, project window update, and automatic creation of Qt
versions and kits by the automatic project importers, including a mess
of the listener pattern without any atomicity.

- the user removes the Qt versions
- the Android plugin updates the automatic kits and individually reports
  the removed kits (*)
- that triggers an update of the project window and an update of the
  target setup page (even if that is not shown, but that's yet another
  issue)
- that triggers the project importers, which add Qt versions, which in
  turn triggers another update of automatic kits in the Android plugin -
  while that is still in the reporting loop at (*)
- that leads to the crash, because the state at this point of time is a
  mess

This minimal fix of the specific crash makes the kit update reporting in
the Android plugin at (*) "more atomic", and the same for similar code
in the iOS plugin.

Fixes: QTCREATORBUG-30347
Change-Id: I2bea6fb735abcaa34469fc43f44aa37313f70429
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Eike Ziller
2024-03-05 11:09:57 +01:00
parent 1badc9f4a8
commit e31a06a0f4
4 changed files with 25 additions and 12 deletions

View File

@@ -1470,8 +1470,7 @@ void AndroidConfigurations::updateAutomaticKitList()
}
// cleanup any mess that might have existed before, by removing all Android kits that
// existed before, but weren't re-used
for (Kit *k : unhandledKits)
KitManager::deregisterKit(k);
KitManager::deregisterKits(unhandledKits);
}
Environment AndroidConfig::toolsEnvironment() const

View File

@@ -305,8 +305,7 @@ void IosConfigurations::updateAutomaticKitList()
existingKits.subtract(resultingKits);
qCDebug(kitSetupLog) << "Removing unused kits:";
printKits(existingKits);
for (Kit *kit : std::as_const(existingKits))
KitManager::deregisterKit(kit);
KitManager::deregisterKits(toList(existingKits));
}
static IosConfigurations *m_instance = nullptr;

View File

@@ -650,16 +650,30 @@ Kit *KitManager::registerKit(const std::function<void (Kit *)> &init, Utils::Id
void KitManager::deregisterKit(Kit *k)
{
QTC_ASSERT(KitManager::isLoaded(), return);
deregisterKits({k});
}
if (!k || !Utils::contains(d->m_kitList, k))
return;
auto taken = Utils::take(d->m_kitList, k);
if (defaultKit() == k) {
Kit *newDefault = Utils::findOrDefault(kits(), [](Kit *k) { return k->isValid(); });
setDefaultKit(newDefault);
void KitManager::deregisterKits(const QList<Kit *> kitList)
{
QTC_ASSERT(KitManager::isLoaded(), return);
std::vector<std::unique_ptr<Kit>> removed; // to keep them alive until the end of the function
Kit *newDefault = nullptr;
for (Kit *k : kitList) {
if (!k)
continue;
std::optional<std::unique_ptr<Kit>> taken = Utils::take(d->m_kitList, k);
if (!taken)
continue;
removed.push_back(std::move(*taken));
if (defaultKit() == k) {
newDefault = Utils::findOrDefault(kits(), [](Kit *k) { return k->isValid(); });
}
}
emit instance()->kitRemoved(k);
if (newDefault)
setDefaultKit(newDefault);
for (auto it = removed.cbegin(); it != removed.cend(); ++it)
emit instance()->kitRemoved(it->get());
emit instance()->kitsChanged();
}

View File

@@ -153,6 +153,7 @@ public:
static Kit *registerKit(const std::function<void(Kit *)> &init, Utils::Id id = {});
static void deregisterKit(Kit *k);
static void deregisterKits(const QList<Kit *> kits);
static void setDefaultKit(Kit *k);
static void saveKits();