Skip to content

fix: prevent crashes and memory leaks during object lifecycle#3168

Open
pengfeixx wants to merge 1 commit intolinuxdeepin:masterfrom
pengfeixx:fix-335919
Open

fix: prevent crashes and memory leaks during object lifecycle#3168
pengfeixx wants to merge 1 commit intolinuxdeepin:masterfrom
pengfeixx:fix-335919

Conversation

@pengfeixx
Copy link
Copy Markdown
Contributor

@pengfeixx pengfeixx commented Apr 10, 2026

Add batch update flag to suppress intermediate signals during page navigation, add null checks for parent private pointer in DccObject, and fix component memory leaks when plugin manager is deleting.

添加批量更新标志以抑制页面导航期间的中间信号发射,
增加 DccObject 父对象私有指针的空检查,修复插件管理器
删除时的组件内存泄漏问题。

Log: 修复对象生命周期中的崩溃和内存泄漏问题
PMS: BUG-335919
Influence: 修复控制中心页面切换过程中可能出现的崩溃问题,
修复插件卸载时的内存泄漏,提升稳定性。

Summary by Sourcery

Improve object lifecycle handling to avoid crashes and memory leaks during page navigation and plugin teardown.

Bug Fixes:

  • Add null checks for DccObject parent private pointer to prevent crashes when accessing parent state.
  • Suppress DccObject activation/deactivation signals during batch page updates to avoid inconsistent state while navigating pages.
  • Detach DccLoader-owned objects from their parent item on destruction to prevent stale references.
  • Ensure plugin data and shared objects are explicitly deleted when the plugin manager is tearing down to avoid memory leaks.
  • Defer QML component deletion when the plugin manager is deleting to prevent dangling operations on destroyed components.

Enhancements:

  • Introduce a batch update mode in DccApp/DccManager to group page updates and emit signals only after navigation is complete.

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: pengfeixx

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 10, 2026

Reviewer's Guide

Adds a batch-update mode to suppress intermediate current-object signals during page navigation, hardens DccObject parent handling with null checks, and fixes plugin-related memory leaks and crashes when the plugin manager is tearing down or QML loaders are destroyed.

Sequence diagram for batch-updated page navigation and suppressed signals

sequenceDiagram
actor User
participant DccManager
participant DccObject
participant DccApp

User->>DccManager: doShowPage(obj, cmd)
activate DccManager

DccManager->>DccManager: m_batchUpdating = true

DccManager->>DccObject: setCurrentObject(nullptr)
activate DccObject
DccObject->>DccApp: instance()
DccApp-->>DccObject: app
DccObject->>DccApp: isBatchUpdating()
DccApp-->>DccObject: true

alt batch updating is true
  DccObject-->>DccManager: skip deactive and currentObjectChanged
else batch updating is false
  DccObject-->>DccManager: emit deactive
  DccObject-->>DccManager: emit currentObjectChanged
end

DccObject-->>DccManager: return
deactivate DccObject

DccManager->>DccManager: update modules and m_currentObjects
DccManager->>DccManager: m_batchUpdating = false

DccManager-->>User: triggeredObjectsChanged, active object updated
deactivate DccManager
Loading

Class diagram for DccApp, DccManager, DccObject lifecycle changes

classDiagram

class DccApp {
  +void setSidebarWidth(int width)
  +void setAnimationMode(AnimationMode mode)
  +bool isBatchUpdating() const
}

class DccManager {
  +void doShowPage(QPointer_DccObject obj, QString cmd)
  +bool isBatchUpdating() const
  -QVector_DccObjectPtr m_currentObjects
  -QVector_DccObjectPtr m_triggeredObjects
  -QHash_QString_QVector_DccObjectPtr m_objMap
  -atomic_bool m_batchUpdating
}

class DccObject {
  +void setCurrentObject(DccObject *obj)
  +DccObject *currentObject()
  +void setWeight(quint32 weight)
  +QVector_DccObjectPtr getChildren() const
  +DccObject *getChild(int childPos) const
  -DccObject_Private *p_ptr
}

class DccObject_Private {
  -DccObject *q_ptr
  -DccObject *m_parent
  -QVector_DccObjectPtr m_children
  -QObject *m_page
  -DccObject *m_currentObject
  -quint32 m_weight
  +~DccObject_Private()
  +const QVector_DccObjectPtr &getChildren() const
  +int getIndex() const
  +DccObject *getChild(int childPos) const
  +void updatePos(DccObject *obj)
}

class PluginManager {
  +bool isDeleting() const
  +void createModule(QQmlComponent *component)
  +void createMain(QQmlComponent *component)
}

class LoadPluginTask {
  -PluginManager *m_pManager
  -PluginDataHolder *m_data
  +void createData()
}

DccApp <|-- DccManager
DccObject o-- DccObject_Private
DccObject_Private --> DccObject : m_parent
DccObject_Private --> DccObject : m_children
DccObject_Private --> DccObject : m_currentObject

PluginManager o-- LoadPluginTask
LoadPluginTask --> PluginManager : m_pManager

class QQmlComponent {
  +QVariant property(QString name) const
  +void deleteLater()
}

PluginManager --> QQmlComponent : createModule
PluginManager --> QQmlComponent : createMain
Loading

File-Level Changes

Change Details Files
Guard DccObject parent access with null checks to avoid crashes when parent internals are already destroyed.
  • Include dccapp.h in dccobject.cpp so DccObject can query the app for batch-update state.
  • Add null check for m_parent->p_ptr in DccObject::Private destructor before removing the child from the parent.
  • Add null check for m_parent->p_ptr in DccObject::Private::getIndex to safely query the parent child list.
  • Add null check for m_parent->p_ptr in DccObject::setWeight before updating the parent’s child ordering.
  • Change DccObject::setCurrentObject to store the old object, update the current object, and then emit deactive/currentObjectChanged only when the app is not in batch-update mode.
src/dde-control-center/plugin/dccobject.cpp
Manage QML DccLoader parentItem relationships explicitly to prevent dangling references and leaks on destruction.
  • Update DccLoader.qml’s updateDccObjItem to only set parentItem when both dccObj and dccObjItem are valid.
  • Add a Component.onDestruction handler that clears dccObj.parentItem when it still points at dccObjItem, breaking the reference cycle on loader destruction.
src/dde-control-center/plugin/DccLoader.qml
Ensure plugin objects and components are properly destroyed or deferred when the PluginManager is in the process of deleting, preventing memory leaks and use-after-free.
  • In LoadPluginTask::createData, delete dataObj/soObj and return immediately when the owning PluginManager reports isDeleting().
  • In PluginManager::createModule and createMain, if isDeleting() is true, schedule the QQmlComponent for deletion via deleteLater() and return early, instead of proceeding with module creation.
src/dde-control-center/pluginmanager.cpp
Introduce a batch-update flag in DccManager/DccApp to suppress intermediate signals during page navigation, reducing churn and avoiding transient deactivation/activation sequences.
  • Add std::atomic m_batchUpdating to DccManager, initialized to false, and expose inline isBatchUpdating() override that returns this flag.
  • Toggle m_batchUpdating to true at the start of DccManager::doShowPage and back to false on all exit paths (before early return when tmpObj is null and after the module list/current objects are finalized).
  • Add a default virtual isBatchUpdating() method to DccApp that returns false, allowing DccObject to query the batch-update state through the base interface.
src/dde-control-center/dccmanager.cpp
src/dde-control-center/dccmanager.h
src/dde-control-center/plugin/dccapp.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The manual m_batchUpdating toggling in DccManager::doShowPage is easy to break if more early-return paths are added later; consider wrapping it in an RAII helper (e.g. a local guard object) so the flag is always reset on every exit path.
  • In DccObject::setCurrentObject, deactive and currentObjectChanged are skipped entirely when isBatchUpdating() is true; if callers still need a final notification after the batch, consider emitting a consolidated signal once the batch update completes or documenting that these signals are intentionally dropped during batch operations.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The manual `m_batchUpdating` toggling in `DccManager::doShowPage` is easy to break if more early-return paths are added later; consider wrapping it in an RAII helper (e.g. a local guard object) so the flag is always reset on every exit path.
- In `DccObject::setCurrentObject`, `deactive` and `currentObjectChanged` are skipped entirely when `isBatchUpdating()` is true; if callers still need a final notification after the batch, consider emitting a consolidated signal once the batch update completes or documenting that these signals are intentionally dropped during batch operations.

## Individual Comments

### Comment 1
<location path="src/dde-control-center/plugin/dccobject.cpp" line_range="446-449" />
<code_context>
-        if (p_ptr->m_currentObject) {
-            Q_EMIT p_ptr->m_currentObject->deactive();
-        }
+        DccObject *oldObject = p_ptr->m_currentObject;
         p_ptr->m_currentObject = obj;
-        Q_EMIT currentObjectChanged(p_ptr->m_currentObject);
+
+        DccApp *app = DccApp::instance();
</code_context>
<issue_to_address>
**issue (bug_risk):** Reordered deactivation vs. current object update may introduce behavior changes in slots.

Previously, `deactive()` was emitted before updating `m_currentObject`, so slots handling `deactive()` and then calling `currentObject()` would still see the old object. With the new ordering (update `m_currentObject` first, then emit `deactive(oldObject)`), those same slots will now see the new object, which is a behavior change that can affect existing plugin code. If the goal is just to suppress signals during batch updates, you could keep the original ordering and gate both signals with `isBatchUpdating()` instead.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +446 to +449
DccObject *oldObject = p_ptr->m_currentObject;
p_ptr->m_currentObject = obj;
Q_EMIT currentObjectChanged(p_ptr->m_currentObject);

DccApp *app = DccApp::instance();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Reordered deactivation vs. current object update may introduce behavior changes in slots.

Previously, deactive() was emitted before updating m_currentObject, so slots handling deactive() and then calling currentObject() would still see the old object. With the new ordering (update m_currentObject first, then emit deactive(oldObject)), those same slots will now see the new object, which is a behavior change that can affect existing plugin code. If the goal is just to suppress signals during batch updates, you could keep the original ordering and gate both signals with isBatchUpdating() instead.

Add batch update flag to suppress intermediate signals during page
navigation, add null checks for parent private pointer in DccObject,
and fix component memory leaks when plugin manager is deleting.

添加批量更新标志以抑制页面导航期间的中间信号发射,
增加 DccObject 父对象私有指针的空检查,修复插件管理器
删除时的组件内存泄漏问题。

Log: 修复对象生命周期中的崩溃和内存泄漏问题
PMS: BUG-335919
Influence: 修复控制中心页面切换过程中可能出现的崩溃问题,
修复插件卸载时的内存泄漏,提升稳定性。
delete dataObj;
if (soObj)
delete soObj;
return;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

直接return,不走 这个正常么, Q_EMIT m_pManager->updatePluginStatus(m_data, DataEnd, ": create data finished. elapsed time :" + QString::number(timer.elapsed()));

Q_EMIT currentObjectChanged(p_ptr->m_currentObject);

DccApp *app = DccApp::instance();
if (!app || !app->isBatchUpdating()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

看下ai的评论,另外app为空不需要执行了吧,

@@ -442,11 +443,16 @@ DccObject *DccObject::currentObject()
void DccObject::setCurrentObject(DccObject *obj)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这应该是在主线程里被触发的吧,是线性的吧,会存在多个交叉被setCurrentObject么,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants