fix(mcp): free the extraction bitset cache on the in-process auto-index thread (LSan)

cbm_kind_in_set lazily builds a THREAD-LOCAL node-type bitset cache during
extraction. That cache must be freed before a worker thread exits, or the
calloc'd bitsets are orphaned when the thread's TLS is torn down and
LeakSanitizer reports them at process exit. Parallel workers already free it
(pass_parallel.c:993) and the main thread frees it at process teardown; the
IN-PROCESS sequential auto-index runs extraction on autoindex_thread — a
short-lived pthread joined by cbm_mcp_server_free — and was the one gap: it
built the cache but never freed it (360 bytes in 9 allocations leaked, all from
ks_build).

This is a PRE-EXISTING latent leak: the extraction path and autoindex_thread are
byte-identical to main. It is newly EXPOSED by #713's new
autoindex_limit_admits_non_git_root_under_limit test, the first LSan-observed
path that runs an in-process auto-index and joins the worker thread in-process —
so the fix ships with the test that exposes it.

Free the calling thread's bitset cache after cbm_pipeline_free(p), mirroring the
established per-worker-thread teardown. Zero behavior change to the CLI/main
path. macOS LSan lane, mcp suite: 360 B / 9 allocations -> 0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-09-09 07:33:51 +02:00
co-authored by Claude Fable 5.1
parent 7e73b48ef1
commit e45d705105
+11
View File
@@ -17383,6 +17383,16 @@ static void register_watcher_if_enabled(cbm_mcp_server_t *srv) {
}
/* Background auto-index thread function */
/* Extraction builds a THREAD-LOCAL node-type bitset cache (cbm_kind_in_set).
* Every worker thread that runs extraction must free that cache before it exits,
* or the calloc'd bitsets are orphaned when the thread's TLS is torn down and
* LeakSanitizer reports them at process exit. Parallel workers do this in
* pass_parallel.c; the in-process (sequential) auto-index runs extraction on
* THIS short-lived thread, so it must free its own cache too. Declared extern
* (not via internal/cbm/helpers.h) to avoid pulling the extraction layer's
* header into the MCP TU — the same pattern test_main.c uses for teardown. */
extern void cbm_kind_in_set_free_cache(void);
static void *autoindex_thread(void *arg) {
cbm_mcp_server_t *srv = (cbm_mcp_server_t *)arg;
@@ -17422,6 +17432,7 @@ static void *autoindex_thread(void *arg) {
cbm_pipeline_unlock();
cbm_pipeline_free(p);
cbm_kind_in_set_free_cache(); /* free THIS thread's extraction bitset cache (see above) */
cbm_mem_collect(); /* return mimalloc pages to OS after indexing (in-process only) */
if (rc == 0) {