Skip to content

Conversation

@18202781743
Copy link
Contributor

@18202781743 18202781743 commented Feb 11, 2026

  1. Modified DPluginLoader destructor to check Qt application state
    before calling destroy()
  2. Added condition to only call destroy() when qApp exists and
    application is not closing down
  3. Added warning message when skipping destruction to avoid crash during
    static cleanup
  4. This prevents crashes when DPluginLoader is destroyed after Qt global
    resources have been cleaned up

Log: Fixed potential crash during application shutdown when using static
plugin loader

Influence:

  1. Test application shutdown process with static plugin loader
  2. Verify no crashes occur during normal application exit
  3. Test edge cases where Qt resources might be cleaned up early
  4. Monitor for warning messages about skipped destruction during
    abnormal shutdown
  5. Verify memory cleanup still occurs properly during normal shutdown

fix: 防止 DPluginLoader 静态析构时崩溃

  1. 修改 DPluginLoader 析构函数,在调用 destroy() 前检查 Qt 应用程序状态
  2. 添加条件判断,仅当 qApp 存在且应用程序未关闭时才调用 destroy()
  3. 添加警告信息,当跳过析构以避免静态清理期间的崩溃时进行提示
  4. 防止在 Qt 全局资源已清理后销毁 DPluginLoader 时发生崩溃

Log: 修复使用静态插件加载器时应用程序关闭可能崩溃的问题

Influence:

  1. 测试使用静态插件加载器时的应用程序关闭过程
  2. 验证正常应用程序退出时不会发生崩溃
  3. 测试 Qt 资源可能提前清理的边缘情况
  4. 监控异常关闭期间关于跳过析构的警告信息
  5. 验证正常关闭期间内存清理仍能正确进行

PMS: BUG-350887

Summary by Sourcery

Bug Fixes:

  • Prevent crashes when DPluginLoader is destroyed after Qt global resources have been cleaned up by only invoking destroy() while the Qt application is still active.

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743

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

sourcery-ai bot commented Feb 11, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

The PR hardens DPluginLoader’s destructor against application shutdown edge cases by only invoking its destroy() logic while a Qt application instance is alive and not closing, and logging a warning instead of tearing down children during static destruction after Qt global cleanup to avoid crashes.

Sequence diagram for DPluginLoader destruction behavior during shutdown

sequenceDiagram
    participant OS
    participant StaticDPluginLoader as DPluginLoader_static
    participant QCoreApplication as QCoreApplication

    OS->>QCoreApplication: start application
    OS->>DPluginLoader_static: construct static instance

    note over QCoreApplication,DPluginLoader_static: Normal shutdown path
    QCoreApplication->>QCoreApplication: emit aboutToQuit
    QCoreApplication->>DPluginLoader_static: explicit destroy() (normal teardown)
    DPluginLoader_static->>DPluginLoader_static: destroy
    QCoreApplication->>QCoreApplication: begin closingDown
    QCoreApplication-->>OS: cleanup Qt global resources

    note over OS,DPluginLoader_static: Static destruction after Qt cleanup
    OS->>DPluginLoader_static: call ~DPluginLoader
    alt qApp exists and !closingDown
        DPluginLoader_static->>DPluginLoader_static: destroy
    else qApp is null or closingDown
        DPluginLoader_static->>DPluginLoader_static: skip destroy
        DPluginLoader_static->>OS: qWarning skipping child destruction
    end
Loading

Class diagram for updated DPluginLoader destructor logic

classDiagram
    class DPluginLoader {
        +DPluginLoader()
        +~DPluginLoader()
        +static DPluginLoader *instance()
        -void destroy()
    }

    class QCoreApplication {
        +static bool closingDown()
    }

    class QApplication {
    }

    DPluginLoader ..> QCoreApplication : checks_closingDown
    DPluginLoader ..> QApplication : uses_qApp
Loading

File-Level Changes

Change Details Files
Gate DPluginLoader destruction logic on Qt application lifetime to avoid crashes during static destruction.
  • Wrap the destroy() call in a conditional that checks qApp is non-null and QCoreApplication::closingDown() is false before running plugin teardown.
  • Add a warning branch when destruction is skipped in cases where DPluginLoader is destroyed after Qt global resources have already been cleaned up, documenting that children are intentionally not destroyed.
  • Document in comments that when DPluginLoader is a Q_APPLICATION_STATIC object its destructor may run after Qt global cleanup, so skipping destroy() avoids crashes at the cost of benign leaks at process exit.
frame/pluginloader.cpp

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

1. Modified DPluginLoader destructor to check Qt application state
before calling destroy()
2. Added condition to only call destroy() when qApp exists and
application is not closing down
3. Added warning message when skipping destruction to avoid crash during
static cleanup
4. This prevents crashes when DPluginLoader is destroyed after Qt global
resources have been cleaned up

Log: Fixed potential crash during application shutdown when using static
plugin loader

Influence:
1. Test application shutdown process with static plugin loader
2. Verify no crashes occur during normal application exit
3. Test edge cases where Qt resources might be cleaned up early
4. Monitor for warning messages about skipped destruction during
abnormal shutdown
5. Verify memory cleanup still occurs properly during normal shutdown

fix: 防止 DPluginLoader 静态析构时崩溃

1. 修改 DPluginLoader 析构函数,在调用 destroy() 前检查 Qt 应用程序状态
2. 添加条件判断,仅当 qApp 存在且应用程序未关闭时才调用 destroy()
3. 添加警告信息,当跳过析构以避免静态清理期间的崩溃时进行提示
4. 防止在 Qt 全局资源已清理后销毁 DPluginLoader 时发生崩溃

Log: 修复使用静态插件加载器时应用程序关闭可能崩溃的问题

Influence:
1. 测试使用静态插件加载器时的应用程序关闭过程
2. 验证正常应用程序退出时不会发生崩溃
3. 测试 Qt 资源可能提前清理的边缘情况
4. 监控异常关闭期间关于跳过析构的警告信息
5. 验证正常关闭期间内存清理仍能正确进行

PMS: BUG-350887
@deepin-ci-robot
Copy link

deepin pr auto review

这段代码的修改是为了解决在程序退出时,由于静态对象析构顺序导致的崩溃问题。这是一个经典且棘手的 C++ 静态析构顺序问题(Static Initialization Order Fiasco)。

以下是对这段代码的详细审查意见,分为逻辑、质量、性能和安全四个方面:

1. 语法逻辑

评价:逻辑正确,处理了边界情况。

  • 改进点:代码正确识别了 QCoreApplication::closingDown() 这一状态。在 Qt 中,当 QCoreApplication 开始销毁其子对象时,该标志位会变为 true。如果在此时(或更晚的静态析构阶段)去析构插件加载器并尝试销毁其管理的子对象(这些子对象可能依赖 Qt 的全局单例如 QThreadStorage、事件循环等),必然会导致崩溃或未定义行为。
  • 解释qApp 检查是为了防止在极其早期的初始化阶段(虽然不太可能发生在析构函数中)或极其晚期的静态析构阶段 qApp 指针本身已经无效时解引用它。!QCoreApplication::closingDown() 确保了只有在 Qt 核心环境仍然健在的时候才执行清理逻辑。

2. 代码质量

评价:良好,注释清晰,权衡合理。

  • 优点
    • 注释详尽:代码中清晰地解释了为什么要这样做("防止崩溃"、"作为静态对象时"、"内存泄漏无关紧要")。这对于后续维护者非常重要,因为这种“故意泄漏”的代码如果不加注释很容易被误认为是 Bug。
    • 防御性编程:使用 qWarning 输出日志,有助于在开发和调试阶段发现异常退出路径。
  • 改进建议
    • 考虑到 DPluginLoader 通常是单例模式(由 instance() 方法暗示),如果它确实被定义为静态局部变量(Meyers' Singleton)或全局静态变量,这个修改是必须的。如果它是由 new 创建并由 QObject 树管理的,通常不需要这种特殊的析构保护。建议确认 DPluginLoader 的生命周期管理方式是否完全依赖静态存储期。

3. 代码性能

评价:无影响。

  • 分析:析构函数只在程序退出时执行一次。增加的几个条件判断(qApp 指针检查和布尔值检查)相对于进程退出时的开销来说可以忽略不计。即使是正常的析构路径,性能也没有损失。

4. 代码安全

评价:提高了程序的稳定性(安全性)。

  • 分析
    • 防止崩溃:这是最主要的安全改进。避免了访问已释放内存(Use-After-Free)导致的程序崩溃。
    • 内存泄漏权衡:代码注释中提到的"内存泄漏无关紧要"是正确的。在进程退出时,现代操作系统(Linux/Windows等)会立即回收进程占用的所有内存、文件句柄和大部分系统资源。强制执行析构函数不仅风险高,而且对系统资源回收没有实际帮助。
    • 潜在风险:如果 DPluginLoader 管理的资源不仅仅是内存,还涉及到需要持久化到磁盘的数据(例如配置文件保存),那么在 else 分支中跳过 destroy() 可能会导致这部分数据丢失。
      • 建议:请确认 destroy() 函数内部是否包含诸如 saveSettings()writeToFile() 等关键逻辑。如果有,需要考虑将这些持久化操作提前到 QCoreApplication::aboutToQuit 信号的处理函数中执行,而不是依赖析构函数来保存。

总结

这是一个高质量、必要的修复。

它正确地处理了 C++ 静态析构顺序问题,通过牺牲进程退出时刻的“完美清理”(实际上由操作系统代劳),换取了程序退出时的稳定性。

最终建议
只需确认一点:确保所有需要持久化的数据(如配置、状态保存)已经绑定在 QCoreApplication::aboutToQuit 信号上完成,而不是依赖 DPluginLoader 的析构函数来执行。除此之外,代码逻辑严密,注释清晰,可以直接合并。

Copy link

@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:

  • Consider reducing the noise of the qWarning in the destructor (e.g., only logging once or behind a debug/verbosity flag), since static destruction paths can be part of normal shutdown and this message may appear on every exit.
  • To make the behavior clearer and avoid future regressions, it might be helpful to centralize the shutdown logic (e.g., track via a flag when destroy() has been called during aboutToQuit) so the destructor can distinguish between an intentionally cleaned-up instance and a late static-destruction case.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider reducing the noise of the `qWarning` in the destructor (e.g., only logging once or behind a debug/verbosity flag), since static destruction paths can be part of normal shutdown and this message may appear on every exit.
- To make the behavior clearer and avoid future regressions, it might be helpful to centralize the shutdown logic (e.g., track via a flag when `destroy()` has been called during `aboutToQuit`) so the destructor can distinguish between an intentionally cleaned-up instance and a late static-destruction case.

## Individual Comments

### Comment 1
<location> `frame/pluginloader.cpp:238-242` </location>
<code_context>
+    if (qApp && !QCoreApplication::closingDown()) {
+        // 正常情况:在 aboutToQuit 中调用的 destroy(),Qt 资源有效
+        destroy();
+    } else {
+        // 异常情况:静态析构阶段,Qt 全局资源可能已清理
+        // 不调用 destroy(),避免触发子对象析构
+        // 内存泄漏无关紧要,程序即将退出,操作系统会回收所有资源
+        qWarning("DPluginLoader destroyed after Qt global cleanup, skipping child destruction to avoid crash.");
+    }
 }
</code_context>

<issue_to_address>
**issue (bug_risk):** Using `qWarning` in the branch that assumes Qt is already torn down could still touch Qt-global state.

Since this path assumes Qt globals may already be torn down, `qWarning` itself could still touch Qt internals (logging categories, handlers, TLS, etc.) and reintroduce a crash risk. Consider either dropping this log or replacing it with a mechanism independent of Qt (e.g., `fprintf(stderr, ...)`) if this code can run after Qt shutdown.
</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 +238 to +242
} else {
// 异常情况:静态析构阶段,Qt 全局资源可能已清理
// 不调用 destroy(),避免触发子对象析构
// 内存泄漏无关紧要,程序即将退出,操作系统会回收所有资源
qWarning("DPluginLoader destroyed after Qt global cleanup, skipping child destruction to avoid crash.");
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): Using qWarning in the branch that assumes Qt is already torn down could still touch Qt-global state.

Since this path assumes Qt globals may already be torn down, qWarning itself could still touch Qt internals (logging categories, handlers, TLS, etc.) and reintroduce a crash risk. Consider either dropping this log or replacing it with a mechanism independent of Qt (e.g., fprintf(stderr, ...)) if this code can run after Qt shutdown.

// 异常情况:静态析构阶段,Qt 全局资源可能已清理
// 不调用 destroy(),避免触发子对象析构
// 内存泄漏无关紧要,程序即将退出,操作系统会回收所有资源
qWarning("DPluginLoader destroyed after Qt global cleanup, skipping child destruction to avoid crash.");
Copy link
Member

Choose a reason for hiding this comment

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

qCWarning?

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