refactor: arean allocate temporaries in kqueue backend

This commit is contained in:
CJ van den Berg 2026-03-08 21:24:33 +01:00
parent 0548953460
commit 3ba8b2bac5
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
2 changed files with 21 additions and 29 deletions

View file

@ -795,43 +795,35 @@ const KQueueBackend = struct {
var dir = std.fs.openDirAbsolute(dir_path, .{ .iterate = true }) catch return;
defer dir.close();
// Arena for all temporaries freed in one shot at the end.
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
const tmp = arena.allocator();
// Collect current files and subdirectories (no lock, reading filesystem only).
var current_files: std.StringHashMapUnmanaged(void) = .empty;
defer {
var it = current_files.iterator();
while (it.next()) |e| allocator.free(e.key_ptr.*);
current_files.deinit(allocator);
}
var current_dirs: std.ArrayListUnmanaged([]u8) = .empty;
defer {
for (current_dirs.items) |d| allocator.free(d);
current_dirs.deinit(allocator);
}
var iter = dir.iterate();
while (try iter.next()) |entry| {
switch (entry.kind) {
.file => {
const name = try allocator.dupe(u8, entry.name);
try current_files.put(allocator, name, {});
const name = try tmp.dupe(u8, entry.name);
try current_files.put(tmp, name, {});
},
.directory => {
const name = try allocator.dupe(u8, entry.name);
try current_dirs.append(allocator, name);
const name = try tmp.dupe(u8, entry.name);
try current_dirs.append(tmp, name);
},
else => {},
}
}
// Diff against snapshot under the lock; collect events to emit after releasing it.
// to_create / to_delete hold borrowed pointers into the snapshot (which uses
// allocator, not tmp); only the list metadata itself uses tmp.
var to_create: std.ArrayListUnmanaged([]const u8) = .empty;
defer to_create.deinit(allocator);
var to_delete: std.ArrayListUnmanaged([]const u8) = .empty;
defer to_delete.deinit(allocator);
var new_dirs: std.ArrayListUnmanaged([]u8) = .empty;
defer {
for (new_dirs.items) |p| allocator.free(p);
new_dirs.deinit(allocator);
}
var new_dirs: std.ArrayListUnmanaged([]const u8) = .empty;
self.snapshots_mutex.lock();
{
@ -839,11 +831,8 @@ const KQueueBackend = struct {
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
const full_path = std.fmt.bufPrint(&path_buf, "{s}/{s}", .{ dir_path, name }) catch continue;
if (!self.snapshots.contains(full_path)) {
const owned = allocator.dupe(u8, full_path) catch continue;
new_dirs.append(allocator, owned) catch {
allocator.free(owned);
continue;
};
const owned = tmp.dupe(u8, full_path) catch continue;
new_dirs.append(tmp, owned) catch continue;
}
}
@ -866,13 +855,13 @@ const KQueueBackend = struct {
self.snapshots_mutex.unlock();
return e;
};
try to_create.append(allocator, owned);
try to_create.append(tmp, owned);
}
var sit = snapshot.iterator();
while (sit.next()) |entry| {
if (current_files.contains(entry.key_ptr.*)) continue;
try to_delete.append(allocator, entry.key_ptr.*);
try to_delete.append(tmp, entry.key_ptr.*);
}
for (to_delete.items) |name| _ = snapshot.fetchRemove(name);
}
@ -891,7 +880,7 @@ const KQueueBackend = struct {
};
self.deregister_file_watch(allocator, full_path);
try self.handler.change(full_path, EventType.deleted, .file);
allocator.free(name);
allocator.free(name); // snapshot key, owned by allocator
}
for (to_create.items) |name| {
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
@ -899,6 +888,7 @@ const KQueueBackend = struct {
self.register_file_watch(allocator, full_path);
try self.handler.change(full_path, EventType.created, .file);
}
// arena.deinit() frees current_files, current_dirs, new_dirs, and list metadata
}
fn register_file_watch(self: *@This(), allocator: std.mem.Allocator, path: []const u8) void {

View file

@ -274,11 +274,13 @@ test "deleting a file emits a 'deleted' event" {
defer watcher.deinit();
try watcher.watch(tmp);
// Create the file after the watcher is active so the backend can cache its type.
// Create the file after the watcher is active so the backend can cache its type,
// then drain before deleting so the file lands in any snapshot before it disappears.
{
const f = try std.fs.createFileAbsolute(file_path, .{});
f.close();
}
try drainEvents(&watcher);
try std.fs.deleteFileAbsolute(file_path);