From b330b001b96a40707fea725b6157bfe505a235b9 Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Thu, 10 Sep 2026 20:54:22 +0800 Subject: [PATCH 1/3] =?UTF-8?q?runtime:=20=E5=B8=A7=E7=BA=A7=E5=8F=A5?= =?UTF-8?q?=E6=9F=84=E7=BC=93=E5=AD=98=E8=B5=B0=20ByRef=EF=BC=88=E4=B8=8D?= =?UTF-8?q?=E7=A9=BF=E9=80=8F=20Ptr=EF=BC=89=EF=BC=8C=E5=B8=A7=E9=80=80?= =?UTF-8?q?=E5=87=BA=E5=A4=B1=E6=95=88=EF=BC=88#268=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runtime/src/kvspace.c | 75 ++++++++++++++++++++++++++++++++++ runtime/src/runtime_internal.h | 23 +++++++++++ 2 files changed, 98 insertions(+) diff --git a/runtime/src/kvspace.c b/runtime/src/kvspace.c index cff38e63..24edc738 100644 --- a/runtime/src/kvspace.c +++ b/runtime/src/kvspace.c @@ -3,6 +3,43 @@ /* kv 访问统一走 kvspace-durable 兼容 C ABI(kvspace*)。 * 后端由链接的 kvspace 库决定(kvspace-durable / kvspace-c 均导出同一 ABI)。 */ +static kvlangRefEnt_t *ref_find(kvlangKv_t *k, const char *key) { + for (int i = 0; i < k->nref; i++) + if (k->ref[i].key && strcmp(k->ref[i].key, key) == 0) return &k->ref[i]; + return NULL; +} + +static void ref_put(kvlangKv_t *k, const char *key, const kvspaceRef_t *r) { + kvlangRefEnt_t *e = ref_find(k, key); + if (!e) { + if (k->nref < KVLANG_REF_CAP) e = &k->ref[k->nref++]; + else { e = &k->ref[0]; free(e->key); } + e->key = strdup(key); + } + e->block_id = r->block_id; + e->gen = r->gen; +} + +static int ref_ok(kvlangKv_t *k) { + return k->ref_on && kvspaceResolveRef && kvspaceGetByRef; +} + +void kvlangKvInvalidateFrame(kvlangKv_t *k, const char *fr) { + if (!k || !k->ref_on || !fr || !fr[0]) return; + size_t n = strlen(fr); + int w = 0; + for (int i = 0; i < k->nref; i++) { + char *key = k->ref[i].key; + if (key && strncmp(key, fr, n) == 0 && (key[n] == 0 || key[n] == '/')) { + free(key); + continue; + } + if (w != i) k->ref[w] = k->ref[i]; + w++; + } + k->nref = w; +} + kvlangKv_t *kvlangKvConnect(const char *dsn) { kvlangKv_t *k = calloc(1, sizeof(*k)); k->h = kvspaceConnect(dsn); @@ -10,12 +47,14 @@ kvlangKv_t *kvlangKvConnect(const char *dsn) { free(k); return NULL; } + k->ref_on = 1; return k; } void kvlangKvDisconnect(kvlangKv_t *k) { if (!k) return; + for (int i = 0; i < k->nref; i++) free(k->ref[i].key); if (k->h) kvspaceClose(k->h); free(k); @@ -55,10 +94,27 @@ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXv key[dl + nl] = 0; uint8_t *d; uint32_t len; + kvlangRefEnt_t *e = ref_ok(k) ? ref_find(k, key) : NULL; + if (e) { + kvspaceRef_t r = { e->block_id, e->gen }; + if (kvspaceGetByRef(k->h, &r, key, &d, &len) == 0 && d && len > 0) { + e->block_id = r.block_id; + e->gen = r.gen; + out->data = d; + out->len = len; + out->borrowed = 1; + free(key); + return 0; + } + } if (kvspaceGet(k->h, key, 0, &d, &len) == 0 && d && len > 0) { out->data = d; out->len = len; out->borrowed = 1; + if (ref_ok(k)) { + kvspaceRef_t r; + if (kvspaceResolveRef(k->h, key, &r) == 0) ref_put(k, key, &r); + } } if (key != stackbuf) free(key); @@ -100,6 +156,19 @@ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, ui /* 借用值全程有效:durable 惰性写不再清读池,读借用池由 VM 在指令边界统一 ReadReset 回收, * 故写期直接用 v->data,不再需要防御性快照。 */ int rc = 0; + if (n == 1 && ref_ok(k) && kvspaceSetPartByRef && pairs[0].key && pairs[0].val.data && + pairs[0].val.len) { + kvlangRefEnt_t *e = ref_find(k, pairs[0].key); + if (e) { + kvspaceRef_t r = { e->block_id, e->gen }; + if (kvspaceSetPartByRef(k->h, &r, pairs[0].key, 0, pairs[0].val.data, + pairs[0].val.len, err, err_cap) == 0) { + e->block_id = r.block_id; + e->gen = r.gen; + return 0; + } + } + } for (int i = 0; i < n; i++) { const kvlangXvalue_t *v = &pairs[i].val; if (!v->data || v->len == 0) { /* None → 删键,令该槽读回 None(不可静默跳过留旧值) */ @@ -124,6 +193,11 @@ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, ui } if (body_len > 0 && dst) memcpy(dst, body, body_len); + if (rc == 0 && n == 1 && ref_ok(k) && pairs[i].key) { + kvspaceRef_t rr; + if (kvspaceResolveRef(k->h, pairs[i].key, &rr) == 0) + ref_put(k, pairs[i].key, &rr); + } } return rc; } @@ -152,6 +226,7 @@ int kvlangKvDel(kvlangKv_t *k, const char *key, char *err, uint32_t err_cap) { } int kvlangKvDelTree(kvlangKv_t *k, const char *prefix, char *err, uint32_t err_cap) { + kvlangKvInvalidateFrame(k, prefix); return kvspaceDelTree(k->h, prefix, err, err_cap); } diff --git a/runtime/src/runtime_internal.h b/runtime/src/runtime_internal.h index 7c5aa288..1f1cc67c 100644 --- a/runtime/src/runtime_internal.h +++ b/runtime/src/runtime_internal.h @@ -42,6 +42,20 @@ extern void kvspaceClose(void *h); /* 借用读:*out 指向后端常驻/回收空间,调用方不得 free。resolve=1 穿透 link。 */ extern int kvspaceGet(void *h, const char *key, int resolve, uint8_t **out, uint32_t *out_len); + +typedef struct { + uint32_t block_id; + uint32_t gen; +} kvspaceRef_t; +extern int kvspaceResolveRef(void *h, const char *key, kvspaceRef_t *ref) + __attribute__((weak)); +extern int kvspaceGetByRef(void *h, kvspaceRef_t *ref, const char *key_fallback, + uint8_t **out, uint32_t *out_len) + __attribute__((weak)); +extern int kvspaceSetPartByRef(void *h, kvspaceRef_t *ref, + const char *key_fallback, uint32_t offset, + const uint8_t *buf, uint32_t buf_len, char *err, + uint32_t err_cap) __attribute__((weak)); /* 指令边界回收读借用池;定位读/写(分片);只读 head 前缀。见 kvspace.h 契约。 */ extern void kvspaceReadReset(void *h); extern int kvspaceGetPart(void *h, const char *key, uint32_t offset, @@ -128,8 +142,16 @@ typedef struct { kvlangXvalue_t val; } kvlangKvPair_t; +#define KVLANG_REF_CAP 64 +typedef struct { + char *key; + uint32_t block_id, gen; +} kvlangRefEnt_t; typedef struct { void *h; + kvlangRefEnt_t ref[KVLANG_REF_CAP]; + int nref; + int ref_on; } kvlangKv_t; /* growable string buffer */ @@ -340,6 +362,7 @@ int kvlangKvSetChar(kvlangKv_t *k, const char *key, const char *s); int kvlangKvDel(kvlangKv_t *k, const char *key, char *err, uint32_t err_cap); int kvlangKvDelTree(kvlangKv_t *k, const char *prefix, char *err, uint32_t err_cap); +void kvlangKvInvalidateFrame(kvlangKv_t *k, const char *frame_root); int kvlangKvCp(kvlangKv_t *k, const char *src, const char *dst, char *err, uint32_t err_cap); int kvlangKvCpTree(kvlangKv_t *k, const char *src, const char *dst, char *err, From 613ba147736679589f22f3a2e4f834ad284b7375 Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Sun, 13 Sep 2026 16:53:07 +0800 Subject: [PATCH 2/3] =?UTF-8?q?runtime:=20=E7=BC=93=E5=AD=98=E5=8F=B6?= =?UTF-8?q?=E5=AD=90=E7=9A=84=20ART=20=E7=88=B6=E8=8A=82=E7=82=B9=EF=BC=8C?= =?UTF-8?q?=E5=85=84=E5=BC=9F=20key=20=E5=85=B1=E7=94=A8=EF=BC=88#268?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iops N=2000 26.3ms → 23.0ms (−13%) prime_sieve N=100 29.6ms → 26.8ms (−9%) hash_table N=200 18.0ms → 15.6ms (−13%) --- .../results-v0.2.17-20260913T085816Z.csv | 31 ++ runtime/CMakeLists.txt | 16 +- runtime/src/kvspace.c | 416 ++++++++++++++++-- runtime/src/runtime_internal.h | 16 +- runtime/tests/test_getmember_siblings.c | 133 ++++++ 5 files changed, 572 insertions(+), 40 deletions(-) create mode 100644 benchmark/results/results-v0.2.17-20260913T085816Z.csv create mode 100644 runtime/tests/test_getmember_siblings.c diff --git a/benchmark/results/results-v0.2.17-20260913T085816Z.csv b/benchmark/results/results-v0.2.17-20260913T085816Z.csv new file mode 100644 index 00000000..34c46931 --- /dev/null +++ b/benchmark/results/results-v0.2.17-20260913T085816Z.csv @@ -0,0 +1,31 @@ +timestamp,version,commit,cpu,case,input,samples,kvlang_shm_ns,kvlang_fs_ns,kvlang_redis_ns,python_ns,rust_ns,c_ns,python_ver,rust_ver,c_ver,native_opt,valid +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,binary_search,16,3,,,,11798,742,531,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,binary_search,N=32,3,11248867,,,20484,1398,1032,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,binary_search,N=64,3,25728718,,,42554,2911,2276,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,binary_trees,depth=4,3,4995333,,,10191,2461,6522,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,binary_trees,depth=5,3,8572702,,,19169,2984,7000,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,binary_trees,depth=6,3,18040546,,,33821,4343,9618,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,fib,n=8,3,5269445,,,9070,282,305,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,fib,n=9,3,8595727,,,10836,352,428,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,fib,n=10,3,13916483,,,14007,461,563,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,hash_table,N=50,3,4120698,,,22577,4590,300,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,hash_table,N=100,3,7761466,,,36046,7110,415,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,False +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,hash_table,N=200,3,15613934,,,71182,13362,641,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,False +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,iops,N=500,3,6141164,,,22309,311,288,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,iops,N=1000,3,12440658,,,41561,494,477,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,iops,N=2000,3,22967493,,,80723,854,815,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,k_nucleotide,rep=3,3,9486496,,,26614,2902,233,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,k_nucleotide,rep=5,3,18489269,,,38783,5402,326,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,k_nucleotide,rep=8,3,32838675,,,58638,6403,485,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,matmul,4,3,,,,13664,283,307,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,matmul,6,3,,,,36635,458,311,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,matmul,8,3,,,,73670,654,512,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,nqueens,4,3,,,,17756,282,243,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,nqueens,5,3,,,,31120,521,525,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,nqueens,6,3,,,,68969,1361,1402,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,prime_sieve,N=50,3,9680882,,,29576,19297,28774,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,prime_sieve,N=75,3,18065222,,,50362,23877,30867,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,prime_sieve,N=100,3,26816330,,,67240,29433,28993,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,quicksort,"N=32,seed=1",3,7634563,,,18481,1095,1040,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,quicksort,"N=64,seed=1",3,16653101,,,38223,2449,2124,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-13T08:58:16Z,v0.2.17-14-g8e0bb9ef,8e0bb9e,Intel(R) Xeon(R) Gold 6418H,quicksort,"N=128,seed=1",3,31409672,,,73365,5095,4847,3.10.12,1.98.0,14.3.0,gcc -O1 / rustc opt-level=1 / cpython,True diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 67469545..21208549 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -# ── 输出统一到 bin/ ────────────────────────────────────────────── +# Outputs go under bin/. set(BIN_DIR "${CMAKE_SOURCE_DIR}/../bin") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${BIN_DIR}") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${BIN_DIR}") @@ -31,3 +31,17 @@ else() set_target_properties(kvlang_runtime PROPERTIES LINK_FLAGS "-Wl,--export-dynamic -Wl,--disable-new-dtags -Wl,-rpath,${KVSPACE_LIB_DIR}") endif() + +add_custom_command( + OUTPUT "${BIN_DIR}/test_getmember_siblings" + COMMAND ${CMAKE_C_COMPILER} -O2 -D_GNU_SOURCE + -I${CMAKE_SOURCE_DIR}/include -I${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/tests/test_getmember_siblings.c + -o ${BIN_DIR}/test_getmember_siblings + -L${BIN_DIR} + -Wl,--disable-new-dtags + -Wl,-rpath,${BIN_DIR} + -Wl,--no-as-needed -lkvspace -Wl,--as-needed -lkvlang_runtime + DEPENDS kvlang_runtime "${CMAKE_SOURCE_DIR}/tests/test_getmember_siblings.c" + VERBATIM) +add_custom_target(test_getmember_siblings ALL DEPENDS "${BIN_DIR}/test_getmember_siblings") diff --git a/runtime/src/kvspace.c b/runtime/src/kvspace.c index 24edc738..0bbae349 100644 --- a/runtime/src/kvspace.c +++ b/runtime/src/kvspace.c @@ -1,11 +1,12 @@ #include "runtime_internal.h" -/* kv 访问统一走 kvspace-durable 兼容 C ABI(kvspace*)。 - * 后端由链接的 kvspace 库决定(kvspace-durable / kvspace-c 均导出同一 ABI)。 */ +/* KV access via the kvspace C ABI. Backend is chosen at runtime from the DSN. */ static kvlangRefEnt_t *ref_find(kvlangKv_t *k, const char *key) { - for (int i = 0; i < k->nref; i++) - if (k->ref[i].key && strcmp(k->ref[i].key, key) == 0) return &k->ref[i]; + for (int i = 0; i < k->nref; i++) { + if (k->ref[i].key && strcmp(k->ref[i].key, key) == 0) + return &k->ref[i]; + } return NULL; } @@ -24,6 +25,236 @@ static int ref_ok(kvlangKv_t *k) { return k->ref_on && kvspaceResolveRef && kvspaceGetByRef; } +static kvlangRefEnt_t *pref_find(kvlangKv_t *k, const char *dir, size_t dl) { + for (int i = 0; i < k->npref; i++) { + const char *pk = k->pref[i].key; + if (pk && k->pref[i].gen && k->pref[i].klen == dl && memcmp(pk, dir, dl) == 0) + return &k->pref[i]; + } + return NULL; +} + +static void hot_clear(kvlangKv_t *k) { + for (int i = 0; i < k->nhot; i++) { + free(k->hot[i].name); + free(k->hot[i].key); + k->hot[i].name = k->hot[i].key = NULL; + k->hot[i].block_id = k->hot[i].gen = k->hot[i].dlen = 0; + } + k->nhot = 0; +} + +static int hot_name_ok(const char *name) { + char c; + if (!name || name[1] != 0) + return 0; + c = name[0]; + return c == 'a' || c == 'i' || c == 'n'; +} + +static inline int hot_get(kvlangKv_t *k, const char *dir, const char *name, + uint8_t **d, uint32_t *len) { + if (!dir || !hot_name_ok(name)) + return 0; + for (int i = 0; i < k->nhot; i++) { + uint32_t dl; + if (!k->hot[i].name || k->hot[i].name[0] != name[0]) + continue; + dl = k->hot[i].dlen; + if (!k->hot[i].key || strncmp(dir, k->hot[i].key, dl) != 0 || dir[dl] != 0) + continue; + kvspaceRef_t r = { k->hot[i].block_id, k->hot[i].gen, 0, 0 }; + if (kvspaceGetByRef(k->h, &r, k->hot[i].key, d, len) == 0 && *d && *len > 0) { + k->hot[i].block_id = r.block_id; + k->hot[i].gen = r.gen; + return 1; + } + } + return 0; +} + +/* Leaf refs only (gen==0). Skip /lib/. */ +static void hot_put(kvlangKv_t *k, const char *name, const char *key, + uint32_t block_id, uint32_t gen) { + kvlangHotEnt_t *e; + size_t kl; + if (!hot_name_ok(name) || !key || !block_id || gen != 0) + return; + if (strncmp(key, "/lib/", 5) == 0) + return; + kl = strlen(key); + if (kl < 1) + return; + for (int i = 0; i < k->nhot; i++) { + if (k->hot[i].name && k->hot[i].name[0] == name[0] && k->hot[i].name[1] == 0) { + if (k->hot[i].key && strcmp(k->hot[i].key, key) == 0) { + k->hot[i].block_id = block_id; + k->hot[i].gen = gen; + return; + } + /* Same name, other frame: keep the first key; `n` may use a second slot. */ + if (name[0] != 'n' || k->nhot >= KVLANG_HOT_CAP) + return; + break; + } + } + if (k->nhot < KVLANG_HOT_CAP) + e = &k->hot[k->nhot++]; + else { + e = &k->hot[KVLANG_HOT_CAP - 1]; + free(e->name); + free(e->key); + } + e->name = strdup(name); + e->key = strdup(key); + if (!e->name || !e->key) { + free(e->name); + free(e->key); + e->name = e->key = NULL; + if (k->nhot > 0 && e == &k->hot[k->nhot - 1]) + k->nhot--; + return; + } + e->block_id = block_id; + e->gen = gen; + e->dlen = (uint32_t)(kl - 1); +} + +static inline kvlangHotEnt_t *hot_find_key(kvlangKv_t *k, const char *key) { + if (!key) + return NULL; + for (int i = 0; i < k->nhot; i++) { + if (k->hot[i].key && k->hot[i].gen == 0 && + strcmp(k->hot[i].key, key) == 0) + return &k->hot[i]; + } + return NULL; +} + +static void parent_clear(kvlangKv_t *k) { + for (int i = 0; i < k->npref; i++) + free(k->pref[i].key); + k->npref = 0; + k->pref_i = 0; + free(k->fpar.key); + k->fpar.key = NULL; + k->fpar.block_id = k->fpar.gen = k->fpar.klen = 0; + hot_clear(k); +} + +static int dir_is_member(const char *dir, size_t dl) { + return dl >= MEMBER_SEP_LEN && + (unsigned char)dir[dl - 2] == 0xC2 && + (unsigned char)dir[dl - 1] == 0xB7; +} + +static void parent_put(kvlangKv_t *k, const char *dir, size_t dl, const kvspaceRef_t *rr) { + kvlangRefEnt_t *e; + if (!dir || !dl || !rr->parent_id || !rr->depth) + return; + if (dl >= 5 && memcmp(dir, "/lib/", 5) == 0) + return; + if (!dir_is_member(dir, dl)) { + if (k->fpar.key && (k->fpar.klen != dl || memcmp(k->fpar.key, dir, dl) != 0)) { + free(k->fpar.key); + k->fpar.key = NULL; + } + if (!k->fpar.key) { + k->fpar.key = malloc(dl + 1); + if (!k->fpar.key) + return; + memcpy(k->fpar.key, dir, dl); + k->fpar.key[dl] = 0; + } + k->fpar.block_id = rr->parent_id; + k->fpar.gen = rr->depth; + k->fpar.klen = (uint32_t)dl; + return; + } + e = pref_find(k, dir, dl); + if (!e) { + if (k->npref < KVLANG_PREF_CAP) + e = &k->pref[k->npref++]; + else { + e = &k->pref[k->pref_i]; + free(e->key); + k->pref_i = (k->pref_i + 1) % KVLANG_PREF_CAP; + } + e->key = malloc(dl + 1); + if (!e->key) + return; + memcpy(e->key, dir, dl); + e->key[dl] = 0; + } + e->block_id = rr->parent_id; + e->gen = rr->depth; + e->klen = (uint32_t)dl; +} + +static int last_dir_sep(const char *key, size_t *seplen) { + const char *slash = strrchr(key, '/'); + const char *start = slash ? slash + 1 : key; + const char *mid = NULL; + for (const char *p = start; *p; p++) { + if ((unsigned char)p[0] == 0xC2 && (unsigned char)p[1] == 0xB7) + mid = p; + } + if (mid) { + *seplen = MEMBER_SEP_LEN; + return (int)(mid - key); + } + if (slash && slash > key) { + *seplen = 1; + return (int)(slash - key); + } + *seplen = 0; + return -1; +} + +static int parent_prefix_ok(const kvlangRefEnt_t *e, const char *key) { + size_t dl; + const char *p; + if (!e || !e->key || !e->gen || !key) + return 0; + dl = e->klen; + if (memcmp(key, e->key, dl) != 0 || !key[dl]) + return 0; + for (p = key + dl; *p; p++) { + if (*p == '/' || + ((unsigned char)p[0] == 0xC2 && (unsigned char)p[1] == 0xB7)) + return 0; + } + return 1; +} + +static kvlangRefEnt_t *pref_cover(kvlangKv_t *k, const char *key) { + if (!key) + return NULL; + for (int i = 0; i < k->npref; i++) { + if (parent_prefix_ok(&k->pref[i], key)) { + if (i != 0) { + kvlangRefEnt_t tmp = k->pref[0]; + k->pref[0] = k->pref[i]; + k->pref[i] = tmp; + } + return &k->pref[0]; + } + } + return NULL; +} + +static int parent_hit_ent(kvlangKv_t *k, const kvlangRefEnt_t *e, const char *key, + uint8_t **d, uint32_t *len) { + if (!parent_prefix_ok(e, key)) + return 0; + kvspaceRef_t r = { e->block_id, e->gen, 0, 0 }; + return kvspaceGetByRef(k->h, &r, key, d, len) == 0 && *d && *len > 0; +} + +static int parent_hit(kvlangKv_t *k, const char *key, uint8_t **d, uint32_t *len) { + return parent_hit_ent(k, pref_cover(k, key), key, d, len); +} + void kvlangKvInvalidateFrame(kvlangKv_t *k, const char *fr) { if (!k || !k->ref_on || !fr || !fr[0]) return; size_t n = strlen(fr); @@ -38,6 +269,43 @@ void kvlangKvInvalidateFrame(kvlangKv_t *k, const char *fr) { w++; } k->nref = w; + { + int pw = 0; + for (int i = 0; i < k->npref; i++) { + char *key = k->pref[i].key; + if (key && strncmp(key, fr, n) == 0 && (key[n] == 0 || key[n] == '/')) { + free(key); + continue; + } + if (pw != i) + k->pref[pw] = k->pref[i]; + pw++; + } + k->npref = pw; + if (k->pref_i >= k->npref) + k->pref_i = 0; + } + if (k->fpar.key && strncmp(k->fpar.key, fr, n) == 0 && + (k->fpar.key[n] == 0 || k->fpar.key[n] == '/')) { + free(k->fpar.key); + k->fpar.key = NULL; + k->fpar.block_id = k->fpar.gen = k->fpar.klen = 0; + } + { + int hw = 0; + for (int i = 0; i < k->nhot; i++) { + char *key = k->hot[i].key; + if (key && strncmp(key, fr, n) == 0 && (key[n] == 0 || key[n] == '/')) { + free(k->hot[i].name); + free(key); + continue; + } + if (hw != i) + k->hot[hw] = k->hot[i]; + hw++; + } + k->nhot = hw; + } } kvlangKv_t *kvlangKvConnect(const char *dsn) { @@ -55,17 +323,31 @@ void kvlangKvDisconnect(kvlangKv_t *k) { if (!k) return; for (int i = 0; i < k->nref; i++) free(k->ref[i].key); + parent_clear(k); if (k->h) kvspaceClose(k->h); free(k); } -/* 借用读(resolve=0,raw):out 直接借 kvspace 常驻/借用池指针(borrowed=1,不 free、不入 - * kvlangKvSet 前不跨写)。空值 → out len=0。 */ +/* Borrowed read (resolve=0). Empty -> out len=0. */ int kvlangKvGetOne(kvlangKv_t *k, const char *key, kvlangXvalue_t *out) { kvlangXvalueZero(out); uint8_t *d; uint32_t len; + if (ref_ok(k) && k->npref && key) { + kvlangRefEnt_t *pe = (k->npref == 1) + ? (parent_prefix_ok(&k->pref[0], key) ? &k->pref[0] : NULL) + : pref_cover(k, key); + if (pe) { + kvspaceRef_t r = { pe->block_id, pe->gen, 0, 0 }; + if (kvspaceGetByRef(k->h, &r, key, &d, &len) == 0 && d && len > 0) { + out->data = d; + out->len = len; + out->borrowed = 1; + return 0; + } + } + } if (kvspaceGet(k->h, key, 0, &d, &len) != 0) return -1; if (d && len > 0) { @@ -76,34 +358,74 @@ int kvlangKvGetOne(kvlangKv_t *k, const char *key, kvlangXvalue_t *out) { return 0; } -/* Frame member: dir 直连 name 组键,借用读(resolve=0:拿 Ptr 本体,不穿透 link—— - * 解引用由 runtime 显式按 target 形态判别,见 ResolveReadValue/ResolveWriteSlot); - * 空值 → out len=0。 */ +/* Frame member: concat dir+name, borrowed read. Empty -> out len=0. */ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXvalue_t *out) { kvlangXvalueZero(out); if (!name || !name[0]) return 0; + uint8_t *d; + uint32_t len; + if (ref_ok(k) && dir && k->nhot && hot_get(k, dir, name, &d, &len)) { + out->data = d; + out->len = len; + out->borrowed = 1; + return 0; + } size_t dl = strlen(dir), nl = strlen(name); /* 短 key 走栈缓冲,免读参热路径每步一次 malloc。 */ char stackbuf[512]; char *key = stackbuf; - if (dl + nl + 1 > sizeof stackbuf) + if (dl + nl + 1 > sizeof stackbuf) { key = malloc(dl + nl + 1); + if (!key) + return -1; + } memcpy(key, dir, dl); memcpy(key + dl, name, nl); key[dl + nl] = 0; - uint8_t *d; - uint32_t len; + if (ref_ok(k) && !hot_name_ok(name) && k->fpar.key && + memcmp(k->fpar.key, dir, k->fpar.klen) == 0 && dir[k->fpar.klen] == 0) { + kvspaceRef_t r = { k->fpar.block_id, k->fpar.gen, 0, 0 }; + if (kvspaceGetByRef(k->h, &r, key, &d, &len) == 0 && d && len > 0) { + out->data = d; + out->len = len; + out->borrowed = 1; + if (key != stackbuf) + free(key); + return 0; + } + } kvlangRefEnt_t *e = ref_ok(k) ? ref_find(k, key) : NULL; if (e) { - kvspaceRef_t r = { e->block_id, e->gen }; + kvspaceRef_t r = { e->block_id, e->gen, 0, 0 }; if (kvspaceGetByRef(k->h, &r, key, &d, &len) == 0 && d && len > 0) { e->block_id = r.block_id; e->gen = r.gen; + hot_put(k, name, key, r.block_id, r.gen); out->data = d; out->len = len; out->borrowed = 1; - free(key); + if (key != stackbuf) + free(key); + return 0; + } + } + if (ref_ok(k)) { + int hit = 0; + if (nl >= MEMBER_SEP_LEN && memchr(name, 0xC2, nl)) { + if (k->npref) + hit = parent_hit(k, key, &d, &len); + } else if (hot_name_ok(name) && k->fpar.key && + memcmp(k->fpar.key, dir, k->fpar.klen) == 0 && dir[k->fpar.klen] == 0) { + kvspaceRef_t r = { k->fpar.block_id, k->fpar.gen, 0, 0 }; + hit = kvspaceGetByRef(k->h, &r, key, &d, &len) == 0 && d && len > 0; + } + if (hit) { + out->data = d; + out->len = len; + out->borrowed = 1; + if (key != stackbuf) + free(key); return 0; } } @@ -111,20 +433,16 @@ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXv out->data = d; out->len = len; out->borrowed = 1; - if (ref_ok(k)) { - kvspaceRef_t r; - if (kvspaceResolveRef(k->h, key, &r) == 0) ref_put(k, key, &r); - } } if (key != stackbuf) free(key); return 0; } -/* 指令边界回收读借用池:VM 每条指令末调一次。 */ +/* Recycle the read-borrow pool at instruction boundaries. */ void kvlangKvReadReset(kvlangKv_t *k) { kvspaceReadReset(k->h); } -/* 定位读:借用读 key 值的 [off, off+len) 字节 → out(borrowed=1)。空/越界 → out len=0。 */ +/* Borrowed slice of the value body. Empty/OOB -> out len=0. */ int kvlangKvGetPart(kvlangKv_t *k, const char *key, uint32_t off, uint32_t len, kvlangXvalue_t *out) { kvlangXvalueZero(out); uint8_t *d; @@ -139,47 +457,58 @@ int kvlangKvGetPart(kvlangKv_t *k, const char *key, uint32_t off, uint32_t len, return 0; } -/* 定位写:就地写 buf 到 key 值的 [off, off+buf_len)(key 须已存在、不改结构)。 */ +/* In-place write into an existing value. Key must already exist. */ int kvlangKvSetPart(kvlangKv_t *k, const char *key, uint32_t off, const uint8_t *buf, uint32_t buf_len, char *err, uint32_t err_cap) { return kvspaceSetPart(k->h, key, off, buf, buf_len, err, err_cap); } -/* 读 head:只读值前缀解码三正交轴 head(不取 body)。空/不存在 → 非 0。 */ +/* Read head only (no body). Missing/empty -> non-zero. */ int kvlangKvGetHead(kvlangKv_t *k, const char *key, kvspaceHead_t *out) { return kvspaceGetHead(k->h, key, out); } -/* 写即构造:逐条解 head 取 (langtype, body)——同 body_len 就地(WriteInPlace),否则新位置 - * (WriteNewPlace)——向 kvspace 要 body 偏移指针后直接写字节,无预合并缓冲。 */ +/* Write each pair: same body_len -> in place, else new place. */ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, uint32_t err_cap) { - /* 借用值全程有效:durable 惰性写不再清读池,读借用池由 VM 在指令边界统一 ReadReset 回收, - * 故写期直接用 v->data,不再需要防御性快照。 */ int rc = 0; if (n == 1 && ref_ok(k) && kvspaceSetPartByRef && pairs[0].key && pairs[0].val.data && pairs[0].val.len) { - kvlangRefEnt_t *e = ref_find(k, pairs[0].key); + const char *key = pairs[0].key; + kvlangHotEnt_t *he = k->nhot ? hot_find_key(k, key) : NULL; + if (he) { + kvspaceRef_t r = { he->block_id, he->gen, 0, 0 }; + if (kvspaceSetPartByRef(k->h, &r, key, 0, pairs[0].val.data, + pairs[0].val.len, err, err_cap) == 0) { + he->block_id = r.block_id; + he->gen = r.gen; + return 0; + } + } + kvlangRefEnt_t *e = ref_find(k, key); if (e) { - kvspaceRef_t r = { e->block_id, e->gen }; - if (kvspaceSetPartByRef(k->h, &r, pairs[0].key, 0, pairs[0].val.data, + kvspaceRef_t r = { e->block_id, e->gen, 0, 0 }; + if (kvspaceSetPartByRef(k->h, &r, key, 0, pairs[0].val.data, pairs[0].val.len, err, err_cap) == 0) { e->block_id = r.block_id; e->gen = r.gen; + { + const char *slash = strrchr(key, '/'); + const char *nm = slash ? slash + 1 : key; + hot_put(k, nm, key, r.block_id, r.gen); + } return 0; } } } for (int i = 0; i < n; i++) { const kvlangXvalue_t *v = &pairs[i].val; - if (!v->data || v->len == 0) { /* None → 删键,令该槽读回 None(不可静默跳过留旧值) */ + if (!v->data || v->len == 0) { /* None deletes the key */ const char *dk[1] = {pairs[i].key}; kvspaceDel(k->h, dk, 1, err, err_cap); continue; } kvspaceHead_t h; - /* head 解码失败才跳过:langtype 为空**不是**跳过理由——空 langtype 的 Ptr 仍是 - * 指针(如跨帧传一个值为 None 的容器实参时,runtime 推不出类型)。None 由上面 - * 的 len==0 分支处理,二者不是一回事。曾因这里静默跳过,致 fs 后端帧槽丢参数。 */ + /* Skip only if head decode fails. Empty langtype is still a valid Ptr. */ if (kvspaceDecodeHead(v->data, v->len, &h) != 0) continue; uint32_t body_len = h.body_len < 0 ? 0 : (uint32_t)h.body_len; @@ -194,9 +523,26 @@ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, ui if (body_len > 0 && dst) memcpy(dst, body, body_len); if (rc == 0 && n == 1 && ref_ok(k) && pairs[i].key) { + const char *key = pairs[i].key; + const char *slash = strrchr(key, '/'); + const char *rest = slash ? slash + 1 : key; + int is_member = rest && memchr(rest, 0xC2, strlen(rest)) != NULL; + if (is_member && (pref_cover(k, key) || + (key[0] == '/' && strncmp(key, "/lib/", 5) == 0))) + continue; kvspaceRef_t rr; - if (kvspaceResolveRef(k->h, pairs[i].key, &rr) == 0) - ref_put(k, pairs[i].key, &rr); + if (kvspaceResolveRef(k->h, key, &rr) == 0) { + if (!is_member) { + ref_put(k, key, &rr); + hot_put(k, rest, key, rr.block_id, rr.gen); + } + if (is_member || !k->fpar.key) { + size_t seplen = 0; + int si = last_dir_sep(key, &seplen); + if (si >= 0) + parent_put(k, key, (size_t)si + seplen, &rr); + } + } } } return rc; diff --git a/runtime/src/runtime_internal.h b/runtime/src/runtime_internal.h index 1f1cc67c..28f7727c 100644 --- a/runtime/src/runtime_internal.h +++ b/runtime/src/runtime_internal.h @@ -46,6 +46,8 @@ extern int kvspaceGet(void *h, const char *key, int resolve, uint8_t **out, typedef struct { uint32_t block_id; uint32_t gen; + uint32_t parent_id; + uint32_t depth; } kvspaceRef_t; extern int kvspaceResolveRef(void *h, const char *key, kvspaceRef_t *ref) __attribute__((weak)); @@ -143,15 +145,21 @@ typedef struct { } kvlangKvPair_t; #define KVLANG_REF_CAP 64 -typedef struct { - char *key; - uint32_t block_id, gen; -} kvlangRefEnt_t; +#define KVLANG_PREF_CAP 5 +#define KVLANG_HOT_CAP 4 +typedef struct { char *key; uint32_t block_id, gen, klen; } kvlangRefEnt_t; +typedef struct { char *name; char *key; uint32_t block_id, gen, dlen; } kvlangHotEnt_t; typedef struct { void *h; kvlangRefEnt_t ref[KVLANG_REF_CAP]; int nref; int ref_on; + kvlangRefEnt_t pref[KVLANG_PREF_CAP]; /* · map ART parents */ + int npref; + int pref_i; + kvlangRefEnt_t fpar; /* frame `/` parent for GetMember siblings */ + kvlangHotEnt_t hot[KVLANG_HOT_CAP]; /* a/i/n leaf refs */ + int nhot; } kvlangKv_t; /* growable string buffer */ diff --git a/runtime/tests/test_getmember_siblings.c b/runtime/tests/test_getmember_siblings.c new file mode 100644 index 00000000..d4be412d --- /dev/null +++ b/runtime/tests/test_getmember_siblings.c @@ -0,0 +1,133 @@ +#include "runtime_internal.h" +#include +#include +#include +#include + +static int failures; + +#define CHECK(c) \ + do { \ + if (!(c)) { \ + failures++; \ + fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #c); \ + } \ + } while (0) + +static int64_t xv_i64(const kvlangXvalue_t *v) { + kvspaceHead_t h; + const uint8_t *b; + int64_t n = 0; + int i; + if (!v || !v->data || v->len == 0) + return -999; + if (kvlangXvalueHead(v, &h) != 0 || h.body_len < 8) + return -999; + b = v->data + h.body_offset; + for (i = 0; i < 8; i++) + n |= (int64_t)b[i] << (8 * i); + return n; +} + +static int set_i64(kvlangKv_t *k, const char *key, int64_t n) { + kvlangXvalue_t v; + kvlangKvPair_t p; + char err[128]; + kvlangXvalueNewInt64(&v, n); + p.key = (char *)key; + p.val = v; + err[0] = 0; + if (kvlangKvSet(k, &p, 1, err, sizeof err) != 0) { + kvlangXvalueFree(&v); + return -1; + } + kvlangXvalueFree(&v); + return 0; +} + +/* Drop leaf/hot so GetMember cannot use per-key block_id. Keep fpar. */ +static void drop_leaf_hot(kvlangKv_t *k) { + int i; + for (i = 0; i < k->nref; i++) + free(k->ref[i].key); + k->nref = 0; + for (i = 0; i < k->nhot; i++) { + free(k->hot[i].name); + free(k->hot[i].key); + k->hot[i].name = k->hot[i].key = NULL; + } + k->nhot = 0; +} + +int main(void) { + char dir[] = "/tmp/kvs-gm-XXXXXX"; + char path[256], dsn[288]; + kvlangKv_t *k; + const char *frm = "/vthread/vt0/[0]/"; + static const char *names[] = {"a", "i", "n", "x", "y", "lo", "hi"}; + static const int64_t want[] = {11, 22, 33, 44, 55, 66, 77}; + const int n = 7; + int i; + kvlangXvalue_t out; + + if (!mkdtemp(dir)) + return 1; + snprintf(path, sizeof path, "%s/s", dir); + snprintf(dsn, sizeof dsn, "shm://%s", path); + + k = kvlangKvConnect(dsn); + if (!k) { + fprintf(stderr, "kvlangKvConnect failed dsn=%s\n", dsn); + return 1; + } + CHECK(k->ref_on); + + for (i = 0; i < n; i++) { + char key[128]; + snprintf(key, sizeof key, "%s%s", frm, names[i]); + CHECK(set_i64(k, key, want[i]) == 0); + } + CHECK(k->fpar.key != NULL); + CHECK(k->fpar.klen == (uint32_t)strlen(frm)); + CHECK(memcmp(k->fpar.key, frm, k->fpar.klen) == 0); + CHECK(k->fpar.block_id != 0); + CHECK(k->fpar.gen != 0); + + drop_leaf_hot(k); + CHECK(k->nref == 0); + CHECK(k->nhot == 0); + CHECK(k->fpar.block_id != 0); + + for (i = 0; i < n; i++) { + int64_t got; + CHECK(kvlangKvGetMember(k, frm, names[i], &out) == 0); + got = xv_i64(&out); + CHECK(got == want[i]); + CHECK(got != want[(i + 1) % n]); + kvlangKvReadReset(k); + } + + printf("GetMember siblings via cached ART parent: %d keys, distinct values\n", n); + printf("fpar block_id=%u depth=%u\n", k->fpar.block_id, k->fpar.gen); + + /* Set lo with leaf/hot empty; hi must stay distinct. */ + { + char lokey[128]; + snprintf(lokey, sizeof lokey, "%slo", frm); + CHECK(set_i64(k, lokey, 88) == 0); + CHECK(kvlangKvGetMember(k, frm, "lo", &out) == 0); + CHECK(xv_i64(&out) == 88); + kvlangKvReadReset(k); + CHECK(kvlangKvGetMember(k, frm, "hi", &out) == 0); + CHECK(xv_i64(&out) == 77); + kvlangKvReadReset(k); + printf("Set sibling via cached ART parent: lo=88 hi=77\n"); + } + + kvlangKvDisconnect(k); + if (failures) { + fprintf(stderr, "%d checks failed\n", failures); + return 1; + } + return 0; +} From 604fbbc4aebd3866d63b84e82d2ae8efbefcd1e2 Mon Sep 17 00:00:00 2001 From: AmuroEita <1071307515@qq.com> Date: Sun, 13 Sep 2026 21:40:12 +0800 Subject: [PATCH 3/3] =?UTF-8?q?runtime:=20ByRef=20=E9=9B=B6=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=20+=20=E7=88=B6=E7=BC=93=E5=AD=98=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=E6=8E=A2=E6=B5=8B=20+=20macOS=20weak=5Fimport?= =?UTF-8?q?=EF=BC=88#268=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ResolveRef 结果未初始化时 parent_id 是栈垃圾,GetByRef 越界 SIGSEGV。 嵌套 key 首次 ResolveRef 后 parent_id==0 则永久关闭父缓存(后端只写了叶子)。 Mach-O 用 weak_import + dynamic_lookup,避免旧 libkvspace 链接失败。 --- runtime/CMakeLists.txt | 33 ++++++++++++++++++--------------- runtime/src/kvspace.c | 32 +++++++++++++++++++++++++------- runtime/src/runtime_internal.h | 15 ++++++++++++--- 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 21208549..51bd0458 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -21,10 +21,11 @@ set(KVSPACE_LIB_DIR "/usr/lib/kvspace" CACHE PATH "kvspace dispatch 前端安装 target_link_directories(kvlang_runtime PRIVATE ${KVSPACE_LIB_DIR}) if(APPLE) - # macOS:数学/线程符号在 libSystem 内(无 -lm/-lpthread);ld64 无 --disable-new-dtags + # macOS:数学/线程符号在 libSystem 内(无 -lm/-lpthread);ld64 无 --disable-new-dtags。 + # weak_import 的 ResolveRef/GetByRef 在旧 libkvspace 上不存在,需 dynamic_lookup。 target_link_libraries(kvlang_runtime PRIVATE kvspace) set_target_properties(kvlang_runtime PROPERTIES - LINK_FLAGS "-Wl,-export_dynamic -Wl,-rpath,${KVSPACE_LIB_DIR}") + LINK_FLAGS "-Wl,-export_dynamic -Wl,-rpath,${KVSPACE_LIB_DIR} -Wl,-undefined,dynamic_lookup") else() target_link_libraries(kvlang_runtime PRIVATE kvspace m pthread) # --export-dynamic 供 kvlang(term 扩展)找符号;--disable-new-dtags 使 rpath 转 DT_RPATH(传递)。 @@ -32,16 +33,18 @@ else() LINK_FLAGS "-Wl,--export-dynamic -Wl,--disable-new-dtags -Wl,-rpath,${KVSPACE_LIB_DIR}") endif() -add_custom_command( - OUTPUT "${BIN_DIR}/test_getmember_siblings" - COMMAND ${CMAKE_C_COMPILER} -O2 -D_GNU_SOURCE - -I${CMAKE_SOURCE_DIR}/include -I${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/tests/test_getmember_siblings.c - -o ${BIN_DIR}/test_getmember_siblings - -L${BIN_DIR} - -Wl,--disable-new-dtags - -Wl,-rpath,${BIN_DIR} - -Wl,--no-as-needed -lkvspace -Wl,--as-needed -lkvlang_runtime - DEPENDS kvlang_runtime "${CMAKE_SOURCE_DIR}/tests/test_getmember_siblings.c" - VERBATIM) -add_custom_target(test_getmember_siblings ALL DEPENDS "${BIN_DIR}/test_getmember_siblings") +if(NOT APPLE) + add_custom_command( + OUTPUT "${BIN_DIR}/test_getmember_siblings" + COMMAND ${CMAKE_C_COMPILER} -O2 -D_GNU_SOURCE + -I${CMAKE_SOURCE_DIR}/include -I${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/tests/test_getmember_siblings.c + -o ${BIN_DIR}/test_getmember_siblings + -L${BIN_DIR} -L${KVSPACE_LIB_DIR} + -Wl,--disable-new-dtags + -Wl,-rpath,${BIN_DIR} -Wl,-rpath,${KVSPACE_LIB_DIR} + -Wl,--no-as-needed -lkvspace -Wl,--as-needed -lkvlang_runtime + DEPENDS kvlang_runtime "${CMAKE_SOURCE_DIR}/tests/test_getmember_siblings.c" + VERBATIM) + add_custom_target(test_getmember_siblings ALL DEPENDS "${BIN_DIR}/test_getmember_siblings") +endif() diff --git a/runtime/src/kvspace.c b/runtime/src/kvspace.c index 0bbae349..aa117007 100644 --- a/runtime/src/kvspace.c +++ b/runtime/src/kvspace.c @@ -25,6 +25,10 @@ static int ref_ok(kvlangKv_t *k) { return k->ref_on && kvspaceResolveRef && kvspaceGetByRef; } +static int parent_ok(kvlangKv_t *k) { + return ref_ok(k) && k->parent_on; +} + static kvlangRefEnt_t *pref_find(kvlangKv_t *k, const char *dir, size_t dl) { for (int i = 0; i < k->npref; i++) { const char *pk = k->pref[i].key; @@ -150,7 +154,7 @@ static int dir_is_member(const char *dir, size_t dl) { static void parent_put(kvlangKv_t *k, const char *dir, size_t dl, const kvspaceRef_t *rr) { kvlangRefEnt_t *e; - if (!dir || !dl || !rr->parent_id || !rr->depth) + if (!parent_ok(k) || !dir || !dl || !rr->parent_id || !rr->depth) return; if (dl >= 5 && memcmp(dir, "/lib/", 5) == 0) return; @@ -211,6 +215,18 @@ static int last_dir_sep(const char *key, size_t *seplen) { return -1; } +/* Nested key + parent_id==0 → backend only wrote leaf fields; disable parent cache. */ +static void parent_note(kvlangKv_t *k, const char *key, const kvspaceRef_t *rr) { + size_t seplen = 0; + if (k->parent_probed || !key) + return; + if (last_dir_sep(key, &seplen) < 0) + return; + k->parent_probed = 1; + if (!rr->parent_id) + k->parent_on = 0; +} + static int parent_prefix_ok(const kvlangRefEnt_t *e, const char *key) { size_t dl; const char *p; @@ -316,6 +332,7 @@ kvlangKv_t *kvlangKvConnect(const char *dsn) { return NULL; } k->ref_on = 1; + k->parent_on = 1; return k; } @@ -334,7 +351,7 @@ int kvlangKvGetOne(kvlangKv_t *k, const char *key, kvlangXvalue_t *out) { kvlangXvalueZero(out); uint8_t *d; uint32_t len; - if (ref_ok(k) && k->npref && key) { + if (parent_ok(k) && k->npref && key) { kvlangRefEnt_t *pe = (k->npref == 1) ? (parent_prefix_ok(&k->pref[0], key) ? &k->pref[0] : NULL) : pref_cover(k, key); @@ -383,7 +400,7 @@ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXv memcpy(key, dir, dl); memcpy(key + dl, name, nl); key[dl + nl] = 0; - if (ref_ok(k) && !hot_name_ok(name) && k->fpar.key && + if (parent_ok(k) && !hot_name_ok(name) && k->fpar.key && memcmp(k->fpar.key, dir, k->fpar.klen) == 0 && dir[k->fpar.klen] == 0) { kvspaceRef_t r = { k->fpar.block_id, k->fpar.gen, 0, 0 }; if (kvspaceGetByRef(k->h, &r, key, &d, &len) == 0 && d && len > 0) { @@ -410,7 +427,7 @@ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXv return 0; } } - if (ref_ok(k)) { + if (parent_ok(k)) { int hit = 0; if (nl >= MEMBER_SEP_LEN && memchr(name, 0xC2, nl)) { if (k->npref) @@ -527,16 +544,17 @@ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, ui const char *slash = strrchr(key, '/'); const char *rest = slash ? slash + 1 : key; int is_member = rest && memchr(rest, 0xC2, strlen(rest)) != NULL; - if (is_member && (pref_cover(k, key) || + if (is_member && (!parent_ok(k) || pref_cover(k, key) || (key[0] == '/' && strncmp(key, "/lib/", 5) == 0))) continue; - kvspaceRef_t rr; + kvspaceRef_t rr = {0}; if (kvspaceResolveRef(k->h, key, &rr) == 0) { + parent_note(k, key, &rr); if (!is_member) { ref_put(k, key, &rr); hot_put(k, rest, key, rr.block_id, rr.gen); } - if (is_member || !k->fpar.key) { + if (parent_ok(k) && (is_member || !k->fpar.key)) { size_t seplen = 0; int si = last_dir_sep(key, &seplen); if (si >= 0) diff --git a/runtime/src/runtime_internal.h b/runtime/src/runtime_internal.h index 28f7727c..600aced7 100644 --- a/runtime/src/runtime_internal.h +++ b/runtime/src/runtime_internal.h @@ -43,21 +43,28 @@ extern void kvspaceClose(void *h); extern int kvspaceGet(void *h, const char *key, int resolve, uint8_t **out, uint32_t *out_len); +/* 对齐 kvspace/include/kvspace/kvspace.h。parent_id/depth 由 ResolveRef 填; + * 后端只写前 8 字节时 parent_id 保持 0,runtime 永久关闭父缓存。 */ typedef struct { uint32_t block_id; uint32_t gen; uint32_t parent_id; uint32_t depth; } kvspaceRef_t; +#if defined(__APPLE__) +#define KVLANG_KVSPACE_WEAK __attribute__((weak_import)) +#else +#define KVLANG_KVSPACE_WEAK __attribute__((weak)) +#endif extern int kvspaceResolveRef(void *h, const char *key, kvspaceRef_t *ref) - __attribute__((weak)); + KVLANG_KVSPACE_WEAK; extern int kvspaceGetByRef(void *h, kvspaceRef_t *ref, const char *key_fallback, uint8_t **out, uint32_t *out_len) - __attribute__((weak)); + KVLANG_KVSPACE_WEAK; extern int kvspaceSetPartByRef(void *h, kvspaceRef_t *ref, const char *key_fallback, uint32_t offset, const uint8_t *buf, uint32_t buf_len, char *err, - uint32_t err_cap) __attribute__((weak)); + uint32_t err_cap) KVLANG_KVSPACE_WEAK; /* 指令边界回收读借用池;定位读/写(分片);只读 head 前缀。见 kvspace.h 契约。 */ extern void kvspaceReadReset(void *h); extern int kvspaceGetPart(void *h, const char *key, uint32_t offset, @@ -154,6 +161,8 @@ typedef struct { kvlangRefEnt_t ref[KVLANG_REF_CAP]; int nref; int ref_on; + int parent_on; /* 嵌套 ResolveRef 后 parent_id==0 则永久关闭 */ + int parent_probed; kvlangRefEnt_t pref[KVLANG_PREF_CAP]; /* · map ART parents */ int npref; int pref_i;