summaryrefslogtreecommitdiffstats
path: root/src/bftw.c
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2023-10-12 11:24:49 -0400
committerTavian Barnes <tavianator@tavianator.com>2023-10-12 11:39:08 -0400
commita029d95b5736a74879f32089514a5a6b63d6efbc (patch)
treecd555ca624296b5a700d04dbe9115a7e8a8402b3 /src/bftw.c
parented2a50d63b25a7b72a32be07a33331544f587296 (diff)
downloadbfs-a029d95b5736a74879f32089514a5a6b63d6efbc.tar.xz
bftw: Fix unbuffered depth-first searches
bftw() implements depth-first search by appending files to a batch, then prepending the batch to the queue. When we switched to separate file/ directory queues, this was only implemented for the file queue. Unbuffered searches don't use the file queue, so they were all breadth- first in practice. This meant that iterative deepening (-S ids) was actually "iterative deepening *breadth*-first search," a horrible strategy with no advantage over regular breadth-first search. Now it performs iterative deepening *depth*-first search, which at least limits its memory consumption. Fixes: c1b16b49988ecff17ae30978ea14798d95b80018
Diffstat (limited to 'src/bftw.c')
-rw-r--r--src/bftw.c56
1 files changed, 41 insertions, 15 deletions
diff --git a/src/bftw.c b/src/bftw.c
index c58001d..6eec42c 100644
--- a/src/bftw.c
+++ b/src/bftw.c
@@ -422,6 +422,8 @@ struct bftw_state {
/** The number of I/O threads. */
size_t nthreads;
+ /** A batch of directories to open. */
+ struct bftw_list dir_batch;
/** The queue of directories to open. */
struct bftw_list to_open;
/** The queue of directories to read. */
@@ -429,10 +431,10 @@ struct bftw_state {
/** The queue of unpinned directories to unwrap. */
struct bftw_list to_close;
+ /** A batch of files to enqueue. */
+ struct bftw_list file_batch;
/** The queue of files to visit. */
struct bftw_list to_visit;
- /** A batch of files to enqueue. */
- struct bftw_list batch;
/** The current path. */
dchar *path;
@@ -499,12 +501,14 @@ static int bftw_state_init(struct bftw_state *state, const struct bftw_args *arg
}
state->nthreads = nthreads;
+
+ SLIST_INIT(&state->dir_batch);
SLIST_INIT(&state->to_open);
SLIST_INIT(&state->to_read);
SLIST_INIT(&state->to_close);
+ SLIST_INIT(&state->file_batch);
SLIST_INIT(&state->to_visit);
- SLIST_INIT(&state->batch);
state->path = NULL;
state->file = NULL;
@@ -833,11 +837,32 @@ fail:
return -1;
}
+/** Open a batch of directories asynchronously. */
+static void bftw_ioq_opendirs(struct bftw_state *state, struct bftw_list *queue) {
+ for_slist (struct bftw_file, dir, queue) {
+ if (bftw_ioq_opendir(state, dir) != 0) {
+ break;
+ }
+ SLIST_POP(queue);
+ }
+}
+
/** Push a directory onto the queue. */
static void bftw_push_dir(struct bftw_state *state, struct bftw_file *file) {
bfs_assert(file->type == BFS_DIR);
- SLIST_APPEND(&state->to_open, file);
+ struct bftw_list *queue;
+ if (state->strategy == BFTW_BFS || (state->flags & BFTW_BUFFER)) {
+ // In breadth-first mode, or if we're already buffering files,
+ // we can push directly to the to_open queue
+ queue = &state->to_open;
+ } else {
+ // For a depth-first, unbuffered search, add directories to a
+ // batch, then push the patch to the front of the queue
+ queue = &state->dir_batch;
+ }
+
+ SLIST_APPEND(queue, file);
if (state->flags & BFTW_SORT) {
// When sorting, directories are kept in order on the to_read
@@ -845,12 +870,7 @@ static void bftw_push_dir(struct bftw_state *state, struct bftw_file *file) {
SLIST_APPEND(&state->to_read, file, to_read);
}
- for_slist (struct bftw_file, dir, &state->to_open) {
- if (bftw_ioq_opendir(state, dir) != 0) {
- break;
- }
- SLIST_POP(&state->to_open);
- }
+ bftw_ioq_opendirs(state, queue);
}
/** Pop a directory to read from the queue. */
@@ -1333,13 +1353,18 @@ static void bftw_list_sort(struct bftw_list *list) {
/** Finish adding a batch of files. */
static void bftw_batch_finish(struct bftw_state *state) {
if (state->flags & BFTW_SORT) {
- bftw_list_sort(&state->batch);
+ bftw_list_sort(&state->file_batch);
}
if (state->strategy != BFTW_BFS) {
- SLIST_EXTEND(&state->batch, &state->to_visit);
+ SLIST_EXTEND(&state->dir_batch, &state->to_open);
+ SLIST_EXTEND(&state->file_batch, &state->to_visit);
}
- SLIST_EXTEND(&state->to_visit, &state->batch);
+
+ SLIST_EXTEND(&state->to_open, &state->dir_batch);
+ SLIST_EXTEND(&state->to_visit, &state->file_batch);
+
+ bftw_ioq_opendirs(state, &state->to_open);
}
/** Close the current directory. */
@@ -1381,7 +1406,7 @@ static int bftw_visit(struct bftw_state *state, const char *name) {
file->type = state->de->type;
}
- SLIST_APPEND(&state->batch, file);
+ SLIST_APPEND(&state->file_batch, file);
return 0;
}
@@ -1429,7 +1454,8 @@ static int bftw_state_destroy(struct bftw_state *state) {
state->ioq = NULL;
}
- SLIST_EXTEND(&state->to_visit, &state->batch);
+ SLIST_EXTEND(&state->to_open, &state->dir_batch);
+ SLIST_EXTEND(&state->to_visit, &state->file_batch);
do {
bftw_gc(state, BFTW_VISIT_NONE);
} while (bftw_pop_dir(state) || bftw_pop_file(state));