Skip to content

fix[utest]: reset finish flags in the mailbox testcase so it can run twice - #11963

Merged
Rbb666 merged 2 commits into
RT-Thread:masterfrom
sexenteen:fix/issue-6565-mailbox-utest-rerun
Nov 3, 2026
Merged

Rbb666 merged 2 commits into
RT-Thread:masterfrom
sexenteen:fix/issue-6565-mailbox-utest-rerun

Conversation

@sexenteen

@sexenteen sexenteen commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

拉取/合并请求描述:(PR description)

[

为什么提交这份PR (why to submit this PR)

Fixes #6565 (the remaining half - see the note at the end).

core.ipc_mailbox fails when it is run a second time in the same boot. Reproduced on
bsp/qemu-vexpress-a9 with RT_USING_UTESTCASES=y and RT_UTEST_MAILBOX=y:

msh />utest_run core.ipc_mailbox      -> [  PASSED  ] testcase (core.ipc_mailbox)
msh />utest_run core.ipc_mailbox      -> [  ASSERT  ] at (mailbox_tc.c); func:
                                         (thread1_recv_static_mb:242); msg: ((1) is true)
                                         (rt_object_get_type(&mb->parent.parent) ==
                                          RT_Object_Class_MailBox) assertion failed
                                          at function:_rt_mb_recv, line number:2884

Cause: the four *_thread_finish flags are only ever set to 1 by the worker threads, never
cleared. On the second run the test body's wait loop

while (static_mb_recv_thread_finish != 1 || static_mb_send_thread_finish != 1)

falls through immediately, so rt_mb_detach() is called while the worker threads started by
this run are still blocked in rt_mb_recv(). They are then resumed with an error and keep
using a mailbox that has already been detached, which trips the object-type assertion in
_rt_mb_recv(). The dynamic variant has the same pattern and would fail the same way.

你的解决方案是什么 (what is your solution)

Clear the flags in utest_tc_init(), which the utest framework calls before each run of the
testcase:

static rt_err_t utest_tc_init(void)
{
    static_mb_recv_thread_finish = 0;
    static_mb_send_thread_finish = 0;
    dynamic_mb_recv_thread_finish = 0;
    dynamic_mb_send_thread_finish = 0;

    return RT_EOK;
}

Only src/utest/mailbox_tc.c is touched (+11/-0). This matches what has already been said in
#6565: part of the testcases were written without considering that they can be executed twice,
leaving variables/resources not reset or released.

请提供验证的bsp和config (provide the config and bsp)

Verification (utest_run core.ipc_mailbox executed three times in one session, after the fix):

run 1 -> [  PASSED  ] testcase (core.ipc_mailbox)
run 2 -> [  PASSED  ] testcase (core.ipc_mailbox)
run 3 -> [  PASSED  ] testcase (core.ipc_mailbox)

The same build without the fix fails on run 2 as shown above. scons -j4 passes and
tools/ci/clang_format_check.py reports "clang-format check success" for the changed file.

Not covered: other BSPs / real hardware (only qemu-vexpress-a9 was tested), and running the
whole utest suite (utest_run without arguments) - only the mailbox testcase was exercised.

Note on the rest of #6565: the other reported problem, thread_tc.c's test_thread_suspend
suspending another thread (which hits RT_ASSERT(thread == rt_thread_self())), was already
confirmed by the maintainers as a testcase problem and that test is already disabled, so it is
not touched here.
]

当前拉取/合并请求的状态 Intent for your PR

必须选择一项 Choose one (Mandatory):

  • 本拉取/合并请求是一个草稿版本 This PR is for a code-review and is intended to get feedback
  • 本拉取/合并请求是一个成熟版本 This PR is mature, and ready to be integrated into the repo

代码质量 Code Quality:

我在这个拉取/合并请求中已经考虑了 As part of this pull request, I've considered the following:

  • 已经仔细查看过代码改动的对比 Already check the difference between PR and old code
  • 代码风格正确,包括缩进空格,命名及其他风格 Style guide is adhered to, including spacing, naming and other styles
  • 没有垃圾代码,代码尽量精简,不包含#if 0代码,不包含已经被注释了的代码 All redundant code is removed and cleaned up
  • 所有变更均有原因及合理的,并且不会影响到其他软件组件代码或BSP All modifications are justified and not affect other components or BSP
  • 对难懂代码均提供对应的注释 I've commented appropriately where code is tricky
  • 代码是高质量的 Code in this PR is of high quality
  • 已经使用clang-format 源码格式化工具确保格式符合RT-Thread代码规范 This PR has been formatted with clang-format and complies with RT-Thread code specification
  • 如果是新增bsp, 已经添加ci检查到.github/ALL_BSP_COMPILE.json 详细请参考链接BSP自查

@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown

👋 感谢您对 RT-Thread 的贡献!Thank you for your contribution to RT-Thread!

为确保代码符合 RT-Thread 的编码规范,请在你的仓库中执行以下步骤运行代码格式化工作流(如果格式化CI运行失败)。
To ensure your code complies with RT-Thread's coding style, please run the code formatting workflow by following the steps below (If the formatting of CI fails to run).


🛠 操作步骤 | Steps

  1. 前往 Actions 页面 | Go to the Actions page
    点击进入工作流 → | Click to open workflow →

  2. 点击 Run workflow | Click Run workflow

  • Use workflow from 保持默认分支(通常为 master)
    Keep the default branch (usually master) in Use workflow from
  • 在 branch 输入框填写 PR 分支 fix/issue-6565-mailbox-utest-rerun
    Enter PR branch fix/issue-6565-mailbox-utest-rerun in the branch field
  • 设置需排除的文件/目录(目录请以"/"结尾)
    Set files/directories to exclude (directories should end with "/")
  1. 等待工作流完成 | Wait for the workflow to complete
    格式化后的代码将作为独立提交推送至你的分支。
    The formatting changes will be pushed to your branch as a separate commit.

完成后,提交将自动更新至 fix/issue-6565-mailbox-utest-rerun 分支,关联的 Pull Request 也会同步更新。
Once completed, commits will be pushed to the fix/issue-6565-mailbox-utest-rerun branch automatically, and the related Pull Request will be updated.

如有问题欢迎联系我们,再次感谢您的贡献!💐
If you have any questions, feel free to reach out. Thanks again for your contribution!

@github-actions github-actions Bot added the Kernel PR has src relate code label Nov 1, 2026
@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

📌 Code Review Assignment

🏷️ Tag: kernel

Reviewers: @GorrayLi @ReviewSun @hamburger-os @lianux-mm @wdfk-prog @xu18838022837

Changed Files (Click to expand)
  • src/utest/mailbox_tc.c

📊 Current Review Status (Last Updated: 2026-11-02 15:53 CST)


📝 Review Instructions

  1. 维护者可以通过单击此处来刷新审查状态: 🔄 刷新状态
    Maintainers can refresh the review status by clicking here: 🔄 Refresh Status

  2. 确认审核通过后评论 LGTM/lgtm
    Comment LGTM/lgtm after confirming approval

  3. PR合并前需至少一位维护者确认
    PR must be confirmed by at least one maintainer before merging

ℹ️ 刷新CI状态操作需要具备仓库写入权限。
ℹ️ Refresh CI status operation requires repository Write permission.

@Yunlk

Yunlk commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

独立复现了一遍,现象和根因都对得上。补一组"第一次 / 第二次"的干净对照数据。

环境

bsp/qemu-vexpress-a9,QEMU 11.0.0,arm-none-eabi-gcc 15.2.1,master 1c801efa。
配置:CONFIG_RT_USING_UTESTCASES=y + CONFIG_RT_UTEST_MAILBOX=y,CONFIG_RT_UTEST_USING_AUTO_RUN 必须关掉(见最后一段)。

复现方式

msh 里分两次执行 utest_run core.ipc_mailbox。我这里没有可脚本化的串口输入,改用等价方式:一个临时 app 在开机后直接调用两次 utest_testcase_run(2, argv),中间隔 1 秒。

结果

第一次:

[I/utest] [----------] [ testcase ] (core.ipc_mailbox) started
[I/utest] [  PASSED  ] [ result   ] testcase (core.ipc_mailbox)
[I/utest] [----------] [ testcase ] (core.ipc_mailbox) finished
[I/utest] [  PASSED  ] [ result   ] 1 tests.

第二次:

[I/utest] [----------] [ testcase ] (core.ipc_mailbox) started
[E/utest] [  ASSERT  ] [ unit     ] at (mailbox_tc.c); func: (thread1_recv_static_mb:242); msg: ((1) is true)
(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox) assertion failed at function:_rt_mb_recv, line number:2884

断言原文是 uassert_false(1),落在 thread1_recv_static_mb() 第 242 行——即第二个 rt_mb_recv(&test_static_mb, ...) 返回了非 RT_EOK 或数据对不上。紧随其后的 _rt_mb_recv 对象类型断言是同一个问题的二次表现:此时 test_static_mb 已经被 rt_mb_detach() 掉了,而工作线程还在里面等。

根因确认:是四个标志没清

/* mailbox_tc.c:60-61 */
static uint8_t static_mb_recv_thread_finish, static_mb_send_thread_finish;
static uint8_t dynamic_mb_recv_thread_finish, dynamic_mb_send_thread_finish;

它们只在工作线程里置 1(:226、:251、:319、:344),没有任何地方清零;utest_tc_init() 原本直接 return RT_EOK。

第二次运行时,while (static_mb_recv_thread_finish != 1 || static_mb_send_thread_finish != 1)(:282)这个等待循环条件一开始就成立,于是测试体不等工作线程结束就继续往下走,接着 test_mailbox_deatch() 把 mailbox detach 掉,而工作线程还阻塞在 rt_mb_recv() 里——于是第 242 行拿到错误返回,随后撞上对象类型断言。

与 #11963 里 utest_tc_init() 清零四个标志的改法一致,我这边观察到的顺序也能对上。

一个可能影响别人复现的坑

CONFIG_RT_USING_CI_ACTION=y 会 select RT_UTEST_USING_AUTO_RUN(src/Kconfig:252-255),开机就自动把所有用例跑一遍。如果不关掉这个,你手动执行的"第一次"实际上是第二次,直接就会看到失败,容易误以为"第一次就挂"。

我第一次就是这么踩的:自动运行先跑了一次 PASSED,然后我的第一次手动运行就 FAILED 了。要拿到干净对照,需要同时确认 CONFIG_RT_UTEST_USING_AUTO_RUN 处于关闭状态——它是个独立选项,光取消 CI_ACTION 不一定够(要看配置是否被显式 select 过)。

复现用的临时 app 没有提交,需要的话我可以整理成 utest_run 之外的自动化形式。

@sexenteen

Copy link
Copy Markdown
Contributor Author

感谢独立复现,根因和改法能对上就很难得 👍

关于 RT_UTEST_USING_AUTO_RUN 的坑:我这边复现时没有开 RT_USING_CI_ACTION,只打开了
CONFIG_RT_USING_UTESTCASES + CONFIG_RT_UTEST_MAILBOX(RT_UTEST_USING_AUTO_RUN 保持关闭),
所以"第一次 PASSED、第二次 FAILED"是在干净状态下拿到的;修复后同一会话连跑三次都是 PASSED。

你的提醒很有价值 —— 我把它当成"复现前提"记下来了:如果 CI_ACTION/自动运行开着,开机那次会消耗掉"第一次"
(src/Kconfig:252-255 的 select RT_UTEST_USING_AUTO_RUN),后面手动跑的第一次其实已经是第二次,会直接看到失败。

另外:我已经把这个分支合到最新 master(现在是 08cad4ee,版本号 5.4.0),合并后重新验证过 ——
utest_run core.ipc_mailbox 连跑两次均 [ PASSED ]。你用的 1c801efa 上也应该同样成立。

你那个"临时 app 调用 utest_testcase_run() 两次"的做法对自动化很有价值,如果愿意整理成
utest_run 之外的自动化形式(比如 CI 里跑两次的回归),我认为可以单独提一个 PR。

@Rbb666

Rbb666 commented Nov 3, 2026

Copy link
Copy Markdown
Member

@codex review

@Rbb666

Rbb666 commented Nov 3, 2026

Copy link
Copy Markdown
Member

utest的命令是支持某个测试用例运行多次的。可以试试给ci的utest run加个参数,冒烟测试几次看看结果,过程中也通过ps/free命令观测到内存泄漏等问题

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

@Rbb666

Rbb666 commented Nov 3, 2026

Copy link
Copy Markdown
Member

@sexenteen 注意下,下次的PR记得变基到master分支,要不然会多出一笔commit,这次我先压缩合并下了

@Rbb666
Rbb666 merged commit 50693c3 into RT-Thread:master Nov 3, 2026
112 checks passed
@sexenteen

Copy link
Copy Markdown
Contributor Author

收到,感谢提醒!后续 PR 我会用 git rebase origin/master 更新分支,不再用 merge,避免多出 merge commit。

关于你提的 utest 重复运行:我确认过 utest_run <case> <loop> 本身已经支持跑多次(components/utilities/utest/utest.c 里的 tc_loop,见 utest_run -help 的第 4/6/8 条用法),缺的是 CI 这一侧 —— .github/workflows/utest_auto_run.yml 目前只跑一遍,也没有内存观测。

这个任务我接下来做,打算这样实现:

  1. 给 CI 的 utest 运行加一个"重复次数"参数,默认 1(保持现有行为不变);
  2. 冒烟测试跑 N 次,每次之间采集 free 的 used/maximum 和 ps 的线程数、栈占用;
  3. 汇总成一份对比:如果 used/maximum 随次数单调增长,就标记为疑似泄漏,并把原始输出附在 CI 日志里。

我先按这个思路做,方案有问题的话你直接说,我按你的意见调整。

@sexenteen

Copy link
Copy Markdown
Contributor Author

按你的建议做了,提了 #11983(feat[utest]: repeat the automatic run and sample free/ps between loops)。

做法:

  • 新增 Kconfig RT_UTEST_AUTO_RUN_LOOP(1..100,默认 1,不配置时行为不变);
  • 自动运行按该值循环,每轮结束后打印 loop x/N runtime info 并执行 free / ps;
  • utest_auto_run 工作流加 workflow_dispatch 参数 utest_loop,>1 时注入该配置,日志监控超时按 loop × 20 分钟放大。

qemu-vexpress-a9 上跑 3 轮验证过:三轮 used 都是 42888、ps 线程表一致(这个用例不泄漏)。
方案或命名有问题的话直接说,我改。

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

Labels

Kernel PR has src relate code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

几处未知原因的rtthread utest问题

3 participants