refactor(plugindev): 模板改用内核独占共享槽池 + ContextPolicy 字段

共享内存是内核内部实现,不对插件开发者暴露。模板不再维护任何分配
游标(历史上 bump 游标 / 模板内位图 CAS 两版都因把可变分配状态放在
共享内存里而出竞态),改为通过内核 RPC 申请/归还:

- 删除 arenaWrite 本地 bump 分配器与 arenaOff/arenaUsed 全局变量
- 新增 arenaAlloc/arenaFree:走 arena.alloc / arena.free RPC
- 新增 putInArena/callWithText:按 payload 大小自动选择共享槽或内联
  JSON(inlinePayloadLimit=512),SDK 公开 API 仍是普通字符串/Map,
  插件开发者无感
- cleaner.invoke 改为读 TextRef/内联 Text、写 RespRef/内联 Text;
  插件不做任何分配(内核预分配请求槽 + 响应槽)
- 同步 handshake:不再解析 arena 槽池布局(偏移与大小由 arena.alloc
  的应答下发)

测试:
- 新增 TestProcTemplate_UsesKernelArenaRPC:模板必须调用 arena.alloc/
  arena.free,且不得再出现 arenaUsed/arenaWrite(回归保护)
- TestProcTemplate_RejectsVersionMismatch 更新为 §13.1 后的「统一区域
  魔数不匹配」文案
- 修正模板与 sdk/plugin.go 的 gofmt 对齐(含补上 ContextPolicy 字段)
This commit is contained in:
JianFeeeee
2026-09-10 17:58:06 +08:00
parent 18fec9b003
commit 71e3325439
3 changed files with 146 additions and 50 deletions

View File

@ -133,13 +133,13 @@ func TestProcTemplate_CoversAllCoreMethods(t *testing.T) {
}
}
// 模板必须处理内核发来的全部 7 个调用(原 C ABI 的 7 个 //export)。
// 模板必须处理内核发来的全部调用(含无法 JSON 序列化的 Cleaner 回调)。
func TestProcTemplate_HandlesAllKernelCalls(t *testing.T) {
src := loadProcTemplate(t)
for _, m := range []string{
"handshake",
"plugin.init", "plugin.start", "plugin.stop",
"tool.invoke", "stage.invoke", "output.invoke",
"tool.invoke", "cleaner.invoke", "stage.invoke", "output.invoke",
} {
if !strings.Contains(src, `case "`+m+`"`) {
t.Errorf("模板未处理内核调用 %q", m)
@ -147,6 +147,31 @@ func TestProcTemplate_HandlesAllKernelCalls(t *testing.T) {
}
}
// 模板必须通过 arena.alloc / arena.free 向内核申请与归还共享内存。
//
// 共享内存是内核独占管理的**内部实现**:插件不能自己维护分配游标。
// 历史上两版跨进程分配器(bump 游标 / 模板内位图 CAS)都因为把可变
// 分配状态放在共享内存里而出竞态,所以这里做回归保护。
func TestProcTemplate_UsesKernelArenaRPC(t *testing.T) {
src := loadProcTemplate(t)
for _, m := range []string{`"arena.alloc"`, `"arena.free"`} {
if !strings.Contains(src, m) {
t.Errorf("模板缺少内核共享内存 RPC %s(插件必须向内核申请/归还)", m)
}
}
// 禁止插件侧再出现本地分配器符号。
//
// 只查代码不查注释:注释里会解释“为什么不再这么做”。
code := stripComments(t, src)
for _, forbidden := range []string{"arenaUsed", "arenaWrite"} {
if strings.Contains(code, forbidden) {
t.Errorf("模板不应再出现插件侧分配器 %q(共享内存由内核独占管理)", forbidden)
}
}
}
// 共享段布局常量必须与内核 internal/plugin/proc/shm.go 一致。
//
// 字段索引错位是最危险的漂移:插件会读到相邻字段的数据,
@ -281,10 +306,11 @@ func TestProcTemplate_DispatchesRequestsConcurrently(t *testing.T) {
}
}
// 协议与共享段版本不匹配必须拒绝,不得半兼容运行。
// 协议与共享内存区域版本/魔数不匹配必须拒绝,不得半兼容运行。
func TestProcTemplate_RejectsVersionMismatch(t *testing.T) {
src := loadProcTemplate(t)
for _, want := range []string{"协议版本不匹配", "共享段版本不匹配", "共享段魔数不匹配"} {
// §13.1 起共享段合并为单一「统一区域」,魔数校验文案随之更新。
for _, want := range []string{"协议版本不匹配", "共享段版本不匹配", "统一区域魔数不匹配"} {
if !strings.Contains(src, want) {
t.Errorf("握手应校验并拒绝 %q", want)
}