diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2023-10-12 15:13:02 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2023-10-12 15:13:02 -0400 |
commit | 773f4a446f03da62d88e6d17be49fdc0a3e38465 (patch) | |
tree | 4f2520957b0ead54e9282bf3ad4f0a444d205ba1 /src | |
parent | 1addab1e5f12cb0fddfa92872bf45653352cc212 (diff) | |
download | bfs-773f4a446f03da62d88e6d17be49fdc0a3e38465.tar.xz |
bftw: Fix to_close list corruption with !BFS_USE_UNWRAPDIR
It's possible for pincount to drop to zero, then get incremented and
drop back to zero again. If this happens, we shouldn't add it to the
to_close list twice.
This should fix the intermittent hang on the macOS CI.
Fixes: 815798e1eea7fc8dacd5acab40202ec4d251d517
Diffstat (limited to 'src')
-rw-r--r-- | src/bftw.c | 19 |
1 files changed, 13 insertions, 6 deletions
@@ -546,6 +546,17 @@ static int bftw_state_init(struct bftw_state *state, const struct bftw_args *arg return 0; } +/** Unpin a directory, and possibly queue it for unwrapping. */ +static void bftw_unpin_dir(struct bftw_state *state, struct bftw_file *file, bool force) { + bftw_cache_unpin(&state->cache, file); + + if (file->dir && (force || file->pincount == 0)) { + if (!SLIST_ATTACHED(&state->to_close, file)) { + SLIST_APPEND(&state->to_close, file); + } + } +} + /** Pop a response from the I/O queue. */ static int bftw_ioq_pop(struct bftw_state *state, bool block) { struct ioq *ioq = state->ioq; @@ -582,10 +593,7 @@ static int bftw_ioq_pop(struct bftw_state *state, bool block) { ++cache->capacity; parent = file->parent; if (parent) { - bftw_cache_unpin(cache, parent); - if (parent->pincount == 0 && parent->dir) { - SLIST_APPEND(&state->to_close, parent); - } + bftw_unpin_dir(state, parent, false); } dir = ent->opendir.dir; @@ -1285,8 +1293,7 @@ static int bftw_gc(struct bftw_state *state, enum bftw_gc_flags flags) { struct bftw_file *file = state->file; if (file && file->dir) { - bftw_cache_unpin(&state->cache, file); - SLIST_APPEND(&state->to_close, file); + bftw_unpin_dir(state, file, true); } state->dir = NULL; state->de = NULL; |