mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Docs] (Disaggregated): P6 plan rewritten in concise wording, 282 -> 124 lines, same content
This commit is contained in:
@@ -1,282 +1,124 @@
|
||||
# P6 spawn transport — 包计划
|
||||
|
||||
> 基线:`feat/disaggregated @ f23fbc1b`(P5e 收官头)。路径均在 `MobileGL/` 下,行号以该头为准。
|
||||
> 契约草稿见 [`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md);它在 `c6` 落地时移为
|
||||
> `MobileGL/MG_Remote/CONTRACT-P6.md`,与四份同胞契约并列。
|
||||
>
|
||||
> **本文件只是计划,尚未实现任何一行。** 它也写在自己的审计(`a6`)之前:
|
||||
> §4 里凡标为待 `a6` 确认的条目,都是现在还不该下结论的。
|
||||
> 基线 `feat/disaggregated @ f23fbc1b`。路径在 `MobileGL/` 下,行号以该头为准。
|
||||
> 契约草稿 [`P6-CONTRACT-DRAFT.md`](P6-CONTRACT-DRAFT.md),`c6` 落地时移为 `MobileGL/MG_Remote/CONTRACT-P6.md`。
|
||||
> **未开工。** 标 `待 a6` 的条目由审计确认,现在不下结论。
|
||||
|
||||
---
|
||||
|
||||
## 0 一句话
|
||||
## 1 树上已有的(不要重写)
|
||||
|
||||
ROADMAP 说"P6 此时只是传输替换"。这句话是 P5c 花了整整一个阶段去**换来**的,不是可以直接
|
||||
引用的前提——所以 P6 的第一个包是**重新证明它**,而不是相信它。证明之后,P6 要造的东西比想象
|
||||
的少:传输原语(socketpair、SCM_RIGHTS、shm adopt、socket 门铃)**已经在树上并且有测试**,
|
||||
缺的是**进程**、**控制面**,以及 P5e 刚刚引入的一个新问题——run-ahead 的客户端不再等待,而
|
||||
另一个进程**会死**。
|
||||
|
||||
---
|
||||
|
||||
## 1 起点:已经在树上的东西
|
||||
|
||||
这一节存在的理由是防止 P6 重写已经绿了的东西。每一行都在基线头上核过。
|
||||
|
||||
| 件 | 状态 | 位置 | 已有测试 |
|
||||
|---|---|---|---|
|
||||
| `ITransport`:`SendFrame`/`ReceiveFrame`/`PeekFrameSize`/`ShareFd`/`ReceiveFd`/`Shutdown` | 接口完整,`ShareFd` 是一等成员而非后补 | `MG_Remote/Transport/ITransport.h` | `InProcessTransportTest` 全套 |
|
||||
| `SocketDoorbell`(spawn 的门铃) | **已实现** | 声明 `Transport/Doorbell.h:385-420`;实现 `Doorbell.cpp:211-360` | `FdPassingTest.cpp:197/227/260/289` 四例:唤醒、超时+早唤醒记忆、对端挂断停止 park、挂断前最后一次 ring 仍送达 |
|
||||
| SCM_RIGHTS fd 传递 | **已实现**(专用 `AF_UNIX SOCK_DGRAM` socketpair,不是控制 socket) | `Transport/FdPassing.{h,cpp}`(`socketpair` 在 `FdPassing.cpp:92`) | `FdPassingTest.cpp:267/291` |
|
||||
| `ShmSegment::Adopt(fd, size)` | **已实现**,POSIX only | `Transport/ShmSegment.h:57`;`ShmSegmentPosix.cpp:120` | `FdPassingTest.cpp:114/187` |
|
||||
| inproc 也走 `ShmSegment`,attach 是 dup+Adopt+Map 的**真实第二份映射**而非别名 | 故意如此,正是为了 P6 当天不是第一次跑 | `Transport/SessionRings.h:22-28, 147-159` | 现有 split 车道每次运行 |
|
||||
| 控制面消息表:`Hello`/`Welcome`/`CapsSnapshot`/`SurfaceOp`/`SurfaceReply`/`ResyncRequest`/`AuxRequest`/`Fatal`/`LogLine` + `CtrlEnvelope` union | **schema 已定**,含 `SurfaceOpKind` 七个枚举、`WindowKind` 五个 | `Protocol/protocol.fbs:166-290` | `ProtocolSmokeTest`、`flatc-check` |
|
||||
| ABI 指纹握手(`sizeof(DynamicBackendParameters)`/`sizeof(MGPCaps)`/构建指纹,不符即 `Fatal{AbiMismatch}`) | 已实现;`CapsCodec.h:23-29` 明说 **P6 的 spawn 同机同二进制,原样继承** | `CapsCodec.{h,cpp}` | `SessionHandshakeTest.cpp:115` |
|
||||
| `FatalCode` 里 `DeviceLost=4`、`ServerCrashed=5` | 枚举已留位 | `protocol.fbs:234-242` | — |
|
||||
| `MOBILEGL_TRANSPORT=spawn\|unix:\|pipe:` 被**具名拒绝**而不是静默回落 | 已实现,且注释点名了它防的是哪种事故 | `ConfigLoader.cpp:310-348` | — |
|
||||
| `MOBILEGL_IPC_SERVER_PATH` 已解析、未消费 | 故意:未解析的环境变量与"解析了又忽略"无法区分 | `Config.h` `IpcTable::ServerPath` | ctest `ENVIRONMENT` 块已携带 |
|
||||
|
||||
**结论**:传输层的难点(lost-wakeup 的双 fence、挂断检测、fd 生命周期、映射对齐)**都已经付过
|
||||
账了**。P6 不是"写一个 socket 传输",是"把已经绿的零件装进一个进程里,并造出控制面"。
|
||||
|
||||
---
|
||||
|
||||
## 2 P6 真正要造的东西
|
||||
|
||||
按"这件东西今天完全不存在"排序:
|
||||
|
||||
1. **`SocketTransport`** —— `ITransport` 在 socketpair 上的实现。`Framing.h` 已有分帧,
|
||||
`FdPassing` 已有 fd 传递,`SocketDoorbell` 已有门铃。这个类是把三者接起来,加上
|
||||
`ReceiveFrame` 的 `BUFFER_TOO_SMALL`**留帧**语义(`ITransport.h` 明确这是接口存在的全部
|
||||
意义,早先的分支正是在这里丢帧把流永久卡死的)。
|
||||
`ClientSession::Start` 目前在 `ClientSession.cpp:468` 硬拒非 `InProcess`。
|
||||
2. **`ServerMain`** —— 子进程入口。今天的 `MobileGLServer`(`CMakeLists.txt:923`)是 **P0 的
|
||||
spike 桩**(`tools/spikes/server_stub/main.cpp`),Android-only、默认 OFF,只写一个 marker。
|
||||
P6 要一个真的:解析继承来的 fd、adopt 四个段、建后端、跑 `ServerLoop`、EOF 即退。
|
||||
3. **进程机制** —— `fork`+`execve`;**envp 剔除**(子进程必须看不见 `MOBILEGL_TRANSPORT`,
|
||||
否则递归 spawn)+ **子进程内强制 monolith** 的双保险;`MOBILEGL_IPC_SERVER_PATH` 找不到时
|
||||
`dladdr` 兜底定位同目录的 `libMobileGLServer.so`;**有界重试**握手(不是无限重试);
|
||||
不留孤儿(`waitpid` / `PR_SET_PDEATHSIG`)。
|
||||
4. **控制面帧** —— 十二个 `Server*` EGL forwarder(`ServerLoop.cpp:802-1022`)今天经
|
||||
`ServerLoop` 的**单槽邮箱**传 **函数指针 + 栈上 `void*`**(`ServerLoop.cpp:533-568`、
|
||||
`RunOnApplyThread` `:645`)。这是 P5c 审计的 G4 行,明确记给 P6。跨进程它们必须变成帧。
|
||||
5. **`s_synced` / `g_syncedRenderStateParameters` 按 context 世代重置** —— P5c 明确留给 P6
|
||||
(`CONTRACT-P5C.md:538-539`)。`DirectGLES.cpp:4586` 的 `g_syncedRenderStateParameters` 与
|
||||
`ScopedDefaultUnpackState::s_synced` 是**进程级静态**,语义上却是 per-context。在同进程里
|
||||
它们碰巧跟着进程走;在一个会被复用的 server 进程里,它们会把上一个 context 的同步状态
|
||||
带给下一个。
|
||||
6. **死亡语义** —— 见 §3.2,这是 P5e 留下的新债。
|
||||
|
||||
---
|
||||
|
||||
## 3 两个必须先决策的设计问题
|
||||
|
||||
### 3.1 窗口与 EGL 句柄:P6 和 P12 的分界线
|
||||
|
||||
`ServerCreateEGLWindowSurface(EGLSurface, const WindowHandle&)` 的 `WindowHandle::Handle` 是
|
||||
`void*`(`MG_Backend/BackendObject.h:561-566`),在 Android 上是 `ANativeWindow*`。
|
||||
`EGLDisplay`/`EGLSurface`/`EGLContext` 同理,全是**进程局部句柄**。子进程拿到这些值一个都
|
||||
用不了。
|
||||
|
||||
schema 已经给了答案,而且是对的:`SurfaceOp` 用 `display`/`surface` 两个 `ulong` **令牌**,
|
||||
`nativeToken` 的注释写着 *"X11 XID / HWND; Android transfers the window out of band"*
|
||||
(`protocol.fbs:186-196`)。那个 out of band 就是 **P12**(`android:process=":mgl"` 的
|
||||
Service 收 Java `Surface`)。
|
||||
|
||||
**所以 P6 的诚实边界是:**
|
||||
|
||||
- P6 落 **pbuffer / surfaceless / 离屏**路径,够跑 `HeadlessGL`、整条 `integration-split`
|
||||
等价车道、以及 trace replay(OpenRA 的 SSIM 门就是这条)。
|
||||
- P6 落**真窗口的全部管道**(令牌、`SurfaceOp`、`SurfaceReply`),但
|
||||
`WindowKind::AndroidNativeWindow` 到达 server 时是**具名拒绝**(`Fatal{UnmigratedSurface,
|
||||
"AndroidNativeWindow@P12"}`),不是崩溃、不是静默黑屏。
|
||||
- 真窗口的到达是 **P12**。ROADMAP 两阶段的门本来就是这么分的(P6 的门是 OpenRA SSIM,P12 的
|
||||
门才是"Minecraft 经 FCL 入世界")。
|
||||
|
||||
**把这条写进契约,因为它最容易被误解成"P6 之后就能在手机上跑游戏了"。** 不能。P6 之后能在
|
||||
手机上跑的是 trace 和离屏场景。
|
||||
|
||||
### 3.2 P5e 给 P6 留下的新问题:run-ahead 的客户端 + 会死的服务端
|
||||
|
||||
**先说已经设计好的部分**,免得重复发明:`ARCHITECTURE.md:504` 已经定死了死亡语义——
|
||||
server 死 → client 读到 EOF/EPIPE → device-lost 闩锁(GL 调用 no-op、`eglSwapBuffers` 返回
|
||||
`EGL_FALSE`+`EGL_CONTEXT_LOST`、`glGetGraphicsResetStatus` 返回 `GL_UNKNOWN_CONTEXT_RESET`);
|
||||
`MOBILEGL_IPC_RESPAWN=1` 重启并全量重推(默认关);client 死 → server 读到 EOF 立即销毁原生
|
||||
context 并退出,`MOBILEGL_IPC_IDLE_EXIT_S`(默认 30)只作最后保险。P6+ 的旋钮清单在
|
||||
`ARCHITECTURE.md:639`。**这些照抄即可。**
|
||||
|
||||
**新的是 P5e 之后的交互**,它写于 run-ahead 之前,所以没有任何现成文档记着它。
|
||||
|
||||
lockstep 时代,apply 线程死掉就是进程 abort——客户端不可能"继续画"。P5e 之后:
|
||||
- 客户端发了就走,不等 apply;
|
||||
- present credit(默认 1)是唯一的节流。
|
||||
|
||||
于是跨进程后出现三个**新的**状态:
|
||||
|
||||
| 情形 | 今天会发生什么 | P6 必须做到 |
|
||||
| 件 | 位置 | 测试 |
|
||||
|---|---|---|
|
||||
| server 进程被 kill,而客户端已经跑在前面若干条记录 | `SocketDoorbell` 的挂断检测会把等待者唤醒(已实现,`Doorbell.cpp` 的 `Drain` 在 EOF 上 latch `m_dead`);但**上层没有闩锁**,而且 run-ahead 的客户端此刻手里有一批**已发布、永远不会被 apply** 的记录 | `ARCHITECTURE.md:504` 的闩锁语义,**永不挂起**、不继续提交;已发布未 apply 的记录被明确丢弃而不是等待 |
|
||||
| server 被 SIGSTOP / Android **cached-app freezer** 冻住 | 客户端停在 credit 上,看起来像挂死,且**不会**有挂断事件 | 带上限的等待 + 具名诊断;不能靠"它总会回来" |
|
||||
| server 活着但 apply 落后一整帧以上 | 这就是 P5e 想要的正常状态 | 必须与上面两种**可区分**——所以死亡判定不能用超时阈值 |
|
||||
| `ITransport`(含 `ShareFd`/`ReceiveFd`) | `MG_Remote/Transport/ITransport.h` | `InProcessTransportTest` |
|
||||
| `SocketDoorbell` | `Transport/Doorbell.h:385-420`;`Doorbell.cpp:211-360` | `MG_Test/Wire/FdPassingTest.cpp:197`/`:227`/`:260`/`:289` |
|
||||
| SCM_RIGHTS fd 传递(专用 `SOCK_DGRAM` socketpair) | `Transport/FdPassing.cpp:92` | `FdPassingTest.cpp:267`/`:291` |
|
||||
| `ShmSegment::Adopt`(POSIX) | `Transport/ShmSegment.h:57`;`ShmSegmentPosix.cpp:120` | `FdPassingTest.cpp:114`/`:187` |
|
||||
| inproc attach 是 dup+Adopt+Map 的真实第二份映射 | `Transport/SessionRings.h:22-28`、`:147-159` | 现有 split 车道 |
|
||||
| 控制面 schema:`SurfaceOp`/`SurfaceReply`/`WindowKind`/`CtrlEnvelope` | `Protocol/protocol.fbs:166-290` | `ProtocolSmokeTest`、`flatc-check` |
|
||||
| ABI 指纹握手(同机同二进制,原样继承) | `CapsCodec.h:23-29` | `SessionHandshakeTest.cpp:115` |
|
||||
| `spawn`/`unix:`/`pipe:` 具名拒绝 | `ConfigLoader.cpp:310-348` | — |
|
||||
| `MOBILEGL_IPC_SERVER_PATH` 已解析未消费 | `Config.h` `IpcTable::ServerPath` | — |
|
||||
|
||||
**闩锁本身是搬 `ARCHITECTURE.md:504`;新设计的是"慢与死如何区分"这一条。**
|
||||
它也正好是 E1 对照(ID-122,P5e 唯一未决项)
|
||||
应该被重新定义成的东西的邻居:一个真正能区分"慢"和"死"的判据。两件事建议一起想。
|
||||
传输原语已付过账。P6 是装配 + 控制面 + 进程。
|
||||
|
||||
---
|
||||
## 2 缺什么
|
||||
|
||||
## 4 包与依赖序
|
||||
1. `SocketTransport`。`ClientSession.cpp:468` 目前硬拒非 `InProcess`。
|
||||
2. `ServerMain`。今天的 `MobileGLServer`(`CMakeLists.txt:923`)是 P0 spike 桩(`tools/spikes/server_stub/main.cpp`),Android-only、默认 OFF。
|
||||
3. 进程机制:fork/execve;envp 剔除 + 子进程强制 monolith(双保险);`dladdr` 兜底;有界重试握手;EOF 即退;不留孤儿。
|
||||
4. 控制面帧。十二个 `Server*` forwarder(`ServerLoop.cpp:802-1022`)经单槽邮箱传**函数指针 + 栈上 `void*`**(`ServerLoop.cpp:533-568`、`:645`),跨进程无意义。P5c 审计 G4 行,记给 P6。
|
||||
5. `g_syncedRenderStateParameters`(`MG_Backend/DirectGLES/DirectGLES.cpp:4586`)与 `ScopedDefaultUnpackState::s_synced` 按 context 世代重置(`CONTRACT-P5C.md:538-539`)。
|
||||
6. 死亡语义,见 §4。
|
||||
|
||||
## 3 边界:P6 不做真窗口
|
||||
|
||||
`WindowHandle::Handle`(`MG_Backend/BackendObject.h:561-566`)在 Android 上是 `ANativeWindow*`,子进程用不了。`protocol.fbs:186-196` 的 `nativeToken` 注释已写明 Android 的窗口 out of band 传递——那是 P12。
|
||||
|
||||
- P6 落 pbuffer / surfaceless / 离屏:够 `HeadlessGL`、等价 split 车道、trace replay,也正是 P6 出口门量的东西。
|
||||
- 真窗口的管道(令牌、`SurfaceOp`、`SurfaceReply`)落,但到达 server 时具名拒绝。
|
||||
- **P6 之后手机上能跑的是 trace 和离屏,不是游戏。**
|
||||
|
||||
## 4 P5e 留下的:不等待的客户端 + 会死的服务端
|
||||
|
||||
闩锁语义已定(`docs/Disaggregated/ARCHITECTURE.md:504`):EOF → device-lost latch、`EGL_CONTEXT_LOST`、`GL_UNKNOWN_CONTEXT_RESET`;`MOBILEGL_IPC_RESPAWN` 默认关;`IDLE_EXIT_S` 默认 30。照抄。
|
||||
|
||||
新的是 run-ahead 之后的三个状态:
|
||||
|
||||
| 情形 | 要求 |
|
||||
|---|---|
|
||||
| server 被 kill,客户端已跑在前面 | 走上述闩锁;已发布未 apply 的记录明确丢弃,不等待 |
|
||||
| server 被 SIGSTOP / Android freezer 冻住(**不产生挂断事件**) | 有界等待 + 具名诊断 |
|
||||
| server 活着但落后一帧以上 | 这是 P5e 的正常态,必须与上两者可区分 |
|
||||
|
||||
死亡判定取 `SocketDoorbell` 在 EOF 上 latch 的 `m_dead`(`Doorbell.cpp` 的 `Drain`),**不用超时阈值**。这是 P6 唯一的新设计,其余是搬运。它与 E1 对照的重新定义(ID-122)共用同一个谓词,建议一起做。
|
||||
|
||||
## 5 包与依赖序
|
||||
|
||||
```
|
||||
a6 (审计,只读,不写代码) -- 先跑,决定后面几包的大小
|
||||
└─ c6 (契约 + 惰性线上行 + 令牌类型 + 知识桩) -- 第二个落,行为零变化
|
||||
├─ so (SocketTransport) ┐
|
||||
├─ sm (ServerMain + fork/exec + 进程纪律) │ 并行 worktree,各自 rebase 在 c6 上
|
||||
├─ cp (控制面:12 个 forwarder → 帧) │
|
||||
├─ st (静态量按 context 世代重置 + G5) │
|
||||
└─ t6 (spawn 车道 + 阴性对照 + CI) ┘
|
||||
a6 (只读审计,不写代码)
|
||||
└─ c6 (契约 + 惰性线上行,行为零变化)
|
||||
├─ so (SocketTransport)
|
||||
├─ sm (ServerMain + fork/exec + 进程纪律)
|
||||
├─ cp (控制面:12 个 forwarder → 帧)
|
||||
├─ st (静态量按 context 世代重置 + G5)
|
||||
└─ t6 (spawn 车道 + 阴性对照 + CI)
|
||||
└─ 集成 commit:ConfigLoader 不再拒绝 spawn
|
||||
```
|
||||
|
||||
### a6 —— 审计(整合者,只读,**第一个**)
|
||||
**a6**(先跑)。"P6 只是传输替换"的证据是 P5c 时代的;P5e 之后树变了(by-handle resolver、server 角色内存、不等待的路径)。产出:逐行清单,形状照 `~/w7/notes/p5c/p5c-audit-v1.md`(现 `notes/p5c/`)。点名查:
|
||||
|
||||
**为什么它是第一个**:P5c 存在的全部理由,是一次只读审计在一个"看起来已经很干净"的头上
|
||||
找出了 **59 个**不经任何载体的直接内存读写。P5e 之后树又变了:多了 by-handle resolver、
|
||||
多了 server 角色内存(`gPipeInputs`)、多了 run-ahead 这条不等待的路径。**"P6 只是传输替换"
|
||||
这句话现在的证据是 P5c 时代的,已经过期。**
|
||||
1. 仍过线的裸指针 / 进程局部句柄。
|
||||
2. **进程级静态中语义属于 context 或 session 的**——§2.5 是已知的两个,问题是还有几个。这类缺陷 `inproc` 结构性看不见。
|
||||
3. `gPipeInputs` 在 spawn 下的归属(P5e 的 ID-135 结论要重验)。
|
||||
4. 十二个 forwarder 对 `SurfaceOpKind` 七个枚举,缺几个。预期缺 `SetSwapInterval` / `ReleaseResources` / `SetWindowHandle`(`SwapEGLBuffers` 走记录、`InitCapabilities` 走 `CapsSnapshot`、`InitWindowSurface` 是客户端 no-op)——待 a6 确认。
|
||||
5. server 能否不链 `MG_Impl`(`MG_Backend/MGPipe/PipeInputs.h` 如此声称)。做不到就记账。
|
||||
|
||||
**产出**(对照 `~/w7/notes/p5c/p5c-audit-v1.md` 的形状):逐行清单,每行给出
|
||||
`file:line`(在 P6 基线头上重新解析)、方向、今天为何能工作、现有守卫、P6 的载体或**具名延期**。
|
||||
**c6**:契约;`protocol.fbs` 追加枚举(只追加);令牌类型;新旋钮(`ARCHITECTURE.md:639` 已登记的 P6+ 项,`POLL_ESCALATE` 归 P10);跨包接缝的声明 + 未落地断言体;`MobileGLServer` 真目标。
|
||||
|
||||
**必须点名去查的**:
|
||||
1. 任何仍然过线的裸指针 / 进程局部句柄(`EGLDisplay`/`EGLSurface`/`EGLContext`/`ANativeWindow*`/
|
||||
`void*`)。
|
||||
2. **进程级静态**中语义上属于 session 或 context 的:`g_syncedRenderStateParameters`、
|
||||
`s_synced` 是已知的两个,问题是**还有几个**。这类东西 inproc 永远看不见。
|
||||
3. `gPipeInputs` 在 spawn 下的归属(P5e 把它变成 server 角色内存,ID-135 证明值由
|
||||
`SuppliedFieldMask` 分区、竞态在元数据标量上——跨进程后这个结论**要重新验**)。
|
||||
4. 十二个 forwarder 的参数形状逐个对照 `SurfaceOpKind` 的七个枚举,给出**缺几个**。
|
||||
预期答案(待 a6 确认):`SwapEGLBuffers` 作为记录走(P5e 已裁定,`BackendObject_Remote.cpp`
|
||||
的 `WaitForApplyBeforeEglForwarder` 注释明说它不在名单上)、`InitCapabilities` 走
|
||||
`CapsSnapshot`、`InitWindowSurface` 是客户端 no-op,剩下九个里七个有枚举,
|
||||
**`SetEGLSwapInterval` / `ReleaseEGLResources` / `SetWindowHandle` 需要新枚举**(union
|
||||
与 enum 只许追加)。
|
||||
5. server 端是否真的可以不链 `MG_Impl`。`MGPipe/PipeInputs.h` 里写着 "P6, where MG_Impl is
|
||||
not in the [server]"。如果 P6 做不到,**说出来并记成 P13 的账**,不要让一句注释替未来
|
||||
的自己许诺。
|
||||
**so**:`SocketTransport`。**必须在第二个进程出现之前就绿**——一次 `socketpair()` + 两个线程跑 `InProcessTransportTest` 全套,含 `BUFFER_TOO_SMALL` 留帧语义。理由同 `SessionRings.h:22-28`。
|
||||
|
||||
**a6 不写代码。** 它的输出是 c6 的输入。
|
||||
**sm / cp / st / t6**:见 §2 与 §3。`cp` 最大;`ForgetCurrentTuple` 的 N-3 语义(`ServerLoop.cpp:318-330`)须在帧路径上原样成立。
|
||||
|
||||
### c6 —— 契约与线上行(整合者,第二个落,行为零变化)
|
||||
## 6 出口门
|
||||
|
||||
`MG_Remote/CONTRACT-P6.md`;`protocol.fbs` 追加缺的 `SurfaceOpKind` 枚举(只追加);
|
||||
令牌类型(`SurfaceToken`/`ContextToken`/`DisplayToken`,客户端铸造、server 侧映射到自己的
|
||||
真句柄);`Config.h` 的新旋钮(`ARCHITECTURE.md:639` 已登记为 P6+ 生效的 `POLL_ESCALATE` / shadow shm /
|
||||
`RESPAWN` / `IDLE_EXIT_S`,其中 `POLL_ESCALATE` 归 P10 —— **在这里加,不在调用点发明**;
|
||||
`ARCHITECTURE.md:639` 同时明确**不设立** `MOBILEGL_IPC_VALIDATE_SERVER`,因为 server 没有
|
||||
`MG_Impl` 校验器,这与 §7.3 是同一个问题的两面);每个跨包接缝的声明 + "未落地"断言体,
|
||||
让所有包从第一天起可编译;`CMakeLists.txt` 的 `MobileGLServer` 真目标(与 P0 spike 并存或
|
||||
取而代之,a6 给结论)。
|
||||
五部分门(`ARCHITECTURE.md` §13.2)之外:
|
||||
|
||||
**门**:unit 绿;`integration-split` 绿(什么都没变);G1 0/0/0/0。
|
||||
1. G1 pull 构建 0/0/0/0,`.text` 不变。
|
||||
2. **G2/G14**:`integration-spawn` 用例名集合与 `integration-split` 逐名相同。
|
||||
3. `integration-spawn` 全绿,数字与 `integration-split` 一致(今天 179/179)。
|
||||
4. **进程树门(机检)**:每条用例前后数子进程,运行中恰好多一个,运行后为零;`HeadlessGL` fork 预检无孤儿。
|
||||
5. **臂证明门**:每条用例在自己的私有日志里留下子进程 pid 与 `transport=spawn`。ID-124 已证明假绿会发生,而一条实际跑了 monolith 的 spawn 车道能通过 1–4。
|
||||
6. 逐包 red-once(R-16),各一条具名对。
|
||||
7. 设备:Redmi `2f7cbe2e`,reboot-clean + 同热窗口配对 A/B;OpenRA 在 Adreno 830 上 split SSIM ≥ 0.99。
|
||||
8. 性能只记录不阻塞,但必须采到一个数:**socket 门铃相对 inproc condvar 的代价**。P5d 把 client 线程从 15.0 压到 9.2 ms/帧,其中有门铃路径的钱。P6 唯一预期的回归。
|
||||
|
||||
### so —— `SocketTransport`
|
||||
## 7 阴性对照
|
||||
|
||||
**关键纪律,照 `SessionRings.h:22-28` 的先例**:`SocketTransport` **必须在第二个进程出现之前
|
||||
就绿**。用一次 `socketpair()` + 两个线程,跑 `InProcessTransportTest` 的同一套断言
|
||||
(`BUFFER_TOO_SMALL` 留帧、`Shutdown` 双向半关、排队消息仍可读尽、`PROTOCOL_MISMATCH` 永久
|
||||
latch)。**一个只在 fork 之后才第一次被执行的传输实现,是这个阶段最该避免的形状。**
|
||||
|
||||
### sm —— `ServerMain` 与进程纪律
|
||||
|
||||
fork/execve;envp 剔除 + 子进程强制 monolith;`dladdr` 兜底;有界重试握手;EOF 即退;
|
||||
无孤儿。**进程树必须可机检**:不是"看上去没多余进程",而是每条用例前后数一次子进程。
|
||||
|
||||
### cp —— 控制面(最大的一包)
|
||||
|
||||
十二个 forwarder 改成请求/应答帧;令牌取代进程局部句柄;server 拥有自己的
|
||||
EGLDisplay/Surface/Context;`ForgetCurrentTuple` / `ForgetCurrentTupleIfItNames` 的语义
|
||||
(`ServerLoop.cpp:318-330`,N-3 那条"创建不同 surface 会销毁重建原生 context")必须在帧上
|
||||
原样成立;真窗口 → 具名拒绝(§3.1)。
|
||||
|
||||
### st —— 静态量
|
||||
|
||||
`g_syncedRenderStateParameters` / `s_synced` 按 context 世代重置;G5 的客户端本地角色标志与
|
||||
caps 快照 cap 位;a6 找出的其余同类。小包,但**红一次很容易写**:不重置 → 第二个 context
|
||||
拿到上一个的同步状态 → 一条具名场景变红。
|
||||
|
||||
### t6 —— 车道
|
||||
|
||||
`integration-spawn` ctest label;`MOBILEGL_TRANSPORT=spawn`;阴性对照(§6);
|
||||
`HeadlessGL` 的 fork 预检;CI job。
|
||||
|
||||
---
|
||||
|
||||
## 5 出口门
|
||||
|
||||
走五部分门(`ARCHITECTURE.md` §13.2)之外,P6 自己的:
|
||||
|
||||
1. **G1** pull 构建 0/0/0/0,`.text` 不变。
|
||||
2. **G2/G14** `integration-spawn` 的**用例名集合与 `integration-split` 逐名相同**。
|
||||
名集合不同就意味着 spawn 车道在偷偷少跑东西。
|
||||
3. `integration-spawn` 全绿,数字与 `integration-split` 当期数字一致(今天是 179/179)。
|
||||
4. **进程树门(机检)**:每条用例前后各数一次子进程;运行中恰好多一个;运行后为零。
|
||||
`HeadlessGL` 的 fork 预检不留孤儿。
|
||||
5. **臂证明门(ID-124 的教训)**:spawn 车道的每条用例必须在**自己的私有日志**里留下子进程
|
||||
pid 与 `transport=spawn`。*"一条声称跑了 spawn、实际跑了 monolith 还绿了的车道"* 正是
|
||||
`ConfigLoader.cpp:315-317` 注释点名要防的事故,而 ID-124 证明这类假绿**真的会发生**。
|
||||
6. **逐包 red-once**(R-16),每包一条具名对。
|
||||
7. 设备:Redmi `2f7cbe2e`,reboot-clean + 同热窗口配对 A/B,OpenRA 在 Adreno 830 上
|
||||
split SSIM ≥ 0.99。
|
||||
8. **性能只记录不阻塞**(路线图通则),但必须真采到,且必须回答一个具体问题:
|
||||
**socket 门铃相对 inproc 的 condvar 贵多少**。每次唤醒多一次 syscall;P5d 三轮把 client
|
||||
线程从 15.0 压到 9.2 ms/帧,其中相当一部分是门铃路径的钱。这是 P6 唯一预期的回归,
|
||||
值得单独一组数。
|
||||
|
||||
---
|
||||
|
||||
## 6 阴性对照(每条都要真跑过一次红)
|
||||
|
||||
| # | 旋钮 | 应该证伪什么 |
|
||||
| # | 旋钮 | 证伪什么 |
|
||||
|---|---|---|
|
||||
| S1 | `MOBILEGL_IPC_SERVER_PATH=/nonexistent` | 具名拒绝,**不是**静默回落 monolith。这是 §5 门 5 的运行时半边 |
|
||||
| S2 | 运行中 `kill -9` server | 干净的 device-lost latch,客户端**不挂起**、不继续提交。§3.2 |
|
||||
| S3 | 子进程 envp 未剔除 | 递归 spawn 必须被第二道保险挡住并具名 |
|
||||
| S4 | 关掉 `st` 的世代重置 | 第二个 context 用到上一个的同步状态,一条具名场景变红 |
|
||||
| S5 | 真窗口 `WindowKind::AndroidNativeWindow` 到达 server | 具名 `Fatal`,不是黑屏也不是崩溃 |
|
||||
| S1 | `MOBILEGL_IPC_SERVER_PATH=/nonexistent` | 具名拒绝,非静默回落 monolith |
|
||||
| S2 | 运行中 `kill -9` server | 客户端能区分慢与死;不挂起、不继续提交 |
|
||||
| S3 | 子进程 envp 未剔除 | 第二道保险挡住递归 spawn 并具名 |
|
||||
| S4 | 关掉 `st` 的世代重置 | 第二个 context 拿到上一个的同步状态 |
|
||||
| S5 | `WindowKind::AndroidNativeWindow` 到达 server | 具名 Fatal |
|
||||
|
||||
**S2 的形状要先想清楚再写**,否则会重蹈 E1 的覆辙(ID-122:它索要的证据在整次运行里出现 0 次)。
|
||||
对照要证伪的是"客户端能区分慢与死",不是"某个 Fatal 出现过"。
|
||||
S2 的形状先想清楚再写:E1 索要的 Fatal 在整次运行里出现 0 次(ID-122)。
|
||||
|
||||
---
|
||||
## 8 风险与未决
|
||||
|
||||
## 7 具名风险与未决
|
||||
1. 慢 / 死的区分(§4)。P6 唯一新设计,与 E1 重定义合并考虑。
|
||||
2. Android cached-app freezer 冻结不产生挂断事件。完整处理归 P12,诊断归 P6。
|
||||
3. server 能否不链 `MG_Impl` — 待 a6;做不到即记账,不留空头承诺。
|
||||
4. Windows:`Adopt` 是 POSIX-only,`unix:` / `pipe:` 继续具名拒绝,P6 只落 POSIX。
|
||||
5. `SEG_REPLY` 2 MiB 单槽 cap 与 chunking(`Transport/ReplySlot.h` 记作 P6 debt,ID-47)→ **推给 P9**。
|
||||
6. class-C 的 511 条 `UnmigratedVerb`(`notes/p6/census-classC.md`)不是 P6 的账。
|
||||
7. E1 对照(ID-122)不因 P6 开工而解决。
|
||||
|
||||
1. **run-ahead + 会死的对端**(§3.2)。P6 唯一的新设计。建议与 E1 重定义一起想。
|
||||
2. **Android cached-app freezer** 会冻住子进程,而冻结不产生挂断事件。P12 的题材,但会在
|
||||
P6 的设备测试里先咬人。
|
||||
3. **server 能否不链 `MG_Impl`** —— a6 回答;做不到就记账,不要留空头承诺。
|
||||
4. **Windows**:`ShmSegment::Adopt` 是 POSIX-only,`NamedPipe`/`UnixSocket` 两种模式
|
||||
**建议继续具名拒绝**,P6 只落 POSIX(Linux + Android)。Windows 机器本来就不是正确性门。
|
||||
5. **`SEG_REPLY` 2 MiB 单槽 cap 与 chunking**(`ReplySlot.h` 记作 "P6 debt",ID-47)——
|
||||
**建议明确推给 P9**(反向通道那一阶段)。P6 不碰回读形状;混进来会让"只是传输替换"再次
|
||||
变成一句空话。
|
||||
6. 511 条 `UnmigratedVerb` 的 class-C 账(`~/w7/notes/p6/census-classC.md`,2026-09-16)
|
||||
**不是 P6 的**。spawn 跑的是 P5b/P5c/P5e 那条同样的缩减路径,门是同一批 179 / 1357。
|
||||
7. E1 对照(ID-122)仍未决,**不因 P6 开工而自动解决**。
|
||||
## 9 不属于 P6
|
||||
|
||||
---
|
||||
真窗口与 `android:process=":mgl"` Service(P12);多 context(P12,`CONTRACT-P5C.md:56` 原写 P6,本计划改判);chunked readback 与 reply-slot 池(P9);`DynamicBackendParameters` 定宽重写(P7);`POLL_ESCALATE`(P10);对象类 BARRIER-PULLED 行与 frontend-keyed twin registry(P3b/P4b、P7)。
|
||||
|
||||
## 8 明确不属于 P6
|
||||
## 10 开工顺序
|
||||
|
||||
真窗口到达(P12);`android:process=":mgl"` Service(P12);chunked readback(P9);
|
||||
`DynamicBackendParameters` 的定宽重写(P7);多 context(`MGPApplierReset::ContextSerial`
|
||||
今天被断言相等,`CONTRACT-P5C.md:56` 说"P6's multi-context shape reads it for real"——
|
||||
**建议推给 P12**,P6 维持单 context 并在契约里写明);对象类 BARRIER-PULLED 行(P3b/P4b/P7)。
|
||||
|
||||
---
|
||||
|
||||
## 9 开工顺序建议
|
||||
|
||||
先跑 **a6**,只读,不改一行。a6 的清单出来之前,`c6` 的契约写不实,后面五包的大小也是猜的。
|
||||
P5c 是这么开始的,也是那个阶段唯一没有返工的决定。
|
||||
先跑 `a6`,只读。清单出来之前 `c6` 写不实,后面五包的大小是猜的。
|
||||
|
||||
Reference in New Issue
Block a user