Skip to content

Commit bb4fca5

Browse files
committed
fix(discover): resolve the cache directory once per walk, not per directory
cbm_resolve_cache_dir() returns a pointer to a static thread-local buffer. The cache prune called it for every directory the walk visited, so it rewrote a buffer other code was entitled to still be holding — an aliasing bug, not a cosmetic inefficiency. The Windows guards job caught it as two REDs: a daemon cold-storm regression and a following "index did not run" setup failure. Both are consistent with the cache path being rewritten underneath a live reader during indexing. Resolve once at the discovery entry point into a thread-local copy and compare against that, so the prune costs one resolver call per walk rather than one per directory, and holds no pointer into shared storage. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
1 parent 91f6223 commit bb4fca5

1 file changed

Lines changed: 16 additions & 2 deletions

File tree

‎src/discover/discover.c‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,19 @@ static bool is_safety_core_dir(const char *name) {
563563
}
564564

565565
/* Check if a directory entry should be skipped (hardcoded dirs + gitignore). */
566+
/* Snapshot of the cache directory for the current walk.
567+
*
568+
* cbm_resolve_cache_dir() returns a pointer to a static thread-local buffer, so
569+
* calling it once per directory — as an earlier version of this prune did —
570+
* rewrites a buffer other code may still be holding. Resolve once at the entry
571+
* point and compare against the copy. */
572+
static _Thread_local char g_walk_cache_dir[CBM_SZ_4K];
573+
574+
static void walk_cache_dir_snapshot(void) {
575+
const char *cache = cbm_workspace_cache_dir();
576+
snprintf(g_walk_cache_dir, sizeof(g_walk_cache_dir), "%s", cache ? cache : "");
577+
}
578+
566579
/* The cache directory holds every indexed project's graph database. When a custom
567580
* CBM_CACHE_DIR sits inside a repository — which happens in tests and is legal in
568581
* production — walking into it would pull other projects' databases into this
@@ -572,8 +585,8 @@ static bool is_safety_core_dir(const char *name) {
572585
* refusing any root containing the cache: refusing a whole root was too blunt,
573586
* and not walking the cache is what the concern actually asks for. */
574587
static bool dir_is_cache_tree(const char *abs_path) {
575-
const char *cache = cbm_workspace_cache_dir();
576-
if (!cache || !cache[0] || !abs_path || !abs_path[0]) {
588+
const char *cache = g_walk_cache_dir;
589+
if (!cache[0] || !abs_path || !abs_path[0]) {
577590
return false;
578591
}
579592
size_t n = strlen(cache);
@@ -1190,6 +1203,7 @@ static cbm_discover_status_t discover_impl(const char *repo_path, const cbm_disc
11901203
.collect_excluded = !count_only && excluded_out != NULL,
11911204
.collect_ignored = !count_only && ignored_out != NULL,
11921205
};
1206+
walk_cache_dir_snapshot();
11931207
walk_dir(repo_path, "", opts, gitignore, global_gi, cbmignore, &fl);
11941208

11951209
/* Cleanup */

0 commit comments

Comments
 (0)