Repository navigation
Conversation
|
Skipping CI for Draft Pull Request. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: Ivy233 The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
Reviewer's GuideThis PR defers starting the notification expiration timer until a bubble is actually inserted into the UI model, wiring a new bubbleDisplayed signal through BubblePanel to NotificationManager, which now schedules timeouts based on the stored client expire timeout only when notifications are displayed, with thread-safe forwarding from the applet and added unit tests. Sequence diagram for deferred notification timeout start when bubble is displayedsequenceDiagram
participant BubbleModel
participant BubblePanel
participant NotifyServerApplet
participant NotificationManager
BubbleModel->>BubbleModel: insertBubble / replaceBubble
BubbleModel-->>BubblePanel: bubbleDisplayed(id)
BubblePanel->>NotifyServerApplet: notificationDisplayed(id)
NotifyServerApplet->>NotificationManager: notificationDisplayed(id)
NotificationManager->>NotificationManager: fetchEntity(id)
NotificationManager->>NotificationManager: pushPendingEntity(entity, entity.timeout())
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
430dd63 to
fcf70d8
Compare
0b0eb8c to
2ae625f
Compare
2ae625f to
5ef467e
Compare
5ef467e to
aa5a30c
Compare
aa5a30c to
fadfda6
Compare
fadfda6 to
e12655f
Compare
a5757b6 to
c7b598c
Compare
0ea9c62 to
24d7693
Compare
24d7693 to
8c366c9
Compare
|
回复 auto review 提出的多线程竞态问题:经逐一核实,
另外,示例中在持锁状态下调用 |
|
/test github-pr-review-ci |
|
TAG Bot New tag: 2.0.53 |
|
TAG Bot New tag: 2.0.54 |
8c366c9 to
cdd2040
Compare
|
TAG Bot New tag: 2.0.55 |
|
TAG Bot New tag: 2.0.56 |
f5f29a6 to
db17683
Compare
| // here instead of letting each frontend view call ExpireTimer::remove on | ||
| // its own. remove() by id is a no-op when the entry already expired on its | ||
| // own (its deadline was dropped when expired() was emitted). | ||
| connect(m_manager, &NotificationManager::NotificationStateChanged, this, [](qint64 id, int processedType) { |
There was a problem hiding this comment.
NotificationStateChanged 这个不需要吧,在其它close里的路径里处理,
There was a problem hiding this comment.
已按建议调整:删除了新增的 NotificationStateChanged → ExpireTimer::remove 连接,改为在 NotifyServerApplet::notificationClosed() 主线程槽内直接调用 ExpireTimer::remove(id)。气泡按钮与通知中心的关闭路径都汇入该槽,在这里停表时机最早——先于 queued 投递给 manager 的关闭请求,也缩小了此前注释中提到的竞态窗口;自然到期路径无需 remove(条目在 expired 发出时已删除,remove 为幂等空操作)。
这里删除的是新增的计时器清理连接;init() 中原有的
NotificationStateChanged → NotifyServerApplet::notificationStateChanged
连接仍需保留。它负责把服务端的处理状态转发给前端,BubblePanel 依赖该信号移除已处理或已关闭的通知,并不属于 ExpireTimer 的清理逻辑。
不经过 notificationClosed() 槽的关闭路径(如第三方通过 D-Bus 调用 CloseNotification)无法在自身路径上停表:该调用经 adaptor 直接落在工作线程的 manager 上,而 ExpireTimer 及其 QTimer 具有主线程亲和性,就地调用属于跨线程访问。这类路径由 notificationClosed() 中 processedType != NotProcessed 的守卫兜底:滞留的倒计时条目到期后只会触发一次被守卫拒绝的空跑,不会对同一通知重复发出 NotificationClosed。
Start the expire countdown when a notification is actually displayed instead of when its Notify call reaches the server, so a notification can no longer time out before the user has seen it, and let the views that present the notification drive that countdown: 1. Add ExpireTimer, a process-wide singleton in panels/notification/common that keeps the deadline of every displayed notification in a QMultiHash keyed by absolute time point and drives a shared single-shot QTimer. It is the server's former pending-timeout machinery moved to the display side. push() is idempotent: when the bubble and the notification center show the same entity (same id and cTime), the first deadline is kept instead of restarting it, a replacement notification cancels the countdown of the bubble slot it takes over, and a non-positive timeout (Critical urgency or expireTimeout 0) means the notification never expires. setBlockId() replaces setBlockClosedId() and lets the hovered bubble survive its deadline, with a short grace period after the hover moves away. 2. BubbleModel::insertBubble/replaceBubble and NotifyStagingModel::push/replace/open start the countdown of the entities they display, and the staging area matches replacements by bubble id through the new rowByBubbleId(), since a replacement may reuse the original entity id or arrive with a new one. 3. NotificationManager drops the pending timeout state and its whole timer path (m_pendingTimeout, m_pendingTimeoutEntities, m_blockClosedId, setBlockClosedId, pushPendingEntity, removePendingEntity, onHandingPendingEntities), and notificationClosed() now ignores close requests for an invalid or already processed entity, so a close queued by the expire timer can no longer emit NotificationClosed twice for the same notification. 4. NotifyServerApplet owns the lifecycle boundary: it closes the notification in the manager when ExpireTimer emits expired(), stops the countdown in its notificationClosed() slot, where the close paths of both frontends (the bubble button and the notification center) converge, and delivers its D-Bus slots to the manager, which lives on the worker thread, via QueuedConnection. A close path that bypasses this slot (e.g. the D-Bus CloseNotification called by a client) leaves its countdown entry to fire into notificationClosed(), where the processed guard rejects it. 5. NotifyEntity exposes timeout() and urgency(), the unused BubbleModel::clear() is removed, and expiretimer_tests covers expiry, idempotent push, hover blocking, replacement and Critical notifications. Log: start the expire countdown only after a notification is displayed Influence: 1. Verify notifications shown by the bubble and by the notification center expire after the client-specified timeout (5 s by default) 2. Verify a hovered bubble does not expire, and a replaced notification starts a fresh countdown for the new content 3. Verify Critical notifications and expireTimeout 0 notifications never close by themselves fix(notification): 通知显示后再开始计算过期时间 将过期倒计时的起点从服务端收到 Notify 调用改为通知真正显示之时, 避免通知在用户看到之前就已超时,并由展示通知的视图驱动该倒计时: 1. 新增 ExpireTimer(panels/notification/common 下的进程级单例), 以 QMultiHash 按绝对时间点保存所有已显示通知的截止时间,并用一个共享 的单次 QTimer 驱动,即原服务端的待超时机制迁移至展示侧。push() 幂等: 气泡通知与通知中心展示同一实体(id 与 cTime 相同)时保留首次的截止 时间而不重新计时;替换通知会取消其所占气泡槽位的旧倒计时;超时时间为 非正值(Critical 紧急度或 expireTimeout 为 0)表示通知不会自动过期。 setBlockId() 取代 setBlockClosedId(),阻止悬停中的气泡过期,并在悬停 移开后保留一段短暂的宽限时间。 2. BubbleModel::insertBubble/replaceBubble 与 NotifyStagingModel::push/replace/open 在展示实体时启动其倒计时; 通知中心通过新增的 rowByBubbleId() 按气泡 id 匹配替换通知,因为替换 通知可能沿用原实体 id,也可能带来新的实体 id。 3. NotificationManager 移除待超时状态及整条定时器路径 (m_pendingTimeout、m_pendingTimeoutEntities、m_blockClosedId、 setBlockClosedId、pushPendingEntity、removePendingEntity、 onHandingPendingEntities);notificationClosed() 现在忽略无效或已处理 实体的关闭请求,使过期定时器排队的关闭请求不会对同一通知重复发出 NotificationClosed。 4. NotifyServerApplet 负责生命周期边界:ExpireTimer 发出 expired() 时 在 manager 中关闭对应通知;在气泡按钮与通知中心两条前端关闭路径共同 汇入的 notificationClosed() 槽中停止倒计时;由于 manager 位于工作 线程,其 D-Bus 槽改为 QueuedConnection 投递。不经过该槽的关闭路径 (如客户端通过 D-Bus 调用 CloseNotification)由 notificationClosed() 中对已处理实体的守卫拒绝。 5. NotifyEntity 新增 timeout()/urgency() 接口,删除已无用的 BubbleModel::clear(),并新增 expiretimer_tests 覆盖过期、幂等 push、 悬停阻塞、替换通知和 Critical 通知等场景。 Log: 通知显示后才开始过期倒计时 Influence: 1. 验证气泡通知与通知中心展示的通知按客户端指定的超时时间(默认 5 秒)过期 2. 验证悬停中的气泡不过期,替换通知按新内容重新开始倒计时 3. 验证 Critical 通知和 expireTimeout 为 0 的通知不会自动关闭 PMS: BUG-372279
db17683 to
420992a
Compare
deepin pr auto review🤖 AI 代码审查报告📊 总体评价
🔍 详细分析1. 语法逻辑 ✅评价: 优秀 ✅ 通过 潜在问题:
建议: 建议在timer间隔转换处增加上限保护,如 qMin(interval, INT_MAX) 防止极端情况下的溢出 2. 代码质量 ✅评价: 优秀 ✅ 通过 潜在问题:
建议: 代码注释质量优秀,设计决策文档化程度高。建议确认clear()移除后无编译期遗漏的调用方 3. 代码性能 ✅评价: 优秀 ✅ 通过 潜在问题:
建议: 对于通知系统典型的小规模数据(通常<20个),当前O(n)性能完全可接受。单QTimer设计配合最近截止时间驱动是高效的方案 4. 代码安全 🔒评价: 优秀 ✅ 通过
安全漏洞详情: 建议: 线程安全性通过将Qt::DirectConnection改为Qt::QueuedConnection得到改善。notificationClosed中新增的processedType()守卫有效防止了竞态条件下的重复关闭问题。无用户输入注入、无硬编码凭证、无缓冲区溢出风险。 💡 改进建议代码示例// expiretimer.cpp - 建议在timer间隔转换处增加上限保护
void ExpireTimer::onTimeout()
{
// ... 处理过期实体 ...
if (m_pendingEntities.isEmpty()) {
m_timer->stop();
m_lastPoint = std::numeric_limits<qint64>::max();
return;
}
auto points = m_pendingEntities.keys();
// 优化:使用 std::min_element 替代 std::sort,O(n) vs O(n log n)
m_lastPoint = *std::min_element(points.begin(), points.end());
// 增加上限保护,防止极端timeout值导致int溢出
auto rawInterval = qMax<qint64>(0, m_lastPoint - QDateTime::currentMSecsSinceEpoch());
int safeInterval = static_cast<int>(qMin<qint64>(rawInterval, std::numeric_limits<int>::max()));
m_timer->start(safeInterval);
}本报告由 AI 代码审查工具自动生成 |
NotificationManagerto a frontendExpireTimersingleton, keeping one shared QTimer and absolute deadlines in a singleQMultiHashwhile removing all server-side timer stateExpireTimer::pushidempotent for the same entity (same id + cTime), so the bubble and the staging area share the first deadline instead of restarting it, and cancel the old bubble-slot countdown when a replacement arrivesExpireTimer::setBlockIdwith a short grace period, without spinning the shared timer at a zero intervalNotificationManager::notificationClosedvia a queued invocation, and countdowns are stopped from the server'sNotificationStateChangedinstead of frontend model removal;notificationClosednow reports a close only once per notificationNotifyEntity(timeout 0 or Critical urgency never expires, -1 falls back to the 5000 ms default) and carrybubbleIdin theexpiredsignalBubbleModel::replaceBubbleIndexNotificationManager/NotifyServerAppletand its outdated testsdataChangedinDockGlobalElementModel, fix refresh of multi-mapped rows inRoleCombineModel)Log: Start the notification expire countdown only after the notification is displayed
Influence:
fix: 通知显示后才启动过期计时
NotificationManager中的 pending-timeout 机制整体迁移到前端ExpireTimer单例:保留单个共享 QTimer 与按绝对截止时间组织的QMultiHash,移除服务端全部定时器状态ExpireTimer::push对同一通知(相同 id + cTime)幂等:气泡与暂存区共享首次截止时间、不重复计时;替换通知按 bubbleId 取消旧槽位倒计时后再启动新计时ExpireTimer::setBlockId,移开悬停后有短暂宽限期,共享 QTimer 不会以 0 间隔空转NotificationManager::notificationClosed,前端根据服务端NotificationStateChanged停止计时而非自行移除;notificationClosed增加去重,避免同一通知重复上报关闭NotifyEntity推导(timeout 为 0 或 Critical 紧急级别永不过期,-1 回退默认 5000ms),expired信号携带 bubbleIdBubbleModel::replaceBubbleIndex保持一致NotificationManager/NotifyServerApplet中过时的 pending-timeout 代码及其旧测试DockGlobalElementModel转发 dataChanged,RoleCombineModel修复多对一映射行的刷新)Log: 通知显示后才启动过期计时
Influence:
PMS: BUG-372279
Summary by Sourcery
Start notification expiration when notifications become visible and centralize countdown management across presentation models.
Bug Fixes:
Enhancements:
Tests:
Chores: