fix(kqueue): avoid more potential leaks

This commit is contained in:
CJ van den Berg 2026-03-14 19:32:16 +01:00
parent 5191340c0b
commit f4821d79e8
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
2 changed files with 12 additions and 2 deletions

View file

@ -184,6 +184,11 @@ fn scan_dir(self: *@This(), allocator: std.mem.Allocator, dir_path: []const u8)
var to_create: std.ArrayListUnmanaged([]const u8) = .empty; var to_create: std.ArrayListUnmanaged([]const u8) = .empty;
var to_delete: std.ArrayListUnmanaged([]const u8) = .empty; var to_delete: std.ArrayListUnmanaged([]const u8) = .empty;
var new_dirs: std.ArrayListUnmanaged([]const u8) = .empty; var new_dirs: std.ArrayListUnmanaged([]const u8) = .empty;
// to_delete items are owned (removed from snapshot) only after the fetchRemove
// loop inside the mutex block. Track this so the defer below only frees them
// once ownership has actually been transferred.
var to_delete_owned = false;
defer if (to_delete_owned) for (to_delete.items) |name| allocator.free(name);
self.snapshots_mutex.lock(); self.snapshots_mutex.lock();
errdefer self.snapshots_mutex.unlock(); errdefer self.snapshots_mutex.unlock();
@ -222,6 +227,7 @@ fn scan_dir(self: *@This(), allocator: std.mem.Allocator, dir_path: []const u8)
try to_delete.append(tmp, entry.key_ptr.*); try to_delete.append(tmp, entry.key_ptr.*);
} }
for (to_delete.items) |name| _ = snapshot.fetchRemove(name); for (to_delete.items) |name| _ = snapshot.fetchRemove(name);
to_delete_owned = true; // ownership transferred; defer will free all items
} }
self.snapshots_mutex.unlock(); self.snapshots_mutex.unlock();
@ -231,7 +237,6 @@ fn scan_dir(self: *@This(), allocator: std.mem.Allocator, dir_path: []const u8)
for (new_dirs.items) |full_path| for (new_dirs.items) |full_path|
try self.handler.change(full_path, EventType.created, .dir); try self.handler.change(full_path, EventType.created, .dir);
for (to_delete.items) |name| { for (to_delete.items) |name| {
defer allocator.free(name); // snapshot key, owned by allocator
var path_buf: [std.fs.max_path_bytes]u8 = undefined; 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; const full_path = std.fmt.bufPrint(&path_buf, "{s}/{s}", .{ dir_path, name }) catch continue;
self.deregister_file_watch(allocator, full_path); self.deregister_file_watch(allocator, full_path);

View file

@ -178,6 +178,11 @@ fn scan_dir(self: *@This(), allocator: std.mem.Allocator, dir_path: []const u8)
var to_delete: std.ArrayListUnmanaged([]const u8) = .empty; var to_delete: std.ArrayListUnmanaged([]const u8) = .empty;
var to_modify: std.ArrayListUnmanaged([]const u8) = .empty; var to_modify: std.ArrayListUnmanaged([]const u8) = .empty;
var new_dirs: std.ArrayListUnmanaged([]const u8) = .empty; var new_dirs: std.ArrayListUnmanaged([]const u8) = .empty;
// to_delete items are owned (removed from snapshot) only after the fetchRemove
// loop inside the mutex block. Track this so the defer below only frees them
// once ownership has actually been transferred.
var to_delete_owned = false;
defer if (to_delete_owned) for (to_delete.items) |name| allocator.free(name);
self.snapshots_mutex.lock(); self.snapshots_mutex.lock();
errdefer self.snapshots_mutex.unlock(); errdefer self.snapshots_mutex.unlock();
@ -224,6 +229,7 @@ fn scan_dir(self: *@This(), allocator: std.mem.Allocator, dir_path: []const u8)
try to_delete.append(tmp, entry.key_ptr.*); // borrow from snapshot try to_delete.append(tmp, entry.key_ptr.*); // borrow from snapshot
} }
for (to_delete.items) |name| _ = snapshot.fetchRemove(name); for (to_delete.items) |name| _ = snapshot.fetchRemove(name);
to_delete_owned = true; // ownership transferred; defer will free all items
} }
self.snapshots_mutex.unlock(); self.snapshots_mutex.unlock();
@ -232,7 +238,6 @@ fn scan_dir(self: *@This(), allocator: std.mem.Allocator, dir_path: []const u8)
for (new_dirs.items) |full_path| for (new_dirs.items) |full_path|
try self.handler.change(full_path, EventType.created, .dir); try self.handler.change(full_path, EventType.created, .dir);
for (to_delete.items) |name| { for (to_delete.items) |name| {
defer allocator.free(name); // snapshot key, owned by allocator
var path_buf: [std.fs.max_path_bytes]u8 = undefined; 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; const full_path = std.fmt.bufPrint(&path_buf, "{s}/{s}", .{ dir_path, name }) catch continue;
try self.handler.change(full_path, EventType.deleted, .file); try self.handler.change(full_path, EventType.deleted, .file);