[docs] (P7): record the PH-1 (3)(4) latch as CONTRACT-P7 §10 item 7 (session children latch and exit 75, inproc keeps Fatal) and its debts in §12; mark the PLAN PH-1, PH-2 and fuzz-arm-2 rows

This commit is contained in:
2026-09-23 13:44:37 -04:00
parent 3ef5c61e96
commit 1f67123835
2 changed files with 5 additions and 3 deletions
+2
View File
@@ -145,6 +145,7 @@ verify **在 wave 3 之前**只在 monolith 臂注册(`MG_IntegrationTest/CMak
4. `CONTRACT-P6.md` §5.2「`Session::Fail` 在 P6 保持 `[[noreturn]]`」——Ph 的翻转按 ID-P7-1 重定界,不做 98 站点的字面翻转;`Server/StagedShadow.h:115`、`Server/StagedTextureStore.h:344` 两处旁路由 wave 0 包 C 收进漏斗。
5. `ARCHITECTURE.md` §8.5「XFB scatter 在 client」——树上取的是 server staged shadow + `OnBufferWriteback`(P5c/P5f);`OnXfbScatterReady` 是死声明,删除。
6. **PH-4 mutable texture-level bounds (2026-09-23, F2 commits `2c6a6e25` / `51480f8c`).** A named `ResourceRespecify` carries exact per-level W/H/D alongside `(uploadTarget, level)`; on split the dimension words reuse `MGPResourceDesc.BufOffset/BufSize` only for non-buffer named image levels, and `WireFingerprint` mixes the extent-carrier revision. The applier stores the exact accepted extent, clears the transient range carrier from its descriptor copy, and passes the extent/target/fallback internal format into `StagedTextureStore.NoteLevelDefined`. The store derives its byte bound under server device limits before uploads; every `ResourceSubData` slab must match that saved extent and stay within the derived byte bound. TexBuffer and multisample targets have no legal subimage producer and staged byte records for them are named `ProtocolCorruption`. **PH-5** charges `sizeof(value_type)` for a vector count against `ReadCursor::Remaining()` before resize, using division to avoid count multiplication overflow. **PH-2** sends out-of-range and backwards-generation handle refusals through `MGPipeSessionFail` at reachable DirectGLES adoption guards; the slot table keeps the same named refusal as a backstop. Luna integration review = land; 49 targeted cases pass, including raw-peer extent, MS, slot and stale-generation controls; strict pull G1 against `adf2cbe0` is byte-identical (`.text=0xa52203`, no symbol changes). |
7. **PH-1 (3)(4) 每会话闩(F2 latch 包,2026-09-23,ID-P7-1)——对 `CONTRACT-P5.md` R-2 与 `CONTRACT-P6.md` §5.2 的更正。** 在 **spawn / TCP 会话子进程**里(`ServerMain::RunSession` 调 `ArmSessionLatch()`,全树唯一的武装点),输入字节来自对端、且已在 `Bool` / `MobileGLResult` 函数里的站点(R-2 的诚实臂与解码边界、`PipeApplier.cpp` 七处、`ServerLoop.cpp` 四处、`SurfaceOpCodec.cpp` 三处,逐站表见 `scripts/ci/ph_latch_sites.py --table`)不再 abort:`SessionLatch` 记同一行 `Fatal{Family, "word"}`、发同一个 SessionFault 帧(只发第一个),记下**第一个**故障后返回 false;`DrainRing` 在入口与每条记录之后查闩,apply 线程离开循环,控制泵与 `ServerApplyWireSurfaceOp` 对闩后的控制帧回 `PROTOCOL_MISMATCH` 不派发,`RunSession` 的控制等待按 100 ms 切片、见闩即关会话,以 **`kSessionLatchedExitCode = 75`** 退出;supervisor 的 reap 行记 `exit=75 (latched fault)` 并计入 `sessionsFaulted`,照常服务下一连接。所以 R-2 的「Fatal{ProtocolCorruption}」在会话子进程里读作「闩 + exit 75」。**inproc 保持 Fatal**:client 与 server 同进程,闩住而存活的 server 会让同进程 client 的 GL 线程停在屏障上、没有 EOF 可醒,也没有 supervisor 去服务「下一连接」——进程就是会话;client 从不武装。未武装时 `SessionLatch` 逐字节等于 `SessionFail`,死亡测试全部保持。负控:`MG_Test/Wire/PeerLatchTest.cpp`(每站一行 raw-peer,外加批内闩与静默对端两例)与 `ServerLoopTest.cpp` 的 `ServerLoopLatchTest.*`;`ph_latch_sites.py` 的 MECHANICS 表把每一道「拒绝其余工作 / 关会话」的检查对到它的负控。
---
@@ -160,6 +161,7 @@ verify **在 wave 3 之前**只在 monolith 臂注册(`MG_IntegrationTest/CMak
## §12 记录债
- **F2 latch(PH-1 (3)(4)、fuzz 臂 2,§10 第 7 条)留下的记录债**:(1) **第二批对端可达站点仍是 Fatal**——`PipeApplier.cpp` 的 `WireProtocolFatal`:`OnFenceCreate` / `Destroy` / `Status` / `Wait` / `WaitServer` 的 `Fence.*` 七处、`OnClear` 的 `Clear.Fbo` / `MGPClear::ValueClass` / `MGPClear::Kind`、`OnGetTextureImage` 的 `.extent` / `.range`(`.reply` 是 server 内部不变量,不算)、`OnDrawVbo` 的 `MGPDrawInfo::IndexSize` 与 `DrawVbo.userIndices`、`OnDeleteStreamOutput`;`QueryServer.inc` 的 `Query*` 十一处(`OnQueryCreate` / `Begin` / `End` / `Counter` / `Available` / `Result` / `Destroy` / `Timestamp`)。它们**都在读对端字节的 `Bool On*` 动词里**,满足 PH-1 (3) 的判据,只是不在计划点名的「`PipeApplier.cpp` 七处」里;只有 `FindFence`(`PipeApplier.cpp:115`)与 `FindQuery`(`QueryServer.inc:2`)返回引用、无诚实拒绝。今天它们 abort(signal 6),supervisor 照常服务下一连接;机械转换 + 每站一行 raw-peer,归 Ph。(2) **PH-2 仍是 Fatal**:`SlotTables.h`、`Managers.cpp`、`Managers.h` 的 `BackendSlotTable.{HandleSlot,Generation}` 八处走 `MGPipeSessionFail`;它们全在 `#if MOBILEGL_BUILD_DISAGGREGATED` 里,改闩**不动** pull `.text`。没改的原因:`MG_Backend` 到漏斗只有单向的 `MGPipeSessionFail` 缝,改闩要一条新的 `MGPipeSessionLatch` 缝,外加证明每个调用者拿到 null twin 后不会在同一记录里撞上另一处 abort——pull 臂的 `return m_nullTwin` / `return nullptr` 是从没在 split 下审过的 release 兜底。八处标 `@Ph-declined (ID-P7-1)`,`PeerLatchTest` 的两行 D11 slot 负控断言 `signal=6` + 具名首行 + 下一会话照常渲染。(3) 生成门 `MGPipeWireProtocolFatal`(`MG_Pipe/generated/PipeWire.inc`)与 `MG_Pipe` 里的 D11 钩(`PipeApply.cpp` 的 CSO slot、respecify 载体)未武装时照旧 abort;武装的会话子进程由 `AdmitsTheGeneratedGate` 先问生成门的两个问题(opcode、最短长度),`PipeApplier::ApplyOne` 在打 verb 戳、读 `MGPipeBarriered` 之前就先问。(4) 返回引用的 9 处 `SessionFail`(`ServerSession.cpp` 七处、`Client/WireTables.cpp:118`、`Client/EmitTables.cpp:212`)标 `@Ph-declined (ID-P7-1)`,保持 Fatal。(5) `ProgramArtifactsCodecTest.AVectorCountCannotReservePastTheRemainingArchiveBytes` 把 PH-5 的界退回旧的按字节界照样绿(计数只比 PH-5 的界多 1,元素读取先把字节读完);有判别力的是 `ProgramArtifactsCodecTest.ACountOnlyTheByteBoundAdmitsIsRefusedBeforeItsResize` 与 `PeerLatchTest` 的 `D11ArchiveVectorCount`(在会话子进程的 `RLIMIT_AS` 之下,旧界的 resize 以 `std::bad_alloc` 死)。
- ~~Magma 两进程 client 在 `StateObjectDeathOps` 落地前 `ObjectDeaths=0`:`CtWireScenario` 死亡用例在 Magma 臂按名 skip(§2.3)。~~ **已退(wave 2-C,`dce9953d`)**:两处 skip 守卫删除,死亡用例在三臂全绿;泄漏的是 **server 的 twin**(framebuffer 无 wire delete opcode,用例现在两边计数都读)。
- §5.4 的「白盒断言发出的是 `BufferSubDataResident(49)`」在本树**不可达**:resident 发射需要 GPU 常驻存储 → 需要 `AdoptPersistentMap` → split 下 T2 对每次采纳都 decline(`PipeApply.cpp:2331`),T0/T1 归 P11。本阶段证明到「server 按自己的表发布该位、client 镜像在双后端 × 三传输收到」为止;`rsd=` PipeStats 计数器在 P11 落地采纳档后会显示这些记录。
- §5.3 的 `g_programResourceCaches` 删除只在 disaggregated 构建:monolith 消费者已改读归档(red-once 在 monolith 臂也红),但归档成员必须 `#if MOBILEGL_BUILD_DISAGGREGATED` 才能守 G1,pull 构建保留旧缓存——钉子挪动那天一行改完,**P13**。
@@ -22,8 +22,8 @@ P7 收官的关键路径是:**先造仪器(wave 0)→ 真机诊断(wave
| 项 | 判定 | 必要性 | 剩余工作(可调度) | 规模 | 波次 |
|---|---|---|---|---|---|
| PH-1 `Session::Fail` 翻成每会话闩 | partial | **必须,但重新定界** | 路线图写的「每个 `[[noreturn]]` 站点造真实返回路径」(98 处 `SessionFail` + 6 处 `WireLogFatal`)**拒做**:≥9 处返回引用(`ServerSession&`、`RingControl&`、`Doorbell&`、`ClientSession&`,`ServerSession.cpp:270/283/740/765/774/784/790`、`WireTables.cpp:118`、`EmitTables.cpp:212`)没有诚实返回值;且 §12.3 的进程级 `eglTerminate` 使「闩住但活着的 server 进程」也服务不了第二个会话。P6.5 的 `--serve` fork-per-session(`ServerMain.cpp:302`)**已经**给出「下一连接照常服务」。真正欠的四片:(1) supervisor **观测并命名**子进程死亡——三处 `waitpid(active, nullptr, WNOHANG)`(`:279/:289/:294`)换成读 status、记 `pid exit=/signal=`、计 `sessionsFaulted`;(2) 两处绕过漏斗的裸 `abort`(`Server/StagedShadow.h:115`、`Server/StagedTextureStore.h:344`)改走 `SessionFail`;(3) **只对对端字节可达的站点**做 latch-and-decline(`PipeWireCodec.cpp` R-2 诚实臂 + 解码边界、`PipeApplier.cpp` 七处、`ServerLoop.cpp` 四处、`SurfaceOpCodec.cpp` 三处——它们都已在 `Bool`/`MobileGLResult` 返回的函数里),`DrainRing` 顶部加闩检查;(4) 负控:喂畸形帧 → 会话闩住 → 下一连接得到 Welcome。 | (1)+(2) 1 天;(3)+(4) 2–2.5 天 | (1)(2) 与 P7 wave 0 同一提交(费用极低);(3)(4) 在 P7 之后 |
| PH-2 slot 预算(`SlotTables.h:347/:371` 两条 `MOBILEGL_ASSERT`) | partial(wave 2-F 未做,见 `ph-f.md` §4) | 必须 | 提升为 release 下具名拒绝(先经 `SessionFail`,闩落地后改闩) | 0.5 天 | 与 D11 五处一起 |
| PH-1 `Session::Fail` 翻成每会话闩 | partial → **(3)(4) done**(F2 latch 包,`p7/f2-latch`:会话子进程武装的 `SessionLatch`、exit 75、inproc 保持 Fatal——`CONTRACT-P7.md` §10 第 7 条;逐站表 `scripts/ci/ph_latch_sites.py --table`;第二批站点、PH-2、生成门记在 §12) | **必须,但重新定界** | 路线图写的「每个 `[[noreturn]]` 站点造真实返回路径」(98 处 `SessionFail` + 6 处 `WireLogFatal`)**拒做**:≥9 处返回引用(`ServerSession&`、`RingControl&`、`Doorbell&`、`ClientSession&`,`ServerSession.cpp:270/283/740/765/774/784/790`、`WireTables.cpp:118`、`EmitTables.cpp:212`)没有诚实返回值;且 §12.3 的进程级 `eglTerminate` 使「闩住但活着的 server 进程」也服务不了第二个会话。P6.5 的 `--serve` fork-per-session(`ServerMain.cpp:302`)**已经**给出「下一连接照常服务」。真正欠的四片:(1) supervisor **观测并命名**子进程死亡——三处 `waitpid(active, nullptr, WNOHANG)`(`:279/:289/:294`)换成读 status、记 `pid exit=/signal=`、计 `sessionsFaulted`;(2) 两处绕过漏斗的裸 `abort`(`Server/StagedShadow.h:115`、`Server/StagedTextureStore.h:344`)改走 `SessionFail`;(3) **只对对端字节可达的站点**做 latch-and-decline(`PipeWireCodec.cpp` R-2 诚实臂 + 解码边界、`PipeApplier.cpp` 七处、`ServerLoop.cpp` 四处、`SurfaceOpCodec.cpp` 三处——它们都已在 `Bool`/`MobileGLResult` 返回的函数里),`DrainRing` 顶部加闩检查;(4) 负控:喂畸形帧 → 会话闩住 → 下一连接得到 Welcome。 | (1)+(2) 1 天;(3)+(4) 2–2.5 天 | (1)(2) 与 P7 wave 0 同一提交(费用极低);(3)(4) 在 P7 之后 |
| PH-2 slot 预算(`SlotTables.h:347/:371` 两条 `MOBILEGL_ASSERT`) | partial(wave 2-F 未做,见 `ph-f.md` §4)→ 具名拒绝经 `MGPipeSessionFail` 已落地(F2,`CONTRACT-P7.md` §10 第 6 条);闩已落地(F2 latch)但**改闩未做**,理由与八处站点见 `CONTRACT-P7.md` §12(`@Ph-declined (ID-P7-1)`) | 必须 | 提升为 release 下具名拒绝(先经 `SessionFail`,闩落地后改闩) | 0.5 天 | 与 D11 五处一起 |
| PH-3 readback 尺寸门(`PipeApplier.cpp:444`) | not_started | 必须 | 绝对上限取 P6.5 `LinkTerms.maxReplyBytes`(server 自陈),在 resize **之前**比较,超限回 `PostReply(kStatusError)` 而非 Fatal | 4–6 h | D11 |
| PH-4 `StagedTextureStore` 按 level 范围设界(`:440`) | not_started | 必须 | `CopyRunInto` 传入 level 宣告 extent,`imageOffset+byteSize` 越界即拒绝 | 1 天 | D11 |
| PH-5 `ArchiveVector` 上限(`ProgramArtifactsCodec.cpp:309`) | not_started | 必须 | `TakeCount` 带元素大小,`raw*sizeof(T)` 对 `Remaining()` 设界 | 4–8 h | D11 |
@@ -33,7 +33,7 @@ P7 收官的关键路径是:**先造仪器(wave 0)→ 真机诊断(wave
| PH-8 尺寸由 server 钳制 | **done**(wave 2-F:单测 + 两进程 smoke `hello_asks_64_gib`;F 修复轮:超出所授条款的请求记一条 `MGLOG_W`) | 必须 | 已陈述;补「client Hello 要 64 GiB 时 server **忽略请求、按自己的尺寸建段**而非分配」的单测(不是钳制:四个计数根本不参与定尺寸) | 0.5 天 | P7 之后 |
| 门:`Session::Fail` 站点普查零新增 | partial ⚠ | 必须 | `scripts/ci/fatal_census.py` 存在且在 CI(`test.yml:2011`),但 (a) `SCAN_ROOT` 只有 `MG_Remote`,server 进程里 ~79 处 abort(DirectGLES.cpp 16、Managers.cpp 12、VkBufferManager.cpp 11、PipeRoute.cpp 7……)看不见;(b) `abort_sites` 记了从不比较;(c) 12 个 family 词无 `.def` 行。改:SCAN_ROOT 扩到 server 镜像(MG_Remote / MG_Pipe / MG_Backend / MG_Impl/Pipe / MG_State),`abort_sites` 变棘轮,补 `.def` 行 | 1 天 | **wave 0**(P7 需要它看见 `@P7` abort) |
| 门:fuzz 臂 1 畸形控制帧 | partial | 必须 | 机制已在(`Framing.h:168` latch、`ControlInbox`),欠端到端脚本 `scripts/ci/ph_fuzz_control_frames.py`(五种畸形)+ `ServerMain.cpp:193` 补 `CtrlEnvelopeBufferHasIdentifier` + ctest 车道 | 1.5 天 | P7 之后 |
| 门:fuzz 臂 2 超界计数 | not_started | 必须 | = PH-1 (3)(4) + D11 五处 | 见上 | P7 之后 |
| 门:fuzz 臂 2 超界计数 | not_started → **done**(F2 latch 包:`MG_Test/Wire/PeerLatchTest.cpp` 每个转换站点与 D11 五处各一行 raw-peer 负控,每行之后同一 `--serve` supervisor 的下一会话得到 Welcome 并渲染;PH-2 与 D11 的 MG_Pipe 钩仍是具名 Fatal(`signal=6`),见 `CONTRACT-P7.md` §12) | 必须 | = PH-1 (3)(4) + D11 五处 | 见上 | P7 之后 |
| 门:fuzz 臂 3 不排空对端 | not_started | 必须 | = PH-6 + 场景 | 见上 | P7 之后 |
| 门:非 loopback 无令牌具名拒绝 | **done**(wave 2-F:smoke 入 CI,SKIP 即红) | 必须 | 已了:名字改为词表内的 `Refuse{Authentication}`(PH-7 (2));`tcp_supervisor_smoke.py` 挂为 `TcpLane.SupervisorProtocolControls`(`integration-tcp`),CI 构建钉版 flatc 并以 `junit_tally.py --require-entry-passed` 要求它**跑了且过了** | 含在 PH-7 | 同期 |
| OQ-21 令牌够不够 / TLS | 已答(ID-P7-3;wave 2-F 落地) | 必须裁定 | 见 ID-P7-3 | 0 | 本文 |