feat: implement xzarray COW freeze/thaw with pre-grown shadow array (P0-2)
- Add in_cow flag + shadow/shadow_caps fields to xzarray_private_t - Convert xzarray_item_t.inuse from bitfield to _Atomic _Bool; update all read/write sites to use atomic_load/store_explicit - Implement xzarray_freeze(): O(1) ptr swap, pre-grows active array 1.5x+16 to avoid realloc during COW, sets pos past shadow range - Implement xzarray_thaw(): merges shadow into active (shadow wins only for slots not updated during COW), supports optional retain_fn GC - xzarray_get_vec(): falls back to shadow during COW so reads remain valid between freeze and thaw - Add COW guard assertion in xzarray_clear() - Wire vfreeze/vthaw into xzarray_type interface - Tests: 5 new COW tests all green; 4 pre-existing failures unchanged
This commit is contained in:
parent
fe69dcbd7a
commit
4c591a11d3
2 changed files with 230 additions and 29 deletions
|
|
@ -8,10 +8,16 @@
|
|||
/* xzarray_type is defined in zarray.c */
|
||||
extern typeinfo_t xzarray_type;
|
||||
|
||||
/* Helper: create an xzarray instance */
|
||||
static meta_obj_t* make_test_xzarray(void)
|
||||
{
|
||||
xzarray_params_t params = { .delta = 4, .datakiller = NULL };
|
||||
return (meta_obj_t*)makeInstance(&xzarray_type, ¶ms);
|
||||
}
|
||||
|
||||
static void test_freeze_thaw_slots_exist(void)
|
||||
{
|
||||
/* After P0-0b, intf_varray_t has vfreeze and vthaw slots.
|
||||
* They are NULL until P0-2 wires the implementations. */
|
||||
/* After P0-2, vfreeze and vthaw must be wired (non-NULL). */
|
||||
const intf_varray_t* iface
|
||||
= (const intf_varray_t*)xzarray_type.interfaces.varray;
|
||||
|
||||
|
|
@ -21,15 +27,110 @@ static void test_freeze_thaw_slots_exist(void)
|
|||
(void)iface->vfreeze;
|
||||
(void)iface->vthaw;
|
||||
|
||||
/* Slots must be NULL until P0-2 wires the real implementations */
|
||||
TEST_ASSERT_NULL(iface->vfreeze);
|
||||
TEST_ASSERT_NULL(iface->vthaw);
|
||||
/* Slots must be non-NULL after P0-2 wires the real implementations */
|
||||
TEST_ASSERT_NOT_NULL(iface->vfreeze);
|
||||
TEST_ASSERT_NOT_NULL(iface->vthaw);
|
||||
|
||||
printf("test_freeze_thaw_slots_exist: PASS\n");
|
||||
}
|
||||
|
||||
static void test_freeze_creates_shadow(void)
|
||||
{
|
||||
meta_obj_t* arr = make_test_xzarray();
|
||||
TEST_ASSERT_NOT_NULL(arr);
|
||||
|
||||
/* Store 3 items */
|
||||
char* items[3] = { "alpha", "beta", "gamma" };
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int idx = vstore(arr, items[i]);
|
||||
TEST_ASSERT_TRUE(idx >= 0);
|
||||
}
|
||||
|
||||
/* Freeze should succeed */
|
||||
TEST_ASSERT_EQUAL_INT(0, vfreeze(arr));
|
||||
|
||||
/* After freeze, vget must still return stored values (COW mode active) */
|
||||
void* out = NULL;
|
||||
TEST_ASSERT_EQUAL_INT(0, vget(arr, 0, &out));
|
||||
TEST_ASSERT_NOT_NULL(out);
|
||||
|
||||
/* Thaw to clean up */
|
||||
vthaw(arr, NULL, NULL);
|
||||
destroy(arr);
|
||||
}
|
||||
|
||||
static void test_freeze_thaw_preserves_all_entries(void)
|
||||
{
|
||||
meta_obj_t* arr = make_test_xzarray();
|
||||
TEST_ASSERT_NOT_NULL(arr);
|
||||
|
||||
int N = 5;
|
||||
char* items[5] = { "a", "b", "c", "d", "e" };
|
||||
int idxs[5];
|
||||
for (int i = 0; i < N; i++) {
|
||||
idxs[i] = vstore(arr, items[i]);
|
||||
TEST_ASSERT_TRUE(idxs[i] >= 0);
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, vfreeze(arr));
|
||||
TEST_ASSERT_EQUAL_INT(0, vthaw(arr, NULL, NULL));
|
||||
|
||||
/* All 5 entries must be reachable after thaw */
|
||||
for (int i = 0; i < N; i++) {
|
||||
void* out = NULL;
|
||||
TEST_ASSERT_EQUAL_INT(0, vget(arr, idxs[i], &out));
|
||||
TEST_ASSERT_EQUAL_PTR(items[i], out);
|
||||
}
|
||||
|
||||
destroy(arr);
|
||||
}
|
||||
|
||||
static void test_mutation_during_cow_visible_after_thaw(void)
|
||||
{
|
||||
meta_obj_t* arr = make_test_xzarray();
|
||||
TEST_ASSERT_NOT_NULL(arr);
|
||||
|
||||
char* original = "original";
|
||||
char* updated = "updated";
|
||||
|
||||
int idx = vstore(arr, original);
|
||||
TEST_ASSERT_TRUE(idx >= 0);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, vfreeze(arr));
|
||||
|
||||
/* Overwrite index during COW — write to new active array */
|
||||
int put_idxs[1] = { idx };
|
||||
void* put_data[1] = { updated };
|
||||
vput_vec(arr, 1, put_idxs, put_data);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, vthaw(arr, NULL, NULL));
|
||||
|
||||
/* After thaw, the updated value must win over the shadow value */
|
||||
void* out = NULL;
|
||||
TEST_ASSERT_EQUAL_INT(0, vget(arr, idx, &out));
|
||||
TEST_ASSERT_EQUAL_PTR(updated, out);
|
||||
|
||||
destroy(arr);
|
||||
}
|
||||
|
||||
static void test_double_freeze_fails(void)
|
||||
{
|
||||
meta_obj_t* arr = make_test_xzarray();
|
||||
TEST_ASSERT_NOT_NULL(arr);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, vfreeze(arr));
|
||||
/* Second freeze must fail (already in COW mode) */
|
||||
TEST_ASSERT_TRUE(vfreeze(arr) != 0);
|
||||
vthaw(arr, NULL, NULL);
|
||||
destroy(arr);
|
||||
}
|
||||
|
||||
void test_zarray_cow(void)
|
||||
{
|
||||
printf("\n>> zarray COW interface slot test\n\n");
|
||||
RUN_TEST(test_freeze_thaw_slots_exist);
|
||||
RUN_TEST(test_freeze_creates_shadow);
|
||||
RUN_TEST(test_freeze_thaw_preserves_all_entries);
|
||||
RUN_TEST(test_mutation_during_cow_visible_after_thaw);
|
||||
RUN_TEST(test_double_freeze_fails);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue