Skip to content

fix(notification): fix action buttons in notification center - #1785

Merged
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
Ivy233:fix/notification-action-invoke-race
Oct 28, 2026
Merged

deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
Ivy233:fix/notification-action-invoke-race

Conversation

@Ivy233

@Ivy233 Ivy233 commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

The notification center removed a notification immediately after dispatching its action, while the action was queued to the notification manager's worker thread. The removal could reach the manager first and delete the entity before actionInvoked() fetched it, so clicking an action button became a no-op.

  1. Stop NotifyModel::invokeAction() from removing the notification locally. The server removes it after handling the action, and the frontend reacts to the resulting Removed state.
  2. Emit entityRemoved() from NotifyAccessor for the Removed state and remove only the view row, without deleting the server-side entity again.
  3. Queue NotifyServerApplet::removeNotification() to the manager's worker thread so removals stay ordered after actionInvoked() and are not called cross-thread.
  4. Mark NotificationManager::removeNotification() Q_INVOKABLE for the queued invocation.

Log: fix notification center action buttons not responding

Influence:

  1. Click action buttons on notifications in the notification center and verify the actions run
  2. Verify normal, overlap, and expanded group notifications are removed from the center after the action executes
  3. Verify manually deleting notifications still works
  4. Verify action buttons in bubbles and the staging area still work

fix(notification): 修复通知中心通知按钮点击无效

通知中心在派发动作后立即删除通知,而动作是排队投递到通知管理线程的。
删除操作可能先到达 manager 并删除实体,随后 actionInvoked() 获取不到通知, 导致点击动作按钮无效。

  1. NotifyModel::invokeAction() 不再立即删除通知,由服务端处理动作后删除, 前端根据 Removed 状态更新。
  2. NotifyAccessor 在 Removed 状态发出 entityRemoved(),NotifyModel 只删除 视图行,不再重复删除服务端实体。
  3. NotifyServerApplet::removeNotification() 改为排队投递到 manager 工作 线程,保证删除顺序在 actionInvoked() 之后,并避免跨线程直接调用。
  4. NotificationManager::removeNotification() 增加 Q_INVOKABLE 以支持排队调用。

Log: 修复通知中心通知按钮点击无响应的问题

Influence:

  1. 在通知中心点击通知动作按钮,确认动作正常执行
  2. 确认普通、重叠及展开的分组通知在动作执行后从通知中心移除
  3. 确认手动删除通知仍然正常
  4. 确认气泡和通知中心 staging 区域的动作按钮仍然正常

PMS: BUG-380339

Summary by Sourcery

Preserve notification action execution by ordering action dispatch ahead of notification removal and making activation-token handling independent of the manager lifetime.

Bug Fixes:

  • Ensure notification action requests reach the manager before notification removal so notification center action buttons continue to execute successfully.
  • Prevent activation-token helpers from being destroyed with the worker-thread manager before token handling completes.

@sourcery-ai

sourcery-ai Bot commented Oct 27, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR fixes notification-center action buttons by preventing premature local/server deletion, updating the view from the server’s Removed state, and queuing removals on the manager worker thread after action dispatch.

Sequence diagram for notification action execution and removal

sequenceDiagram
    participant User
    participant NotifyModel
    participant NotifyAccessor
    participant NotificationManager
    participant NotifyServerApplet

    User->>NotifyModel: invokeAction(id, actionId)
    NotifyModel->>NotifyAccessor: invokeAction(entity, actionId)
    NotifyAccessor->>NotificationManager: actionInvoked()
    NotificationManager->>NotifyServerApplet: removeNotification(id)
    NotifyServerApplet->>NotificationManager: QMetaObject.invokeMethod(removeNotification, QueuedConnection)
    NotificationManager->>NotificationManager: removeNotification(id)
    NotificationManager-->>NotifyAccessor: NotificationStateChanged(Removed)
    NotifyAccessor-->>NotifyModel: entityRemoved(id)
    NotifyModel->>NotifyModel: removeInternal(id, false)
Loading

File-Level Changes

Change Details Files
Preserve notification entities until the server processes actions, then remove only the corresponding frontend rows when the Removed state arrives.
  • Added an entityRemoved signal for Removed notification states.
  • Connected the signal to a model handler that removes rows without issuing a second server deletion.
  • Refactored removal logic to distinguish view-only removal from server-backed deletion.
  • Stopped invokeAction() from removing notifications immediately.
panels/notification/center/notifyaccessor.cpp
panels/notification/center/notifyaccessor.h
panels/notification/center/notifymodel.cpp
panels/notification/center/notifymodel.h
Serialize notification removal through the manager worker thread so action dispatch is processed before deletion.
  • Made removeNotification() invokable through Qt's meta-object system.
  • Changed the applet to use a queued invocation instead of a direct cross-thread call.
panels/notification/server/notificationmanager.h
panels/notification/server/notifyserverapplet.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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread panels/notification/center/notifymodel.cpp Outdated
@Ivy233
Ivy233 requested a review from 18202781743 October 27, 2026 12:41
@Ivy233
Ivy233 force-pushed the fix/notification-action-invoke-race branch from b08d434 to 67a753f Compare October 28, 2026 02:40
@Ivy233

Ivy233 commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

感谢评审建议。

  1. 考虑到当前 BUG 较紧急,时间上不适合在本 PR 中继续做 BlockingQueuedConnection 的线程模型改造和完整回归,本次先按最小改动处理。
  2. 因此本次选择回退到 2.0.56 的行为:actionInvoked() 使用 DirectConnection,保证 action 在 remove() 删除通知前同步执行。BlockingQueuedConnection 方案涉及的阻塞、死锁和 action 执行线程问题,后续单独评估处理。

18202781743
18202781743 previously approved these changes Oct 28, 2026
@Ivy233
Ivy233 force-pushed the fix/notification-action-invoke-race branch from 67a753f to 7f94ba6 Compare October 28, 2026 03:36
Comment thread panels/notification/server/notificationmanager.cpp Outdated
The expire rework changed NotifyServerApplet::actionInvoked() from
Qt::DirectConnection to Qt::QueuedConnection. NotifyModel::invokeAction()
still removes the notification right after dispatching the action, so the
queued action could reach the manager after the removal and find no entity,
making action buttons in the notification center a no-op.

Restore the synchronous dispatch so the action is handled before remove()
deletes the entity. The notification center keeps a single delete entry:
remove() handles user operations (close, dismiss and action invocation) and
deletes both the view row and the database entity. Notifications that move
into history after a bubble disappears still arrive through
NotificationStateChanged(Processed) and are not deleted by this path.

Since actionInvoked() now runs on the main thread while the manager lives on
the worker thread, XdgActivation must not be parented to the manager. Doing
so triggers "Cannot create children for a parent that is in a different
thread". Create it without a parent instead; the tokenReady handler deletes
it when the token arrives. The manager is long-lived, so no separate
manager-destroyed cleanup is kept for this short-lived object.

The synchronous blocking is the same as 2.0.56 and is small and intentional.

Log: fix action buttons in the notification center

Influence:
1. Click action buttons on notifications in the notification center and
   verify the actions run
2. Verify normal, overlap, and expanded group notifications are removed from
   the center after the action executes
3. Verify manually deleting notifications still works
4. Verify action buttons in bubbles and the staging area still work
5. Verify a notification enters the history area after its bubble expires
6. Trigger both x-deepin-action and non-extended actions and verify the
   dde-shell log has no "Cannot create children for a parent that is in a
   different thread" warning

fix(notification): 恢复动作派发的同步调用

过期逻辑重构把 NotifyServerApplet::actionInvoked() 从
Qt::DirectConnection 改成了 Qt::QueuedConnection,而
NotifyModel::invokeAction() 仍在派发动作后立即删除通知。排队后的动作
可能晚于删除到达 manager,此时实体已不存在,导致通知中心的动作按钮无效。

恢复同步派发,确保动作在 remove() 删除实体前已由 manager 处理。通知中心
只保留一个删除入口:remove() 负责用户操作(关闭、忽略和点击动作)对视图
与数据库的删除。气泡消失后进入历史通知仍通过
NotificationStateChanged(Processed) 处理,不经过该删除入口。

由于 actionInvoked() 现在在主线程执行,而 manager 位于工作线程,不能再把
manager 作为 XdgActivation 的 parent,否则会触发 "Cannot create children
for a parent that is in a different thread" 警告。改为不设置 parent,由
tokenReady 回调在 token 到达后 deleteLater() 释放。manager 为长生命周期
对象,因此不再为这个短生命周期对象额外保留 manager 销毁时的清理连接。

该同步阻塞与 2.0.56 一致,影响较小且是有意保留的。

Log: 修复通知中心动作按钮点击无效的问题

Influence:
1. 在通知中心点击通知动作按钮,确认动作正常执行
2. 确认普通、重叠及展开的分组通知在动作执行后从通知中心移除
3. 确认手动删除通知仍然正常
4. 确认气泡和通知中心 staging 区域的动作按钮仍然正常
5. 确认气泡过期后通知进入历史通知区域
6. 分别触发 x-deepin-action 和普通 action,确认 dde-shell 日志中不再出现
   "Cannot create children for a parent that is in a different thread" 警告

PMS: BUG-380339
@Ivy233
Ivy233 force-pushed the fix/notification-action-invoke-race branch from 7f94ba6 to d25ea58 Compare October 28, 2026 04:40
@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

🤖 AI 代码审查报告

总体评分: 84 分 (通过阈值: 70分)

Pass


📊 总体评价

项目 结果
审查结论 代码审查通过
评分详情 本次提交修复了通知中心动作按钮的两个问题:移除XdgActivation的父对象以避免双重释放, 将QueuedConnection改为DirectConnection以确保动作在通知删除前同步执行。代码注释完善, 无安全漏洞。但存在两处需关注的问题:(1) DirectConnection导致actionInvoked在调用方线程 (主线程)而非manager的工作线程执行,存在线程安全/数据竞争风险,建议改用 BlockingQueuedConnection;(2) XdgActivation移除父对象后,若tokenReady未触发或 NotificationManager先于token到达被销毁,对象将泄漏,建议添加destroyed安全网连接。

🔍 详细分析

1. 语法逻辑 ❌

评价: 存在逻辑缺陷 ❌ 不通过

潜在问题:
✅ 未发现明显问题

建议: ['将notifyserverapplet.cpp中两处Qt::DirectConnection替换为Qt::BlockingQueuedConnection,既保证同步执行顺序又确保在正确的线程执行', '在notificationmanager.cpp两处XdgActivation创建后添加安全网连接:connect(this, &QObject::destroyed, activation, &QObject::deleteLater); 确保所有路径下对象都能被清理']


2. 代码质量 ✅

评价: 代码结构清晰,注释完整 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: []


3. 代码性能 ✅

评价: 性能良好,资源使用合理 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: []


4. 代码安全 🔒

评价: 存在0个安全漏洞 ✅ 通过

🔐 发现 0 个安全漏洞

安全漏洞详情:
✅ 未发现安全漏洞

建议: []


💡 改进建议代码示例

// === notifyserverapplet.cpp 修复 ===
// 使用 BlockingQueuedConnection 替代 DirectConnection,
// 保证同步执行顺序的同时确保在 manager 的工作线程执行

void NotifyServerApplet::actionInvoked(qint64 id, uint bubbleId, const QString &actionKey)
{
    QMetaObject::invokeMethod(m_manager, "actionInvoked", Qt::BlockingQueuedConnection,
                              Q_ARG(qint64, id), Q_ARG(uint, bubbleId), Q_ARG(QString, actionKey));
}

void NotifyServerApplet::actionInvoked(qint64 id, const QString &actionKey)
{
    QMetaObject::invokeMethod(m_manager, "actionInvoked", Qt::BlockingQueuedConnection,
                              Q_ARG(qint64, id), Q_ARG(QString, actionKey));
}

// === notificationmanager.cpp 修复 ===
// 保留无父对象创建,但添加 destroyed 安全网连接防止泄漏

// actionInvoked() 中(约第147行):
auto *activation = new DS_NAMESPACE::XdgActivation();
connect(this, &QObject::destroyed, activation, &QObject::deleteLater);
connect(activation, &DS_NAMESPACE::XdgActivation::tokenReady, this,
    [activation](const QString &token) {
        // ... 使用 token ...
        activation->deleteLater();
    });

// doActionInvoked() 中(约第566行):
auto *activation = new DS_NAMESPACE::XdgActivation();
connect(this, &QObject::destroyed, activation, &QObject::deleteLater);
connect(activation, &DS_NAMESPACE::XdgActivation::tokenReady, this,
    [amArgsCopy, activation](const QString &token) {
        // ... 使用 token ...
        activation->deleteLater();
    });

本报告由 AI 代码审查工具自动生成

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743, Ivy233

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

@Ivy233

Ivy233 commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

/forcemerge

@deepin-bot

deepin-bot Bot commented Oct 28, 2026

Copy link
Copy Markdown

This pr force merged! (status: blocked)

@deepin-bot
deepin-bot Bot merged commit 3323968 into linuxdeepin:master Oct 28, 2026
7 of 10 checks passed
@Ivy233
Ivy233 deleted the fix/notification-action-invoke-race branch October 28, 2026 05:07
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