From 9d3d5903083e82d84b7fa619375d4b36ac014b2d Mon Sep 17 00:00:00 2001 From: Ivy233 Date: Fri, 31 Jul 2026 15:45:28 +0800 Subject: [PATCH] fix: prevent dde-apps crash when restarting due to concurrent model updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AMAppItemModel was populated from a QtConcurrent worker thread, which mutated the model (appendRow) on a non-GUI thread while the main thread's queued onReferenceModelChanged() read it concurrently, causing a data race and a SIGSEGV in QStandardItemModel::data() during restart. 1. Replace QtConcurrent::run with QDBusPendingCallWatcher so the model is populated on the model's own (main) thread, eliminating the cross-thread race and stale queued rowsInserted callbacks. 2. Change m_referenceModel from a raw pointer to QPointer so it is automatically cleared when the model is destroyed. 3. Add a null check at the start of onReferenceModelChanged() to skip updates when the reference model has been destroyed. Log: Fixed a crash in dde-shell during restart caused by concurrent access to the app model. Influence: 1. Restart dde-shell repeatedly and verify no crash occurs. 2. Test app install/remove and grouping during restart. 3. Verify the launchpad app group arrangement is preserved after restart. fix: 修复重启时因并发访问应用模型导致的 dde-shell 崩溃 AMAppItemModel 此前在 QtConcurrent 工作线程中填充,在工作线程中修改 模型(appendRow),同时主线程通过队列回调 onReferenceModelChanged() 并发 读取模型,导致数据竞争,重启时在 QStandardItemModel::data() 中触发段错误。 1. 将 QtConcurrent::run 替换为 QDBusPendingCallWatcher,使模型在自身 (主)线程中填充,消除跨线程竞争和过期的 rowsInserted 队列回调。 2. 将 m_referenceModel 由裸指针改为 QPointer,模型销毁时自动置空。 3. 在 onReferenceModelChanged() 开头增加空指针检查,模型已销毁时跳过更新。 Log: 修复重启时因并发访问应用模型导致的 dde-shell 崩溃。 Influence: 1. 反复重启 dde-shell,验证不再崩溃。 2. 重启过程中测试应用的安装/卸载及分组功能。 3. 验证重启后启动器应用分组布局得以保留。 PMS: BUG-372351 --- applets/dde-apps/amappitemmodel.cpp | 24 +++++++++++++++--------- applets/dde-apps/appgroupmanager.cpp | 7 ++++++- applets/dde-apps/appgroupmanager.h | 5 +++-- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/applets/dde-apps/amappitemmodel.cpp b/applets/dde-apps/amappitemmodel.cpp index 426ac9c13..38291a6d9 100644 --- a/applets/dde-apps/amappitemmodel.cpp +++ b/applets/dde-apps/amappitemmodel.cpp @@ -1,15 +1,15 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "amappitemmodel.h" #include "amappitem.h" -#include "appgroupmanager.h" #include "appitemmodel.h" #include "objectmanager1interface.h" #include -#include +#include +#include Q_LOGGING_CATEGORY(appsLog, "org.deepin.dde.shell.dde-apps.amappitemmodel") @@ -17,7 +17,7 @@ namespace apps { AMAppItemModel::AMAppItemModel(QObject *parent) : AppItemModel(parent) - , m_manager(new ObjectManager("org.desktopspec.ApplicationManager1", "/org/desktopspec/ApplicationManager1", QDBusConnection::sessionBus())) + , m_manager(new ObjectManager("org.desktopspec.ApplicationManager1", "/org/desktopspec/ApplicationManager1", QDBusConnection::sessionBus(), this)) , m_ready(false) { qRegisterMetaType(); @@ -50,10 +50,17 @@ AMAppItemModel::AMAppItemModel(QObject *parent) removeRow(res.first().row()); }); - // load static desktop info from am - auto future = QtConcurrent::run([this]() { - auto apps = m_manager->GetManagedObjects().value(); - + // load static desktop info from am asynchronously + auto reply = m_manager->GetManagedObjects(); + auto *watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher]() { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + if (reply.isError()) { + qCWarning(appsLog()) << "Failed to get managed objects:" << reply.error().message(); + return; + } + auto apps = reply.value(); for (auto app = apps.cbegin(); app != apps.cend(); app++) { auto path = app.key(); if (!path.path().isEmpty()) { @@ -61,7 +68,6 @@ AMAppItemModel::AMAppItemModel(QObject *parent) appendRow(c); } } - setProperty("ready", true); qCDebug(appsLog) << "AMAppItemModel is now ready with apps counts:" << rowCount(); }); diff --git a/applets/dde-apps/appgroupmanager.cpp b/applets/dde-apps/appgroupmanager.cpp index b5ec71b16..157fc1771 100644 --- a/applets/dde-apps/appgroupmanager.cpp +++ b/applets/dde-apps/appgroupmanager.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -240,6 +240,11 @@ QVariantList AppGroupManager::fromListOfStringList(const QList & li // On AM model changed, add newly installed apps to group (if any) and remove apps that are no longer exists. void AppGroupManager::onReferenceModelChanged() { + if (!m_referenceModel) { + qWarning() << "referenceModel has been destroyed, skip updating"; + return; + } + // Avoid remove all existing records when first time (AM model is not ready). if (m_referenceModel->rowCount() == 0) { qDebug() << "referenceModel not ready, wait for next time"; diff --git a/applets/dde-apps/appgroupmanager.h b/applets/dde-apps/appgroupmanager.h index a51745b06..a33e0977e 100644 --- a/applets/dde-apps/appgroupmanager.h +++ b/applets/dde-apps/appgroupmanager.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -82,7 +83,7 @@ class AppGroupManager : public QStandardItemModel private: bool m_appGroupInitialized; - AMAppItemModel * m_referenceModel; + QPointer m_referenceModel; QTimer* m_dumpTimer; Dtk::Core::DConfig *m_config; };