Skip to content

[决策] 强制重审 seek 竞态:批准第二次限定例外与状态契约加固 #112

Closed

Description

@wt159

拍板与背景

用户 2026-11-09 明确指令「强制重审」,即主动打破 745ba7b 固化的「SIGSEGV flake 反复出现」三条门槛(全历史 50 个 sdk-coverage run 中同形态 SIGSEGV 仅 1 次;本机 10 轮 ctest -j4 0/10、单用例 100 次 0/100;无崩溃栈),不等第二次出现即启动 effort。沿用用户既有的一次性批量授权:需要拍板的开放问题一律采用 AI 推荐,票 Notes 覆盖「一会话一票」。

必须先写死的边界

  • 没有复现、没有崩溃栈;CI 崩溃发生在 Linux ubuntu-24.04,本机取证是 Windows / MinGW730,不可等价外推。
  • 本 effort 交付的是「消除静态可辨、确定性命中的状态契约违反 + 建立长期判据 + 稳定性对照」,不是「修复了 42488656322 attempt 1 的 SIGSEGV」。根因未定位,票与规格一律不得宣称修复。
  • 判据锁的是「stop() 必须清空未消费 seek 槽」这条内部状态契约,不证明它与 SIGSEGV 有因果关系。

本轮新增取证(推翻上一轮取证的效力)

上一轮 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% 命中的常态,不是偶发,不足以解释一次偶发崩溃。但 stop()(AudioStreamDecoder.cpp:207-217)确实不清理 m_seekRequestMs,而 cancelRequested()(:451-457)把「槽内有值」当取消 → stop 返回后该谓词仍为真,构成确定的状态契约违反。

假设排序与处置

假设 判定 处置
H1 seek 槽未消费完就 stop → SIGSEGV Windows 侧证伪(240 次窗口命中 0 崩溃);状态泄漏本身确定存在 只做 stop() 清槽的一行防御性加固 + 判据
H2 seek 分支 :546 的 m_abortDecode.store(false) 清掉 stop 刚发布的取消位 240 次窗口命中未见 join 异常;属 E 链 #59/#60/#65 已裁决时序 不改,理由与反悔条件写进规格
H3 m_extractor 裸指针悬空 / H4 析构期重复 _releaseAudio() 需 ASan 或 Linux core dump,本机 MinGW 无 ASan 未验,列为后续取证条件,不猜测改动
已排除 m_seekBaseMs / m_seekDiscardBytes 只在解码线程读写,positionMs() 不读它们 不重复怀疑

决议(AI 推荐,用户已批量授权)

  1. 批准「不新造接缝」第二次限定例外(先例 [决策] 重审「不新造接缝」:批准一个默认关闭的设备错误注入点 #109 / PR 补齐 C1/C5 端到端判据:默认关闭的设备错误注入点 #111):仅在 SDK 内部类型 AudioStreamDecoder 增加两个默认生产路径不调用的测试专用方法(写槽 / 读槽),用于确定性锁定「stop 清空未消费 seek 槽」。不进 soundbridge::Player 公开 API、不改七个 void 控制入口、不碰 MusicPlayer.cpp 播放逻辑。
  2. 生产改动仅一行:AudioStreamDecoder::stop() 在 join 之后 m_seekRequestMs.store(-1)。正常路径行为等价(start() 本就在 stop() 之后清槽;state=IDLE 会让 seekToMs() 直接返回;解码线程已 join)。
  3. 不改 :546 及任何 E 链时序、不改判据 / TIMEOUT / 坏输入 55 例基线 / 14 个 sdk_* 目标 / CI 配置。
  4. 闸门(沿用 C1/C5):release/acquire、钩子只在控制线程、绝不落解码线程或判据主循环;变异验证证明判据可红;master 与分支同轮数 ctest -j4 ≥10 轮对照,分支假红率高于基线即撤回全部 sdk/ 改动;两轴 code-review;Linux + Windows CI 双绿。
  5. 反悔通道:若判据日后被证明只锁实现细节、无契约价值,须另开新票同时撤钩子与判据,不得只删判据留下「无门禁的改动」。

验收

  • 新增判据在加固前红、加固后绿(变异验证)。
  • 10 轮 ctest -j4 同轮数对照全绿且假红率不高于基线。
  • PR 阶段 Linux sdk-coverage + Windows sdk-windows 双绿。
  • 规格 / AGENTS.md / 记忆回填时保留「SIGSEGV 根因未定位、H2/H3/H4 未改或未验」的如实记录;745ba7b 的门槛条不删,补「11-09 用户强制重审」的破例留痕。

Activity

  1. wt159 commented on Nov 9, 2026

    @wt159
    OwnerAuthor

    Resolution:决议已执行完毕(含其撤回分支),票 CLOSED,改动未进 master

    ⚠️ 本票正文是历史留痕、不修改。正文里的决议是「批准第二次限定例外 + 一套闸门」,闸门被完整执行并触发了它自己规定的撤回分支,因此最终状态是「未落地」,不得据正文认为 stop() 清槽已合入。完整依据见 PR #114 结论 comment。

    决议 4 的闸门触发

    正文决议 4 与票 #113 验收均写死:「master 与分支同轮数 ctest -j4 ≥10 轮对照,分支假红率高于基线即撤回全部 sdk/ 改动」。实跑结果:

    组 全绿 SIGSEGV
    分支第一组(review 修改前) 9/10 0
    分支最终组(review 修改后最终代码) 9/10 0
    master 同机同 build 目录同 -j4 10/10 0

    分支两次失败同为 sdk_interaction_tests / test_player_integration.cpp:1408 CHECK( afterMonotonic );sdk_unit_tests(含新判据)20/20 Passed。PR 阶段 Linux 42544814467 / Windows 42544814456 均 success attempt=1,但 CI 双绿按约定不替代本地同轮数对照。于是按规则撤回三文件 +47 行、关闭 PR #114、不合并,且不放宽任何判据 / TIMEOUT / 坏输入 55 例基线 / 14 个 sdk_* 目标 / CI 配置。

    统计上须诚实:20 轮对 10 轮、且失败用例与本改动零交集,不能证明因果;本结论是「按事先约定规则撤回」,不是「已证实改动放大假红」。这一点已写进 PR 结论,避免后来者把它读成回归证据。

    本 effort 留下的取证资产(仍然有效,写入真源)

    • H1 证伪(Windows 侧):上一轮 100 次单用例压力跑的是 test_player_integration.cpp:726 既有用例,它在 setPosition() 后先轮询等到位置到达、再多等 800ms 才 stop(),seek 槽早已被消费,根本不经过候选窗口,0/100 对 H1 无证伪力。本轮改用「seek 后立刻 stop」临时 harness + 控制线程侧探针(零解码线程写),三组各 80 轮,windowHits=160/240 每轮 2/3 次确定性命中、0 崩溃 / 0 断言失败 / 0 非零退出。该叠加窗口是常态,不足以解释一次偶发崩溃。
    • 状态契约违反确实存在:AudioStreamDecoder::stop() join 之后不清 m_seekRequestMs,而 cancelRequested() 把「槽内有值」当取消 → stop() 返回后该谓词仍为真。但不证明它与 SIGSEGV 有因果关系。
    • H2 未改:AudioStreamDecoder.cpp:546 的 m_abortDecode.store(false) 属 E 链 [T04] 播放任务所有权与生命周期 prefactor #59/[T05] FLAC 增量垂直切片(MVP) #60/[T10] seek 两档与精度判据套件 #65 已裁决时序,240 次窗口命中未见 join 异常;反悔条件见 doc/streaming-playback-spec.md。
    • H3 / H4 未验:m_extractor 裸指针悬空 / 析构期重复 _releaseAudio() 需要 Linux core dump 或 ASan,本机 MinGW 无 ASan,不猜测改动。
    • 已排除:m_seekBaseMs / m_seekDiscardBytes 只在解码线程读写,positionMs() 不读它们;WorkQueue 为 SDK 内部单线程串行,UAF 假设无支持。

    边界与门槛状态

    • SIGSEGV 根因仍未定位:42488656322 attempt 1 崩溃在 Linux ubuntu-24.04,本机取证在 Windows / MinGW730,不可外推;无复现、无栈、无 ASan。本 effort 从一开始就声明「交付状态契约加固 + 判据 + 对照,不是修复 SIGSEGV」。
    • 745ba7b / 96d3213 的「反复出现」三条门槛不删、不放宽,本次「强制重审」是用户 11-09 明确指令下的一次性破例,已在 AGENTS.md NOTES 留痕,不构成后续自动启动 effort 的先例。三条门槛的实测状态不变(① 全历史同形态仅 1 次、② 本机 0/10 与 0/100、③ 无栈)。
    • 本轮一次性批量授权见票 Notes(沿用「需要拍板的问题一律采用 AI 推荐」),撤回是事先写死的闸门自动触发,未临时找用户确认、未事后放宽判据。

    后续

    若要重新落地,须另开新票:先解决基线假红放大,或拿到 Linux 侧可复现 / ASan 证据,再重走全套闸门(release/acquire、钩子只在控制线程、变异验证、同轮数 master 对照、两轴 review、双平台 CI)。不得复用 ca3543fef 直接合并或 cherry-pick。原生阻塞边 #113 blocked_by #112 随两票关闭一并终结。

  2. wt159 commented on Nov 9, 2026

    @wt159
    OwnerAuthor

    决议含其撤回分支已完整执行,PR #114 撤回关闭、改动未进 master;完整 resolution 见上方 comment。

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agent信息完备,agent 可直接开工

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions