Skip to content

统一读值视图 kvspaceGetView:shm 零拷贝借用 / durable 拷贝,owned 标记统一释放 #8

Description

@miaobyte

问题

读值现有两条割裂路径,消费者(runtime/layout/扩展)被迫按后端手工分支:

API 语义 shm durable
kvspaceGet(h,key,out,out_len) 恒拷贝,调用方 kvspaceBytesFree malloc+memcpy(在零拷贝之上再抄一遍) malloc+memcpy(传输本身)
kvspaceXvalueBodyPtr(h,key,resolve,out_head) 零拷贝借用 body 指针,勿 free 直指 mmap 返回 NULL(unsupported)

后果:

  1. 想吃 shm 零拷贝的消费者必须写 p=BodyPtr(...); if(!p){ Get(...); …; BytesFree(...);}——两套 free 语义、两条代码路径,易错。
  2. 反过来,图省事一律 kvspaceGet 的消费者,在 shm 上白白多一次 malloc+memcpy:kvlang#204 端到端实测该拷贝层 ≈35 ns/op,纯属可省。
  3. BodyPtr 只给 body 指针,不给整条 XValue 长度;Get 给整条。返回形状也不统一。

目标

单一读路径:shm 走零拷贝借用、durable 走拷贝,二者同一调用签名 + 同一释放契约。消费者不再按后端分支,owned 标记决定是否 free,无脑调 Release。

设计:ownership-tagged value view

typedef struct {
    const uint8_t *data;   /* 整条 XValue(head+body);借用=指向 mmap,拥有=malloc buffer */
    uint32_t       len;    /* 整条字节数 */
    kvspaceHead_t  head;   /* 已解码;body = data + head.body_offset,长度 head.body_len */
    uint8_t        owned;  /* 0=借用(勿 free,随槽失效)  1=拥有(须 Release) */
} kvspaceView;

/* 读视图:shm 借用零拷贝 / durable 拷贝,统一 owned 标记。0=成功。 */
int  kvspaceGetView(void *h, const char *key, int resolve, kvspaceView *out);
/* owned→free,借用→no-op;调用方永远无脑调用,不看后端。 */
void kvspaceViewRelease(kvspaceView *v);

消费者代码从此后端无关:

kvspaceView v;
if (kvspaceGetView(h, "a", 0, &v) == 0) {
    int64_t a = read_int64(v.data + v.head.body_offset);
    /* ... use v.head / body ... */
    kvspaceViewRelease(&v);
}

关键:纯前端组合,两后端 ABI 不变

GetView 复用现有符号,不新增后端符号——沿用 frontend.c 已有的「BodyPtr = shmget + DecodeHead」组合套路:

int kvspaceGetView(void *h, const char *key, int resolve, kvspaceView *out) {
    if (!h || !key || !out) return 1;
    kvspace_handle *x = H(h);
    memset(out, 0, sizeof *out);
    if (x->vt->shmget) {                    /* shm:借用 */
        int32_t len = 0;
        uint8_t *d = x->vt->shmget(x->backend, key, resolve, &len);
        if (!d || len <= 0) return 1;
        out->data = d; out->len = (uint32_t)len; out->owned = 0;
    } else {                                /* durable:拷贝 */
        uint8_t *d = NULL; uint32_t len = 0;
        if (x->vt->get(x->backend, key, &d, &len) != 0 || !d || len == 0) return 1;
        out->data = d; out->len = len; out->owned = 1;
    }
    return kvspaceDecodeHead(out->data, out->len, &out->head);
}

void kvspaceViewRelease(kvspaceView *v) {
    if (v && v->owned && v->data) kvspaceBytesFree((uint8_t *)v->data, v->len);
    if (v) { v->data = NULL; v->len = 0; }
}

kvspaceGet / kvspaceXvalueBodyPtr 保留(向后兼容),GetView 是二者的统一上位替代。

生命周期契约(借用 vs 拥有,须显式写死不可隐藏)

  • 借用视图(owned=0,shm):有效期至该 key 的下一次 Set/Del/CpkvspaceClose不得跨写留存。与 kvspace-c#13 的同尺寸就地覆写一致——后续 Set 可能就地改写这块借用字节,故借用视图对同槽写入非快照稳定
  • 拥有视图(owned=1,durable,或 shm 强制拷贝):有效期至 Release,是私有快照,免疫并发改写。
  • 需要跨写稳定快照的调用方:从借用视图 memcpy 出去,或用下面的强制拷贝入口。

可选:shm 上强制拷贝

给需要快照语义的调用方一个旋钮,shm 上也走拷贝、owned=1

int kvspaceGetViewOwned(void *h, const char *key, int resolve, kvspaceView *out);
/* shm:shmget 后 malloc+memcpy 成私有 buffer;durable:等同 GetView。 */

与约束的一致性

  • 零进程内缓存:借用视图零驻留、零拷贝;durable 的拷贝是传输本身(每次调用的瞬态 buffer,随 Release 释放),非跨调用缓存。本设计不引入任何缓存。
  • 多进程:借用视图只读共享 mmap;契约已声明「写后失效」,与 kvspace-c#13 的共享内存写语义对齐。

可选 phase 2:可写视图(统一零拷贝写)

对称地统一写路径——shm 就地写、durable 编码回传:

typedef struct { uint8_t *body; uint32_t body_cap; void *_impl; uint8_t owned; } kvspaceMutView;
int  kvspaceGetMutView(void *h, const char *key, int resolve, uint32_t want_body_len, kvspaceMutView *out);
int  kvspaceMutViewCommit(kvspaceMutView *v);   /* shm:no-op(写即持久)  durable:encode+Set */
void kvspaceMutViewAbort(kvspaceMutView *v);

shm 返回 mmap 内可写 body 指针(容量足够时就地写,commit 为 no-op);durable 返回临时缓冲,commit 时编码 + Set 落盘。此项较侵入,建议读视图先行、写视图后续单议。

开放点

  • resolve 语义不对称:shmgetresolve 参,durable kvspaceGet 签名无——需确认 durable 是否恒 resolve,或补 resolve-capable get,否则 GetView(resolve=1) 在 durable 上语义待定。
  • GetViewOwned 是否纳入首版,还是留给需要快照的调用方自行 memcpy
  • 写视图(phase 2)的 want_body_len 与 durable 预填当前值的取舍。

关联

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions