From d02aae16ce62ef267db72bcfb2feb93eb2b4b6d3 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Thu, 22 Oct 2026 08:31:06 -0600 Subject: [PATCH 1/2] benchmarks: clear heap roots after batch cleanup Signed-off-by: Eduardo Silva --- benchmarks/records.c | 1 + tests/CMakeLists.txt | 1 + tests/benchmark_records.c | 63 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 tests/benchmark_records.c diff --git a/benchmarks/records.c b/benchmarks/records.c index 4c2cd85..f4e4e4d 100644 --- a/benchmarks/records.c +++ b/benchmarks/records.c @@ -100,6 +100,7 @@ static int run_batch(struct cfl_arena *arena, struct cfl_variant **roots, for (index = 0; index < options->records; index++) { if (roots[index] != NULL) { cfl_variant_destroy(roots[index]); + roots[index] = NULL; } } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index dd18ca0..2dba055 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,6 +12,7 @@ set(UNIT_TESTS_FILES list.c variant.c arena.c + benchmark_records.c object.c version.c utils.c diff --git a/tests/benchmark_records.c b/tests/benchmark_records.c new file mode 100644 index 0000000..346b149 --- /dev/null +++ b/tests/benchmark_records.c @@ -0,0 +1,63 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include + +static size_t record_count; +static size_t fail_at = SIZE_MAX; + +static struct cfl_variant *create_record_root(struct cfl_arena *arena, + struct cfl_kvlist *record) +{ + if (record_count++ == fail_at) { + /* Simulate a failed record without leaking its completed contents. */ + cfl_kvlist_destroy(record); + return NULL; + } + + return cfl_variant_create_from_kvlist_in(arena, record); +} + +#define cfl_variant_create_from_kvlist_in create_record_root +#define main benchmark_records_main +#include "../benchmarks/records.c" +#undef main +#undef cfl_variant_create_from_kvlist_in + +#include "cfl_tests_internal.h" + +static void test_heap_batch_failure(void) +{ + struct cfl_variant *roots[3] = {NULL, NULL, NULL}; + struct batch_options options; + size_t failure_index; + size_t index; + int result; + + options.records = 3; + options.referenced = CFL_FALSE; + options.mutate = CFL_TRUE; + + for (failure_index = 0; failure_index < options.records; failure_index++) { + record_count = 0; + fail_at = SIZE_MAX; + result = run_batch(NULL, roots, &options); + TEST_CHECK(result == 0); + if (result != 0) { + return; + } + + record_count = 0; + fail_at = failure_index; + result = run_batch(NULL, roots, &options); + TEST_CHECK(result == -1); + TEST_CHECK(record_count == failure_index + 1); + for (index = 0; index < options.records; index++) { + TEST_CHECK(roots[index] == NULL); + } + } +} + +TEST_LIST = { + {"heap_batch_failure", test_heap_batch_failure}, + {0} +}; From 270c8b1c3ec6c13a76b7dd075da2dc47906a9df3 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Thu, 22 Oct 2026 08:31:06 -0600 Subject: [PATCH 2/2] docs: clarify owned SDS large-object threshold Signed-off-by: Eduardo Silva --- ARENA.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ARENA.md b/ARENA.md index b41cdf2..085a276 100644 --- a/ARENA.md +++ b/ARENA.md @@ -245,8 +245,10 @@ allocation. Raw allocations and small CFL objects come from arena chunks. Raw requests larger than the active chunk receive a dedicated chunk. The large-object -threshold applies to arena-aware owned SDS values, which use separately tracked -external storage so unusually large strings do not consume normal chunks. +threshold applies only to arena-aware owned SDS payloads larger than 1 KiB, +which can use separately tracked external storage so unusually large strings +do not consume normal chunks. Payloads up to and including 1 KiB use the pooled +size classes regardless of the configured threshold. Reusable external buffers may remain cached after reset. Configure the maximum cached capacity with: