Free the thread-local node-type cache to satisfy LeakSanitizer

cbm_kind_in_set builds a thread-local cache of calloc'd symbol bitsets (ks_cache) that was never freed, so LeakSanitizer (Linux x64) reported the per-worker-thread caches as leaks once the previously-empty grammars gained real node-type sets. Add cbm_kind_in_set_free_cache() and call it at worker-thread teardown (extract_worker, beside cbm_slab_destroy_thread) and main-thread exit (test runner, beside sqlite3_shutdown).
This commit is contained in:
Martin Vogel
2026-06-06 12:34:59 +02:00
parent 16034f21a8
commit d8d114747c
4 changed files with 28 additions and 1 deletions
+15
View File
@@ -399,6 +399,21 @@ bool cbm_kind_in_set(TSNode node, const char **types) {
return kind_in_set_strcmp(node, (const char *const *)types);
}
/* Free the calling thread's node-type bitset cache (the calloc'd `bits` arrays
* that cbm_kind_in_set builds lazily). The cache is thread-local, so each worker
* thread and the main thread must call this at teardown (worker exit / process
* exit) for LeakSanitizer to report no leak. Safe if no cache was ever built. */
void cbm_kind_in_set_free_cache(void) {
for (int i = 0; i < KS_SLOTS; i++) {
free(ks_cache[i].bits);
ks_cache[i].bits = NULL;
ks_cache[i].lang = NULL;
ks_cache[i].types = NULL;
ks_cache[i].nsyms = 0;
ks_cache[i].exact = false;
}
}
bool cbm_has_ancestor_kind(TSNode node, const char *kind, int max_depth) {
TSNode cur = node;
for (int i = 0; i < max_depth; i++) {
+4
View File
@@ -42,6 +42,10 @@ TSNode cbm_find_child_by_kind(TSNode parent, const char *kind);
// Check if node kind matches a set of types (NULL-terminated array of strings).
bool cbm_kind_in_set(TSNode node, const char **types);
// Free the calling thread's cbm_kind_in_set bitset cache (call at thread/process
// teardown so the thread-local cache is not reported as a leak).
void cbm_kind_in_set_free_cache(void);
// Check if node has an ancestor of the given kind, within max_depth levels.
bool cbm_has_ancestor_kind(TSNode node, const char *kind, int max_depth);
+2
View File
@@ -48,6 +48,7 @@ enum { PP_CSHARP_M_PREFIX_LEN = 2 };
#include "pipeline/pipeline_internal.h"
#include "pipeline/pass_lsp_cross.h" /* cbm_pxc_* helpers for fused cross-file LSP */
#include "pipeline/lsp_resolve.h"
#include "helpers.h" /* cbm_kind_in_set_free_cache — per-worker-thread cache teardown */
#include "pipeline/worker_pool.h"
#include "foundation/compat.h"
#include "foundation/compat_thread.h"
@@ -635,6 +636,7 @@ static void extract_worker(int worker_id, void *ctx_ptr) {
/* Final cleanup (parser already destroyed in loop, just slab state) */
cbm_slab_destroy_thread();
cbm_kind_in_set_free_cache(); /* free this worker thread's node-type bitset cache */
}
static void merge_pkg_entries(cbm_pipeline_ctx_t *ctx, cbm_pkg_entries_t *pkg_entries,
+7 -1
View File
@@ -80,6 +80,11 @@ extern void suite_incremental(void);
extern void suite_simhash(void);
extern void suite_stack_overflow(void);
/* Free the main thread's thread-local node-type bitset cache before exit so
* LeakSanitizer (Linux x64) doesn't report it. Worker threads free their own
* caches at thread teardown (pass_parallel.c). */
extern void cbm_kind_in_set_free_cache(void);
int main(void) {
printf("\n codebase-memory-mcp C test suite\n");
@@ -207,7 +212,8 @@ int main(void) {
RUN_SUITE(incremental);
/* Release sqlite3 internal caches so ASan doesn't report them as leaks */
/* Release process-lifetime caches so LeakSanitizer reports no leaks. */
cbm_kind_in_set_free_cache();
sqlite3_shutdown();
TEST_SUMMARY();
}