mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 17:38:10 +00:00
- unified.go: SuperBlock 布局 + SharedRef 类型 - NewHost(): 两段 memfd 合并为单一 memfd,fd 3 = 统一区域,fd 4 = eventfd - 子进程侧: testdata 插件从 SuperBlock 解析 ctxOff/evtOff - streaming 测试: EvtConsumer 协程在 host.Close 前 Stop+Wait 防 SIGSEGV
236 lines
5.4 KiB
Go
236 lines
5.4 KiB
Go
//go:build ignore
|
||
|
||
// appendplugin 在 stage 中把自己的标记追加到 FinalText(读-改-写)。
|
||
//
|
||
// 用于跨进程 lost update 验证:多个此类插件并发处理同一 stage,
|
||
// 若全部标记都保留 ⇒ 无丢失;若少了 ⇒ 出现 lost update。
|
||
//
|
||
// 这是实验 8(5 进程 × 300 轮零丢失)在真实 RPC + 真实内核 RunStage
|
||
// 下的复刻——机制单测已过,这里验证集成后同样成立。
|
||
package main
|
||
|
||
import (
|
||
"bufio"
|
||
"encoding/binary"
|
||
"encoding/json"
|
||
"fmt"
|
||
"os"
|
||
"sync"
|
||
"syscall"
|
||
)
|
||
|
||
type request struct {
|
||
ID uint64 `json:"id,omitempty"`
|
||
Method string `json:"method"`
|
||
Params json.RawMessage `json:"params,omitempty"`
|
||
}
|
||
|
||
type response struct {
|
||
ID uint64 `json:"id"`
|
||
Result interface{} `json:"result,omitempty"`
|
||
Error string `json:"error,omitempty"`
|
||
}
|
||
|
||
var (
|
||
out = bufio.NewWriter(os.Stdout)
|
||
writeMu sync.Mutex
|
||
|
||
nextID uint64
|
||
pendMu sync.Mutex
|
||
pending = map[uint64]chan json.RawMessage{}
|
||
|
||
shm []byte
|
||
tag string
|
||
)
|
||
|
||
func send(v interface{}) {
|
||
b, _ := json.Marshal(v)
|
||
writeMu.Lock()
|
||
out.Write(b)
|
||
out.WriteByte('\n')
|
||
out.Flush()
|
||
writeMu.Unlock()
|
||
}
|
||
|
||
func callKernel(method string, params interface{}) (json.RawMessage, bool) {
|
||
pendMu.Lock()
|
||
nextID++
|
||
id := nextID
|
||
ch := make(chan json.RawMessage, 1)
|
||
pending[id] = ch
|
||
pendMu.Unlock()
|
||
|
||
var raw json.RawMessage
|
||
if params != nil {
|
||
b, _ := json.Marshal(params)
|
||
raw = b
|
||
}
|
||
send(request{ID: id, Method: method, Params: raw})
|
||
r, ok := <-ch
|
||
return r, ok
|
||
}
|
||
|
||
const (
|
||
headerSize = 64
|
||
stageFieldCount = 18
|
||
sliceSize = 8
|
||
flagCount = 8
|
||
|
||
offArenaBase = 8
|
||
offArenaCap = 12
|
||
offArenaUsed = 16
|
||
offCtxBase = 20
|
||
offSeq = 24
|
||
|
||
fFinalText = 5 // 与 proc/shmcodec.go 的 stageField 枚举顺序一致
|
||
)
|
||
|
||
func desc(field int) (uint32, uint32) {
|
||
cb := binary.LittleEndian.Uint32(shm[offCtxBase:])
|
||
o := cb + uint32(field*sliceSize)
|
||
return binary.LittleEndian.Uint32(shm[o:]), binary.LittleEndian.Uint32(shm[o+4:])
|
||
}
|
||
|
||
func setDesc(field int, off, ln uint32) {
|
||
cb := binary.LittleEndian.Uint32(shm[offCtxBase:])
|
||
o := cb + uint32(field*sliceSize)
|
||
binary.LittleEndian.PutUint32(shm[o:], off)
|
||
binary.LittleEndian.PutUint32(shm[o+4:], ln)
|
||
}
|
||
|
||
func readFinalText() string {
|
||
off, ln := desc(fFinalText)
|
||
if off == 0 && ln == 0 {
|
||
return ""
|
||
}
|
||
if ln == 0 {
|
||
return ""
|
||
}
|
||
base := binary.LittleEndian.Uint32(shm[offArenaBase:])
|
||
return string(shm[base+off : base+off+ln])
|
||
}
|
||
|
||
func writeFinalText(s string) error {
|
||
used := binary.LittleEndian.Uint32(shm[offArenaUsed:])
|
||
if used == 0 {
|
||
used = 1
|
||
}
|
||
cap_ := binary.LittleEndian.Uint32(shm[offArenaCap:])
|
||
if used+uint32(len(s)) > cap_ {
|
||
return fmt.Errorf("arena 空间不足")
|
||
}
|
||
base := binary.LittleEndian.Uint32(shm[offArenaBase:])
|
||
copy(shm[base+used:], []byte(s))
|
||
binary.LittleEndian.PutUint32(shm[offArenaUsed:], used+uint32(len(s)))
|
||
setDesc(fFinalText, used, uint32(len(s)))
|
||
// 世代号自增
|
||
v := binary.LittleEndian.Uint64(shm[offSeq:])
|
||
binary.LittleEndian.PutUint64(shm[offSeq:], v+1)
|
||
return nil
|
||
}
|
||
|
||
func main() {
|
||
tag = os.Getenv("PLUGIN_TAG")
|
||
if tag == "" {
|
||
tag = "?"
|
||
}
|
||
|
||
in := bufio.NewScanner(bufio.NewReader(os.Stdin))
|
||
in.Buffer(make([]byte, 0, 64*1024), 1024*1024)
|
||
|
||
for in.Scan() {
|
||
line := make([]byte, len(in.Bytes()))
|
||
copy(line, in.Bytes())
|
||
|
||
var probe struct {
|
||
ID uint64 `json:"id"`
|
||
Method string `json:"method"`
|
||
}
|
||
if json.Unmarshal(line, &probe) != nil {
|
||
continue
|
||
}
|
||
if probe.Method == "" {
|
||
var resp struct {
|
||
ID uint64 `json:"id"`
|
||
Result json.RawMessage `json:"result"`
|
||
}
|
||
json.Unmarshal(line, &resp)
|
||
pendMu.Lock()
|
||
ch, ok := pending[resp.ID]
|
||
delete(pending, resp.ID)
|
||
pendMu.Unlock()
|
||
if ok {
|
||
ch <- resp.Result
|
||
}
|
||
continue
|
||
}
|
||
|
||
var req request
|
||
json.Unmarshal(line, &req)
|
||
|
||
switch req.Method {
|
||
case "handshake":
|
||
var hp struct {
|
||
ShmSize int `json:"shm_size"`
|
||
}
|
||
json.Unmarshal(req.Params, &hp)
|
||
if hp.ShmSize > 0 {
|
||
m, err := syscall.Mmap(3, 0, hp.ShmSize,
|
||
syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
|
||
if err != nil {
|
||
send(response{ID: req.ID, Error: fmt.Sprintf("mmap: %v", err)})
|
||
continue
|
||
}
|
||
// 统一区域:前 64B 是 SuperBlock,StageContext 段在其后
|
||
ctxOff := binary.LittleEndian.Uint32(m[20:])
|
||
ctxSize := binary.LittleEndian.Uint32(m[24:])
|
||
shm = m[ctxOff : ctxOff+ctxSize]
|
||
}
|
||
send(response{ID: req.ID, Result: map[string]interface{}{
|
||
"protocol": 1, "sdk_version": "test",
|
||
"plugin_name": "append-" + tag, "pid": os.Getpid(),
|
||
}})
|
||
|
||
case "plugin.init":
|
||
send(response{ID: req.ID})
|
||
|
||
case "plugin.start":
|
||
go func(id uint64) {
|
||
callKernel("stage.register", map[string]interface{}{
|
||
"stage": "after_toolcall",
|
||
"scope": "global",
|
||
})
|
||
send(response{ID: id})
|
||
}(req.ID)
|
||
|
||
case "plugin.stop":
|
||
send(response{ID: req.ID})
|
||
out.Flush()
|
||
os.Exit(0)
|
||
|
||
case "stage.invoke":
|
||
go func(id uint64) {
|
||
if shm == nil {
|
||
send(response{ID: id, Error: "共享段未挂载"})
|
||
return
|
||
}
|
||
// 拿锁 → 读 → 追加自己的标记 → 写回 → 放锁
|
||
callKernel("stage.lock", nil)
|
||
cur := readFinalText()
|
||
err := writeFinalText(cur + tag)
|
||
callKernel("stage.unlock", nil)
|
||
if err != nil {
|
||
send(response{ID: id, Error: err.Error()})
|
||
return
|
||
}
|
||
send(response{ID: id, Result: map[string]interface{}{"dirty_fields": 1}})
|
||
}(req.ID)
|
||
|
||
default:
|
||
if req.ID != 0 {
|
||
send(response{ID: req.ID})
|
||
}
|
||
}
|
||
}
|
||
}
|