Skip to content

fix: possible proxy model desync bug on model reset - #1695

Open
BLumia wants to merge 1 commit into
linuxdeepin:masterfrom
BLumia:fixfix
Open

fix: possible proxy model desync bug on model reset#1695
BLumia wants to merge 1 commit into
linuxdeepin:masterfrom
BLumia:fixfix

Conversation

@BLumia

@BLumia BLumia commented Aug 10, 2026

Copy link
Copy Markdown
Member

Bug 1 (RoleCombineModel 缺少 modelReset 转发) — 可触发

AbstractWindowMonitor::clearTrackedWindows() 会调用 beginResetModel() / endResetModel(),在以下时机被调用:

treelandwindowmonitor.cpp:105 和 x11windowmonitor.cpp:94 — 当显示服务器重连或 compositor 重启时,窗口监视器会清空所有跟踪窗口并复位模型 这个 modelReset 会穿过 BoolFilterModel 到达 RoleCombineModel 的 major model。修复前,复位后 m_indexMap 残留旧映射且下游收不到 reset 信号,导致新旧数据冲突。这是一个生产环境可触发的 bug。

Bug 2 (RoleGroupModel rowsRemoved 跳过后续分组) — 当前不可触发,属于防御性修复

RoleGroupModel 的源模型是 DockGlobalElementModel,其所有删除路径(destroyWindow 单窗口关闭、DockGlobalElementModel 内部循环)都是逐个调用 beginRemoveRows(pos, pos) / endRemoveRows(),每次只有 first == last。因此 range 分支在当前代码中永远不会被执行,--i 缺失不会造成问题。但 handler 的接口签名支持 first..last 范围,LargeDataMemoryStabilityTest 测试也直接传入了范围,若未来有代码路径直接传入多行范围,就会触发。这是一个防御性修复。

Log:

Summary by Sourcery

Fix potential desync issues between RoleCombineModel/RoleGroupModel and their source models when models are reset or rows are removed in ranges.

Bug Fixes:

  • Ensure RoleCombineModel rebuilds its index mapping and emits modelReset when the major source model is reset, preventing stale mappings and ghost/duplicate dock items.
  • Handle minor source model resets in RoleCombineModel test coverage to guard against stale mappings after apps model clear/reset operations.
  • Correct RoleGroupModel rowsRemoved handling for multi-row ranges so later groups are not skipped and child rows are consistently removed.
  • Add tests verifying RoleGroupModel emits child-level dataChanged with a valid parent and that downstream models can handle this correctly.

Enhancements:

  • Extend test models with explicit reset/clear helpers to simulate modelReset scenarios for RoleCombineModel.
  • Add targeted unit tests covering RoleCombineModel reset forwarding and RoleGroupModel range removal behavior for window/task management.

Tests:

  • Introduce new RoleGroupModel tests for range-based row removal and child dataChanged parent validity to capture previous edge-case bugs.
  • Add RoleCombineModel tests for major and minor source model resets to ensure index map reconstruction and signal forwarding behave correctly.

Bug 1 (RoleCombineModel 缺少 modelReset 转发) — 可触发

AbstractWindowMonitor::clearTrackedWindows() 会调用 beginResetModel() / endResetModel(),在以下时机被调用:

treelandwindowmonitor.cpp:105 和 x11windowmonitor.cpp:94 — 当显示服务器重连或 compositor 重启时,窗口监视器会清空所有跟踪窗口并复位模型
这个 modelReset 会穿过 BoolFilterModel 到达 RoleCombineModel 的 major model。修复前,复位后 m_indexMap 残留旧映射且下游收不到 reset 信号,导致新旧数据冲突。这是一个生产环境可触发的 bug。

Bug 2 (RoleGroupModel rowsRemoved 跳过后续分组) — 当前不可触发,属于防御性修复

RoleGroupModel 的源模型是 DockGlobalElementModel,其所有删除路径(destroyWindow 单窗口关闭、DockGlobalElementModel 内部循环)都是逐个调用 beginRemoveRows(pos, pos) / endRemoveRows(),每次只有 first == last。因此 range 分支在当前代码中永远不会被执行,--i 缺失不会造成问题。但 handler 的接口签名支持 first..last 范围,LargeDataMemoryStabilityTest 测试也直接传入了范围,若未来有代码路径直接传入多行范围,就会触发。这是一个防御性修复。

Log:
@BLumia
BLumia requested a review from Ivy233 August 10, 2026 02:21

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

Sorry @BLumia, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: BLumia

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

sourcery-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds reset handling and defensive mapping fixes to proxy models used by the dock task manager, plus targeted test coverage to reproduce and guard against two bugs in RoleCombineModel and RoleGroupModel.

Sequence diagram for modelReset handling in RoleCombineModel

sequenceDiagram
    actor AbstractWindowMonitor
    participant SourceModel as QAbstractItemModel_major
    participant RoleCombineModel

    AbstractWindowMonitor->>SourceModel: clearTrackedWindows()
    SourceModel->>SourceModel: beginResetModel()
    SourceModel-->>RoleCombineModel: modelReset
    RoleCombineModel->>RoleCombineModel: beginResetModel()
    RoleCombineModel->>RoleCombineModel: m_indexMap.clear()
    RoleCombineModel->>SourceModel: rowCount()
    RoleCombineModel->>SourceModel: columnCount()
    loop rebuild_indexMap
        RoleCombineModel->>SourceModel: index(i, j)
        RoleCombineModel->>RoleCombineModel: func(majorIndex.data(majorRoles), m_minor)
        RoleCombineModel->>RoleCombineModel: m_indexMap[qMakePair(i, j)] = qMakePair(minorRow, minorColumn)
    end
    RoleCombineModel->>RoleCombineModel: endResetModel()
    SourceModel->>SourceModel: endResetModel()
Loading

File-Level Changes

Change Details Files
RoleCombineModel now forwards modelReset from its major source model and rebuilds its internal index map to keep combined data in sync.
  • Connects major source model’s modelReset signal to a lambda that wraps beginResetModel/endResetModel on RoleCombineModel.
  • Clears m_indexMap and fully recomputes the major->minor index mappings during reset, using the existing combine function and role mapping.
  • Ensures rowCount reflects the cleared major model and that downstream views receive a modelReset when the major model resets.
panels/dock/taskmanager/rolecombinemodel.cpp
RoleGroupModel’s row-removal logic is hardened so that batch removals that empty a group do not cause subsequent groups to be skipped.
  • Adjusts the loop over m_rowMap entries in the rowsRemoved handler to decrement the iterator index after removing an empty group, avoiding skipping the next entry.
  • Keeps adjustMap behavior unchanged but now guarantees all affected groups are processed even when first..last spans multiple rows.
panels/dock/taskmanager/rolegroupmodel.cpp
Test support models gain explicit reset helpers to simulate modelReset scenarios in unit tests.
  • Adds TestModelA::resetModel which performs beginResetModel, deletes all stored DataA objects, clears the list, and ends the reset.
  • Adds TestModelB::clear which similarly resets and clears all stored DataB objects.
tests/panels/dock/taskmanager/combinemodela.cpp
tests/panels/dock/taskmanager/combinemodelb.cpp
tests/panels/dock/taskmanager/combinemodela.h
tests/panels/dock/taskmanager/combinemodelb.h
Unit tests are expanded to reproduce and validate the fixed bugs in RoleGroupModel and RoleCombineModel.
  • Adds RoleGroupModel::RowsRemovedRangeSkip test that constructs multiple groups and removes a row range to ensure all groups are updated and mappings stay valid.
  • Adds RoleGroupModel::ChildDataChangedHasValidParent test that verifies child-level dataChanged emissions with a valid parent index behave as expected for downstream models.
  • Adds RoleCombineModel::MajorModelResetForwarding test that exercises major model reset, checks modelReset emission, and validates that the index map is rebuilt correctly.
  • Adds RoleCombineModel::MinorModelResetForwarding test that simulates minor model reset via clear(), ensuring combined mapping still finds the correct updated data and doesn’t crash.
tests/panels/dock/taskmanager/rolegroupmodeltests.cpp
tests/panels/dock/taskmanager/rolecombinemodeltests.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

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

★ 总体评分:95分

■ 【总体评价】

代码精准修复了任务栏幽灵图标和分组索引越界两个核心BUG,逻辑严密且配套了高质量的单元测试。
修复点直击要害且符合Qt模型规范,因测试中暴露出部分尚未修复的遗留缺陷扣5分。

■ 【详细分析】

  • 1.语法逻辑(完全正确)✓

RoleCombineModel 中新增的 modelReset 槽函数正确调用了 beginResetModel()endResetModel() 包裹状态清理与映射重建,完全遵循 Qt 的 Model/View 架构规范;RoleGroupModel 中增加的 --i 操作精准抵消了 removeOne 引发的隐式下标前移,彻底解决了批量删除时的跳过与负数索引问题。
潜在问题:测试用例 MinorModelResetForwardingChildDataChangedHasValidParent 的注释明确指出了当前系统仍存在两个未修复的遗留逻辑缺陷,即 minor 源模型复位时映射未重建、子级 dataChanged 信号被下游错误解析,但这些问题不属于本次 diff 引入的错误。
建议:在后续迭代中补充对 minor 源模型 modelReset 信号的监听处理;在 RoleGroupModel 转发 dataChanged 时过滤或平铺子级索引,防止下游越界。

  • 2.代码质量(良好)✓

修复代码极其精简,没有引入任何冗余逻辑。配套的测试代码质量极高,不仅覆盖了正常重置与批量删除场景,还在注释中详细记录了 BUG 的触发链路(如“m_indexMap 残留旧映射->下游不清理->幽灵图标”),极大地提升了代码的可维护性。
潜在问题:测试用例中验证了未修复的 BUG(如 minor 模型重置),这可能导致该特定测试用例处于失败或仅验证不崩溃的弱断言状态,影响 CI 流水线的红绿状态判断。
建议:对于已知但未在本次修复的 BUG 测试用例,建议加上 DISABLED_ 前缀或使用 EXPECT_NO_DEATH 替代强业务断言,待修复后再启用。

  • 3.代码性能(无性能问题)✓

modelReset 槽内使用了双重 for 循环重建 m_indexMap,时间复杂度为 O(N*M)。但 modelReset 信号本身属于极低频事件(仅在窗口监控器大规模刷新或初始化时触发),且全量重建是保证映射一致性的必要代价,不存在性能瓶颈。
建议:保持现状,无需针对此低频路径进行过度优化。

  • 4.代码安全(存在0个安全漏洞)✓

漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个
本次变更完全局限于内存中的 Qt 模型索引映射与信号槽转发,不涉及任何外部输入解析、文件系统操作、进程创建或网络通信,攻击面为零,无安全风险。
建议:继续保持纯内存数据结构操作的隔离性。

■ 【改进建议代码示例】

// 针对测试用例中暴露的遗留问题:补充 minor 源模型 modelReset 信号的处理
// 文件:panels/dock/taskmanager/rolecombinemodel.cpp
// 在构造函数中,紧接现有的 major modelReset connect 之后添加:

    // forward modelReset from minor source
    if (m_minor) {
        connect(m_minor, &QAbstractItemModel::modelReset, this, [this, majorRoles, func]() {
            beginResetModel();
            m_indexMap.clear();
            int rowCount = sourceModel()->rowCount();
            int columnCount = sourceModel()->columnCount();
            for (int i = 0; i < rowCount; i++) {
                for (int j = 0; j < columnCount; j++) {
                    QModelIndex majorIndex = sourceModel()->index(i, j);
                    QModelIndex minorIndex = func(majorIndex.data(majorRoles), m_minor);
                    if (majorIndex.isValid() && minorIndex.isValid())
                        m_indexMap[qMakePair(i, j)] = qMakePair(minorIndex.row(), minorIndex.column());
                }
            }
            endResetModel();
        });
    }

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.

2 participants