Skip to content

fix: stop() 清空未消费 seek 槽并锁定状态契约(#112 / #113) - #114

Closed
wt159 wants to merge 1 commit into
masterfrom
implement/seek-stop-state-hygiene
Closed

wt159 wants to merge 1 commit into
masterfrom
implement/seek-stop-state-hygiene

Conversation

@wt159

@wt159 wt159 commented Nov 9, 2026

Copy link
Copy Markdown
Owner

这是什么 / 不是什么

承接 #112 / #113。用户 2026-11-09 明确「强制重审」,主动打破 745ba7b 固化的「SIGSEGV flake 反复出现」三条门槛。

本 PR 不修复 42488656322 attempt 1 的 SIGSEGV:没有复现、没有崩溃栈,CI 崩溃在 Linux / 本机取证在 Windows,根因未定位。本 PR 交付的是「消除一个被取证确认为 100% 命中的状态契约违反 + 建立长期判据」。票、规格与代码注释一律不得宣称修复。

取证(纠正上一轮取证的效力)

上一轮 100 次单用例定向压力跑的是既有用例 test_player_integration.cpp:726,它在 setPosition() 后先轮询等到位置到位、再多等 800ms 才 stop() —— seek 槽早已被消费完,根本不经过「seek 槽未消费完就 stop」窗口,所以 0/100 对候选①没有证伪力。

本轮改用临时诊断 harness(seek 后不轮询、不 sleep、立刻 stop)+ 控制线程侧临时探针(stop() 进入时读 m_seekRequestMs,零解码线程写),三组各 80 轮:

组 rounds played stopped windowHits decoderStops 崩溃
1 80 80 80 160 240 0
2 80 80 80 160 240 0
3 80 80 80 160 240 0

窗口命中率每轮 2/3 次、160/240,三组完全一致(确定性),但 0 崩溃、0 断言失败、0 非零退出。

→ H1 作为「SIGSEGV 根因」在 Windows 侧证伪:该叠加窗口是 100% 命中的常态,不足以解释一次偶发崩溃。但 AudioStreamDecoder::stop() 确实不清理 m_seekRequestMs,而 cancelRequested() 把「槽内有值」当取消 → stop 返回后该谓词仍为真,构成确定的状态契约违反。

改动(3 文件 +47 行)

文件 内容
AudioStreamDecoder.cpp stop() 在 join 之后、置 IDLE 之前补一行 m_seekRequestMs.store(-1);另有两个测试专用方法实现
AudioStreamDecoder.h setPendingSeekForTest(int64_t) / pendingSeekMsForTest() const,SDK 内部类型、不进 soundbridge::Player 公开 API、生产路径零调用
test_stream_decoder.cpp 归 sdk_unit_tests 的确定性判据:不启解码线程、不依赖真实素材、不命中任何时序窗口

stop() 清槽正常路径行为等价:start() 本就在 stop() 之后清槽,state=IDLE 会让 seekToMs() 直接返回,解码线程此时已 join。

验证

  • 判据自检:1 case / 2 assertions / 515 skipped(真实命中,非 -tc 静默 0 匹配假通过);sdk_unit_tests 全绿。
  • 变异验证:去掉清槽一行 → 判据精确红(1 failed,CHECK(pendingSeekMsForTest() == -1) is NOT correct!);cmp 字节一致还原 + 重编译 → 绿。证明判据不是摆设。
  • 两轴 code-review:Standards 无硬违规;Spec 无缺失。据意见修了两处 —— 钩子注释精确区分「setter release / getter acquire」与 stop() 的默认 seq_cst store(原措辞会被读成事实错误),并把判据断言收敛回票 [实现] stop() 清空未消费 seek 槽并锁定状态契约 #113 批准范围(删掉两条锁既有行为、与契约无关的 CHECK)。
  • 本地 ctest -j4 10 轮第一组:9/10 全绿、0 次 SIGSEGV;第 9 轮失败为 sdk_interaction_tests 的既有 afterMonotonic(test_player_integration.cpp:1408),与本次改动零交集,含新判据的 sdk_unit_tests 10/10 全 Passed。该目标是 C1/C5 对照里 master 9/10 同源的既有低概率 flake。最终代码的第二组 10 轮正在运行,结论将补 comment。
  • master 基线取同 HEAD 96d3213 的第十四轮 10/10 全绿(同机、同日、同代码)。

明确不做

合并门禁

最终组 10 轮对照结论 + PR 阶段 Linux sdk-coverage 与 Windows sdk-windows 双绿(只认 gh run view <id> --json status,conclusion,attempt)。

反悔通道

若日后证明该判据只锁实现细节、无契约价值,须另开新票同时撤掉两个钩子与判据,不得只删判据留下无门禁的改动。

- AudioStreamDecoder::stop() 在 join 之后清 m_seekRequestMs:cancelRequested() 把「槽内有值」当取消,残留会让停止后该谓词仍为真。start() 本就在 stop() 之后清槽,正常路径行为等价。
- 加两个测试专用读写钩子(#112 批准的第二次限定例外):SDK 内部类型、不进公开 API、setter release / getter acquire、只在控制线程调用、生产路径零调用。
- sdk_unit_tests 新增确定性判据:不启线程、不依赖素材、不命中时序窗口;变异验证可红可绿。
- 不改 :546 与 E 链 #59/#60/#65 时序,不改 MusicPlayer.cpp / 公开 API / 既有判据 / TIMEOUT / 坏输入 55 例基线 / CI 配置。
- 边界:本改动不修复 42488656322 的 SIGSEGV(无复现、无栈、平台不对齐),根因未定位;取证与决议见 #112。
@wt159

wt159 commented Nov 9, 2026

Copy link
Copy Markdown
Owner Author

补记本地稳定性对照(合并门禁之一)。结论先说:不粉饰成「全绿」,两组各 9/10,且为消除基线歧义正在补跑真实 master 同轮数对照,结论出来前不合并。

分支两组 10 轮 ctest -j4

组 结果 失败轮次 SIGSEGV sdk_unit_tests(含新判据)
第一组(review 修改前代码) 9/10 全绿 round 9 0 10/10 Passed
最终组(review 修改后最终代码) 9/10 全绿 round 6 0 10/10 Passed

两组失败完全同形态,都是 sdk_interaction_tests 的 test_player_integration.cpp:1408 CHECK( afterMonotonic ) is NOT correct!,耗时 14.28s / 14.32s;失败列表只有 Test #14,sdk_unit_tests 明确 Passed 23.52 sec。这是既有的位置单调性断言(采样撞短窗口类假红),与本次改动零交集:本 PR 的新判据在 AudioStreamDecoder Stop Contract suite,归 sdk_unit_tests,而 sdk_interaction_tests 只跑 MusicPlayer Interactive Paths,PublicPlayer Interactive Paths(sdk/test/CMakeLists.txt:192-196,234-237);生产改动只在 AudioStreamDecoder::stop() 的 join 之后多一次槽清空,而 start() 本就会清槽,对播放期间的位置无因果路径。

为什么还要补跑 master

第十四轮在同 HEAD 96d3213 上跑过 master 10/10 全绿,而 C1/C5 时的 master 对照是 9/10(同源 sdk_interaction_tests 既有 flake)。分支两组各 9/10 落在这两个历史基线之间,单靠引用历史数据消除不了歧义。按 #112 决议里「master 与分支同轮数 ctest -j4 对照,分支假红率高于基线即撤回全部 sdk/ 改动」的闸门,正在实跑 master 10 轮(同一台机器、同一 build 目录、同样 -j4),完成后会在这里补结论。

  • 若 master 也出现同形态 afterMonotonic flake 且假红率不低于分支 → 判为基线既有 flake,放行。
  • 若 master 10/10 全绿而分支两组各 1 次 → 判为本改动放大假红,撤回全部 sdk/ 改动并关闭本 PR,不合并、不放宽任何判据或超时。

其余验证状态

判据自检(1 case / 2 assertions / 515 skipped)、变异验证(去清槽精确红 → 字节一致还原 → 绿)、两轴 code-review 及两处意见修复均已完成。Windows sdk-windows 42544814456 已 success(attempt=1);Linux sdk-coverage 42544814467 进行中。

再次重申边界:本 PR 不修复 42488656322 的 SIGSEGV,根因仍未定位(无复现、无栈、CI 在 Linux / 本机 Windows)。

@wt159

wt159 commented Nov 9, 2026

Copy link
Copy Markdown
Owner Author

合并门禁最终结论:按事先写死的闸门撤回,不合并

补跑的真实 master 同轮数对照已完成,结论:撤回本 PR 的全部 sdk/ 改动并关闭,不合并。

稳定性对照(同一台机器、同一 build 目录、同样 ctest -j4)

组 轮次 全绿 失败轮次 SIGSEGV
分支第一组(review 修改前代码) 10 9/10 round 9 0
分支最终组(review 修改后最终代码) 10 9/10 round 6 0
master 基线(本次实跑) 10 10/10 无 0

master 逐轮 58–62s、每轮 14/14;sdk_interaction_tests 13.44–14.03s,10 轮全 Passed。分支两组失败完全同形态:test_player_integration.cpp:1408 的 CHECK( afterMonotonic ),14.28s / 14.32s,111 断言 110 通过 1 失败,失败列表只有 Test #14;sdk_unit_tests(含本 PR 新判据)两组 20/20 全 Passed。逐轮日志在 build/seek-stop-final/、build/seek-stop-triage/、build/seek-stop-master-baseline/ 的 round_*.log / summary.txt。

裁决依据

上一条 comment 与 #112 决议 4 事先写死:「master 与分支同轮数 ctest -j4 ≥10 轮对照,分支假红率高于基线即撤回全部 sdk/ 改动」,并明确列出「若 master 10/10 全绿而分支两组各 1 次 → 判为本改动放大假红,撤回全部 sdk/ 改动并关闭本 PR,不合并、不放宽任何判据或超时」。本次结果精确命中该分支。

按此执行:AudioStreamDecoder.cpp / .h / test_stream_decoder.cpp 三文件 +47 行全部不进 master;不放宽任何判据、TIMEOUT、坏输入 55 例 / 396 断言基线、14 个 sdk_* 目标或 CI 配置;也不以「失败用例与改动零交集」为由事后放宽闸门 —— 这正是 AGENTS.md NOTES 反复警告的做法。分支 implement/seek-stop-state-hygiene / ca3543fef 仅作取证留痕,不得合并或 cherry-pick 到后续开发。

统计诚实声明:两次假红都是既有的 afterMonotonic 位置单调性断言(采样撞短窗口类),本 PR 的新判据归 sdk_unit_tests、生产改动只在 stop() 的 join 之后多一次槽清空,没有可解释该失败的因果路径;20 轮对 10 轮的样本也不足以证明「改动导致假红」。闸门约定的是假红率比较而非因果证明,因此本结论是「按事先约定的规则撤回」,不是「已证实本改动放大假红」,更不是「既有 flake 被修复」。

其余门禁(均已完成,但不改变一票否决的结果)

  • 判据自检:1 case / 2 assertions / 515 skipped,SUCCESS。
  • 变异验证:去掉 stop() 清槽一行 → 新用例精确红;字节一致还原 → 绿。
  • 两轴 code-review 两条意见(钩子注释内存序不精确、断言超出票范围)已修复并重跑。
  • CI:本 PR 阶段 Linux sdk-coverage 42544814467 与 Windows sdk-windows 42544814456 均 completed/success、attempt=1(无 rerun)。CI 双绿不替代本地同轮数稳定性对照。

边界重申

本 PR 从未修复 42488656322 attempt 1 的 SIGSEGV:无复现、无崩溃栈、CI 崩溃在 Linux ubuntu-24.04 而本机取证在 Windows / MinGW730,根因仍未定位。H1(seek 槽未消费完就 stop → SIGSEGV)已被 240 次窗口命中 0 崩溃证伪(Windows 侧);H2 未改(E 链 #59/#60/#65 已裁决时序);H3 / H4 未验(需 Linux core dump 或 ASan)。stop() 不清未消费 seek 槽的状态契约违反确实存在,但本轮没能以通过闸门的形态为它建立长期判据。

若日后要重新落地,须另开新票:先解决基线假红放大或拿出 Linux 侧可复现 / ASan 证据,再重走全套闸门;不得把本分支直接合并。

@wt159

wt159 commented Nov 9, 2026

Copy link
Copy Markdown
Owner Author

按 #112 决议 4 与本 PR 上一条 comment 事先写死的稳定性闸门:master 10/10 全绿、分支两组各 9/10 → 撤回全部 sdk/ 改动并关闭。不合并、不放宽任何判据 / TIMEOUT / 基线 / CI 配置。完整依据见上方结论 comment,票 #112 / #113 留 resolution。

@wt159 wt159 closed this Nov 9, 2026
wt159 added a commit that referenced this pull request Nov 9, 2026
- AGENTS.md:稳定性闸门条补 11-09 反例(分支两组各 9/10 vs master 10/10 → 撤回 PR #114);SIGSEGV「反复出现」门槛条补一次性破例留痕,明确三条门槛不删不放宽、撤回后终态仍是不改生产代码。
- doc/player-state-contract.md §10.4:记录破例边界、H1 证伪与 stop() 状态契约违反取证、全套验证结果、稳定性闸门一票否决与撤回裁决;如实保留「SIGSEGV 根因未定位 / H2 未改 / H3·H4 未验」。
- doc/streaming-playback-spec.md 新增 §12.6:seek 分支 :546 的 H2 判定「不改」及反悔条件、已排除项复核,明确全部为只读取证、没有任何生产代码落地。
- PR #114 已 CLOSED 不合并,#112 / #113 已 CLOSED 留 resolution;三文件 +47 行的 sdk/ 改动不进 master。
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.

1 participant