feat: implement kvmgr_capture_keys for lock-free checkpoint serialization (P0-14a)
Add kvtab_foreach walk primitive (P0-14) to kv.c/kv.h, then implement kvmgr_capture_keys in kvmgr.c that deep-copies all key→index mappings into a malloc'd slot_key_t[] array safe to read after the write lock is released during checkpoint serialization (Part 3.8). Key design points: - Both kvtab_foreach and kvmgr_capture_keys take var (RTTI data pointer) and use RTTI_DATA_TO_OBJ internally — the RTTI framework's makeInstance returns the data region pointer, not the meta_obj_t header pointer - String keys are copied via strncpy into slot_key_t.key[XCE_MAXNAME_LEN] - Integer keys are formatted as decimal strings via snprintf - Two-pass approach: count live entries first, allocate, then fill - Tests cover string kvmgr (3 entries), empty kvmgr, and deep-copy safety
This commit is contained in:
parent
4903c83f28
commit
eaf1801d6d
7 changed files with 361 additions and 0 deletions
108
src/lib/kvmgr.c
108
src/lib/kvmgr.c
|
|
@ -1,12 +1,15 @@
|
|||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <rtti.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "htab.h"
|
||||
#include "kv.h"
|
||||
#include "kvmgr.h"
|
||||
#include "mylock.h"
|
||||
#include "persist.h"
|
||||
#include "utils.h"
|
||||
#include "xce/types.h"
|
||||
#include "zarray.h"
|
||||
|
|
@ -302,3 +305,108 @@ typeinfo_t xkvmgr_type = {
|
|||
.lock = &(intf_lock_t) { .lock = (lock_fn_t)xkvmgr_lock,
|
||||
.unlock = (unlock_fn_t)xkvmgr_unlock } }
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* kvmgr_capture_keys — P0-14a */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
typedef struct {
|
||||
slot_key_t* keys;
|
||||
size_t capacity;
|
||||
size_t filled;
|
||||
kvmgr_key_type_t key_type;
|
||||
int oom;
|
||||
} capture_ctx_t;
|
||||
|
||||
static void capture_one(void* raw_key, int index, void* arg)
|
||||
{
|
||||
capture_ctx_t* ctx = (capture_ctx_t*)arg;
|
||||
|
||||
if (ctx->oom || ctx->filled >= ctx->capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
slot_key_t* sk = &ctx->keys[ctx->filled];
|
||||
sk->index = index;
|
||||
|
||||
if (ctx->key_type == KVMGR_KEY_STRING) {
|
||||
const char* src = (const char*)raw_key;
|
||||
strncpy(sk->key, src, XCE_MAXNAME_LEN - 1);
|
||||
sk->key[XCE_MAXNAME_LEN - 1] = '\0';
|
||||
sk->key_len = strlen(sk->key) + 1;
|
||||
} else {
|
||||
/* Integer key — format as decimal string */
|
||||
int64_t intval = (int64_t)(long)raw_key;
|
||||
int n = snprintf(sk->key, XCE_MAXNAME_LEN,
|
||||
"%" PRId64, intval);
|
||||
if (n < 0 || n >= XCE_MAXNAME_LEN) {
|
||||
/* Truncated — mark as oom so caller can detect */
|
||||
ctx->oom = 1;
|
||||
return;
|
||||
}
|
||||
sk->key_len = (size_t)n + 1;
|
||||
}
|
||||
|
||||
ctx->filled++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Count live entries by iterating; kvtab_foreach returns the count.
|
||||
* We use a NULL callback to count first, then allocate, then fill.
|
||||
*/
|
||||
static void count_one(void* key, int index, void* arg)
|
||||
{
|
||||
(void)key;
|
||||
(void)index;
|
||||
size_t* n = (size_t*)arg;
|
||||
(*n)++;
|
||||
}
|
||||
|
||||
int kvmgr_capture_keys(var kvmgr_var,
|
||||
slot_key_t** keys_out,
|
||||
size_t* n_keys_out)
|
||||
{
|
||||
*keys_out = NULL;
|
||||
*n_keys_out = 0;
|
||||
|
||||
if (kvmgr_var == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The public API pointer (var) points to the data region;
|
||||
* convert to the meta_obj_t header, then extract xkvmgr_t. */
|
||||
meta_obj_t* obj = RTTI_DATA_TO_OBJ(kvmgr_var);
|
||||
xkvmgr_t* kvm = RTTI_OBJ_TO_DATA(xkvmgr_t, obj);
|
||||
|
||||
/* Count live entries */
|
||||
size_t n = 0;
|
||||
kvtab_foreach(kvm->kvtab, count_one, &n);
|
||||
|
||||
if (n == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
slot_key_t* keys = calloc(n, sizeof(slot_key_t));
|
||||
if (keys == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
capture_ctx_t ctx = {
|
||||
.keys = keys,
|
||||
.capacity = n,
|
||||
.filled = 0,
|
||||
.key_type = kvm->key_type,
|
||||
.oom = 0,
|
||||
};
|
||||
|
||||
kvtab_foreach(kvm->kvtab, capture_one, &ctx);
|
||||
|
||||
if (ctx.oom) {
|
||||
free(keys);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*keys_out = keys;
|
||||
*n_keys_out = ctx.filled;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue