sonar

Scan files at memory bandwidth speed.
Log | Files | Refs

commit d9269bfe5f0a6e8c089176269eff2f9d75c9dd64
parent 6319df6d084a88159255796eaffd6a8d75c1b5eb
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Sun, 20 Sep 2026 07:41:32 -0300

walk: free the directories a stopped scan never reached

A worker frees the path it is handed, but a scan called off mid-volume leaves the
rest of the queue untouched, and those paths were owned by nobody once `manage`
dropped the queue. Cancelling a walk of /usr after 40 ms stranded 910
allocations, 17 KB; the whole volume would strand far more, and a UI that offers
a cancel button pays it every time.

`drop` is the discard half of the contract `manage` now states. It runs on the
thread that called `scan`, where the paths were cloned, so it frees them from the
allocator that made them.

Diffstat:
Mwalk/walk.odin | 11++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/walk/walk.odin b/walk/walk.odin @@ -80,7 +80,7 @@ scan :: proc(root: string, t: ^scan.Tree, cfg := Config{}) -> Error { } seed := []Dir{{index = first, path = strings.clone(root, allocator)}} - flow.manage(seed, states, walk_dir, hand_over) + flow.manage(seed, states, walk_dir, hand_over, drop) for &s in states { if s.err != nil { @@ -108,6 +108,15 @@ hand_over :: proc(d: Dir, ok: bool, w: ^Worker, queue: ^[dynamic]Dir) -> bool { return w.err == nil && !scan.cancelled(w.writer.tree) } +// A directory a stopped scan never reached. `walk_dir` frees what it is handed; this +// is the other half, since cancelling mid-volume otherwise strands a path per +// directory found but not yet walked. Runs on the thread that called `scan`, and +// paths are cloned from that context, so the two allocators are the same one. +@(private) +drop :: proc(d: Dir) { + delete(d.path, context.allocator) +} + // A directory waiting to be read, and the node already standing for it. @(private) Dir :: struct {