diff --git a/MobileGL/Config.h b/MobileGL/Config.h index 93d3b941..130ddceb 100644 --- a/MobileGL/Config.h +++ b/MobileGL/Config.h @@ -516,13 +516,17 @@ namespace MobileGL::MG_Config { // MOBILEGL_IPC_EVENT_WAIT_MS (PH-6, ID-P7-2): the SERVER's patience, in ms, for ONE // reverse-channel event that finds SEG_EVENT full under run-ahead. The apply thread // publishes, rings the client and parks until the client drains enough room; a client - // that has not made room when this runs out - it stopped draining, it drains a slot at - // a time, or it went away - FORFEITS the reverse channel: that event and every later - // one is dropped and counted (eventDropped), and the session stops by the ordinary - // stop path rather than by Fatal{EventRingOverflow}. It bounds the whole reservation, - // not one park, so a trickling peer cannot stretch it. Was a 30000 ms constant spent - // twice. Read by the server only (ServerSpawn.cpp passes it to a spawned child); the - // lockstep arm (no kCapRunAheadApply, i.e. Magma) never waits and keeps P5C's Fatal. + // that has not made room when this runs out - it stopped draining (NotDraining) or it + // drains a slot at a time (TooSlow) - FORFEITS the reverse channel: that event and every + // later one is dropped and counted (eventDropped), and the session stops by the ordinary + // stop path rather than by Fatal{EventRingOverflow}. A peer that goes away (PeerGone) or + // a session that is stopped (Stopped) ends the wait at once instead, so the knob may + // exceed ServerLoop::Stop()'s 5000 ms join. It bounds the whole reservation, not one + // park, so a trickling peer cannot stretch it - nor can a shm peer that writes the + // (peer-writable) eventRingFull latch to 0 itself, though that one keeps the apply + // thread spinning rather than parked until the budget runs out. Was a 30000 ms constant + // spent twice. Read by the server only (ServerSpawn.cpp passes it to a spawned child); + // the lockstep arm (no kCapRunAheadApply, i.e. Magma) never waits and keeps P5C's Fatal. Uint32 EventWaitMs = 2000; // MOBILEGL_IPC_PERSISTENT_BLOCK_KB: block granularity of the persistent-map push. // 0 IS A NEGATIVE CONTROL, NOT "unlimited": it disables the push, and diff --git a/MobileGL/MG_Remote/CONTRACT-P7.md b/MobileGL/MG_Remote/CONTRACT-P7.md index b5dac5a4..2e627b28 100644 --- a/MobileGL/MG_Remote/CONTRACT-P7.md +++ b/MobileGL/MG_Remote/CONTRACT-P7.md @@ -185,3 +185,4 @@ verify **在 wave 3 之前**只在 monolith 臂注册(`MG_IntegrationTest/CMak - 采样器 14:`SamplerObject::Get{BorderColor,BorderColorI,BorderColorUI,CompareMode,MagFilter,MaxAnisotropy,MaxLod,MinFilter,MinLod,MipmapMode,SamplerCompareFunc,WrapR,WrapS,WrapT}`(`VkSamplerManager` 的 `` 实例化;5 个另由 `UniformManager::ProgramSamplesOnlySingleLevelTextures`)。 - 对象读 6:`FramebufferAttachmentObject::{GetSize,IsComplete,IsValid}`、`RenderbufferObject::IsAllocated`、`VertexArrayObject::GetIndexBufferBindingSlot`、`BufferObject::EnsureGpuResidentStorage`(monolith render pass / clear / blit / 回读 / 索引 / SSBO 与 XFB)。 - `MGB_CTX` 访问 4:`PipeInputs::{GetTextureObject,HasOpenTransformFeedbackSpan,InvalidateCompileEnv}`、`GLImpl::CopyTextureImageToClientOrPBO_State`。 +- **PH-6 / fuzz 臂 3 的记录债(包 f2-event,ID-P7-2;细节 `~/w7/logs/f2-event/NOTE.md` §5)**:(1) **被丢事件所属的那条记录照常完成**:它仍贴 `kStatusOk` 回复并推进 `appliedSeq`(`ServerLoop.cpp` `DrainRing` 在该记录之后才停),而 `Client/` 里没有任何代码读 `eventDropped`(`EventRingConsumer::DroppedEvents()` 只有测试在读)——一个先发不等的 readback、事后才等其回复的 client,可能在看到 EOF 之前读到缺那次 writeback 的影子。只有不等待的 client 走得到(等待中的 client 在等待里就排空了)。修法候选是让触发 forfeit 的记录回复失败,或 client 在 `eventDropped != 0` 时闩 device-lost;两者都动 client 的等待 / 回复契约,未做。(2) **inproc 不被告知**:forfeit 停掉 apply 线程,同进程的 client 收不到任何信号。这不是「只有测试能到达」(初版 NOTE 的说法不对):(3) 的场景在 inproc 上同样可达,之后 client 的下一次等待在 120 s 后以 `Fatal{BarrierTimeout}` 死——诊断名字是错的,真因是 forfeit(审查指出,本包未测)。(3) **默认 2000 ms 未在设备上测过**:合法 client 停调 GL 超过旋钮(如 Android 退后台)且队里有一环以上未等待的事件(XFB writeback)会丢会话;此前是 30 s 后 Fatal。(4) **lockstep 臂不变**:无 `kCapRunAheadApply` 的 server(Magma)满环仍是 P5C 的 `Fatal{EventRingOverflow}`,不排空的对端让会话 SIGABRT(计入 `sessionsFaulted`,下一连接照常服务);fuzz 臂 3 没有 Magma 臂。(5) **对端不被告知原因**:没有 forfeit 的 wire 词,client 只见 EOF → device-lost;加 `Refuse` / `Fatal` 码会改 `wireFingerprint`。(6) **shm 的 `eventRingFull` 在对端可写的页上**:持续写 0 的对端让保留循环不 park 地自旋,直到旋钮耗尽后以 `TooSlow` forfeit 或会话被 Stop(有界,只记)。(7) **stream 面的阻塞 `sendmsg`**(`StreamLink.cpp:158`,apply 线程经 `StreamLink::Flush` 同步 `SendEvents`)**不是无界的**:数据套接字带 `TCP_USER_TIMEOUT=5000`(`SocketTransport.cpp:99-112`)。实测(临时探针,未入库):对端 SIGSTOP、旋钮 8000,stream 面 5327 ms 时链路被内核判死 → `ReverseChannelForfeit{PeerGone}`、exit 0;shm 面等满 8006 ms → `NotDraining`、exit 0。即 stream 面上一个冻住(活着但不 ACK)的对端约 5 s 被叫作 PeerGone,与旋钮无关;apply 线程恰好卡在 send 里时控制流又结束,`Stop()` 的 5000 ms join 与这 5 s 赛跑——未构造出,记录。(8) forfeit 后队列里剩下的记录不再应用(有意);`RunSession` 每会话每 250 ms 轮询一次控制套接字,且每一轮都查 apply 线程(记录,不是门)。(9) 排在 PH-6 等待后面的 surface op:server 的控制线程阻塞在 apply 线程的邮箱里(P5e 的等待谓词不含控制请求,本包未改),对端活着但只是不排空时,这个 op 要等旋钮耗尽才被回答;对端死亡则由数据 bell 报告(shm 面靠 server bell 挂在控制套接字上的死亡见证,镜像 CONTRACT-P6 D5c)。(10) **server bell 的死亡见证改变了 shm 会话的正常收尾顺序**:client 的 `ShutdownSend`(`ClientSession::Stop` 第 2 步)现在可能先让 apply 线程从 park 里以 dead bell 退出、再由控制循环读到 EOF;两条路走同一个退出块,每次 shm 会话正常收尾 server 日志多一行 INFO `the peer hung up (witness fd …, revents=0x2000)`(其措辞是为 client 侧写的)。 diff --git a/MobileGL/MG_Remote/Server/ServerSession.cpp b/MobileGL/MG_Remote/Server/ServerSession.cpp index 77c343f1..a1f000e0 100644 --- a/MobileGL/MG_Remote/Server/ServerSession.cpp +++ b/MobileGL/MG_Remote/Server/ServerSession.cpp @@ -284,8 +284,9 @@ namespace MobileGL::MG_Remote::Server { SessionFail(MGFatalFamily::EventRingOverflow, "MGPipe: Fatal{EventRingOverflow} - %s needs %llu bytes and SEG_EVENT " "caps ONE record at %llu (half its capacity). Flow control cannot help: " "no amount of draining makes a record fit a ring that is too small for " - "it. Raise MOBILEGL_IPC_EVENT_KB or slice the event at its producer, as " - "the writeback path already does", + "it. SEG_EVENT's size is fixed (SessionSegmentSizes::EventRingBytes; the " + "MOBILEGL_IPC_EVENT_KB knob P5e proposed was never built), so slice the " + "event at its producer, as the writeback path already does", eventName, static_cast(payloadBytes), static_cast(cap)); } diff --git a/docs/Disaggregated/ARCHITECTURE.md b/docs/Disaggregated/ARCHITECTURE.md index 95968296..64937e44 100644 --- a/docs/Disaggregated/ARCHITECTURE.md +++ b/docs/Disaggregated/ARCHITECTURE.md @@ -653,7 +653,7 @@ CMake: | `MOBILEGL_IPC_RING_MB` | 8 | `SEG_CMD`;单条记录至多一半 | | `MOBILEGL_IPC_STAGE_MB` | 32 | `SEG_STAGE`;目标负载 profile 显式 256 | | `MOBILEGL_IPC_SPIN_US` | 50 | park 前自旋;P5d 三轮起自旋按一次性校准的迭代预算走、稳态不读时钟,`0` = 不自旋直接 park | -| `MOBILEGL_IPC_EVENT_WAIT_MS` | 2000(1–600000) | PH-6(ID-P7-2),只由 server 读(`ServerSpawn.cpp` 放行给 spawn 子进程):run-ahead 下一条反向事件遇到满的 `SEG_EVENT` 时,server 等 client 腾出空间的**整笔预算**(不是每次 park 的)。到期、client 只涓流不腾够、或 client 的 bell 已死(先判死,不误报为超时)→ `ReverseChannelForfeit{NotDraining\|TooSlow\|PeerGone}`:该事件及其后全部计数丢弃(`eventDropped`),apply 循环按正常 Stop 退出,会话 exit 0,supervisor 照常欢迎下一连接。取代原先 30000 ms × 两轮后的 `Fatal{EventRingOverflow}`。lockstep 臂(无 `kCapRunAheadApply`,即 Magma)不等待,仍是 P5C 的 Fatal | +| `MOBILEGL_IPC_EVENT_WAIT_MS` | 2000(1–600000) | PH-6(ID-P7-2),只由 server 读(`ServerSpawn.cpp` 放行给 spawn 子进程):run-ahead 下一条反向事件遇到满的 `SEG_EVENT` 时,server 等 client 腾出空间的**整笔预算**(不是每次 park 的)。到期(client 不排空 / 只涓流不腾够)→ `ReverseChannelForfeit{NotDraining\|TooSlow}`;client 已走(bell 已死——shm 上靠 server bell 挂在控制套接字上的死亡见证——或控制流 EOF)→ `{PeerGone}`、会话被 Stop(如控制流收到畸形帧)→ `{Stopped}`,这两种立即结束等待,所以旋钮可以大于 `ServerLoop::Stop()` 的 5000 ms join。之后该事件及其后全部计数丢弃(`eventDropped`),apply 循环按正常 Stop 退出,控制循环每一轮都查 apply 线程是否还在,会话 exit 0,supervisor 照常欢迎下一连接。取代原先 30000 ms × 两轮后的 `Fatal{EventRingOverflow}`。lockstep 臂(无 `kCapRunAheadApply`,即 Magma)不等待,仍是 P5C 的 Fatal | | `MOBILEGL_IPC_PERSISTENT_BLOCK_KB` | 64 | persistent-map 推送块粒度;`0` 是 E3(a) 阴性对照 | | `MOBILEGL_IPC_PERSISTENT_HASH_SUPPRESS` | 1 | 推送只发内容变了的块(追踪 buffer 走 mprotect 位图,未追踪走内容哈希);`0` 恢复全范围推送(A/B 对照) | | `MOBILEGL_IPC_BATCH_WAITS` | 1 | 值类记录(无 reply slot 的 kCtxState/kCtxCso/kCtxObject)发布即返回,barrier 推迟到下一个拉取类 verb;**`generate_mipmap` 例外**(它的 apply 读 `MGB_CTX->GetActiveTextureUnit()`,规则:只有 apply 不读残余填充字段的记录才可免等);`0` 恢复 R-1 逐条 barrier(`MOBILEGL_PIPE_VERIFY` 强制 0) |