From 6301c078c80f9de559cc86b406d63811d47a0c78 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Thu, 2 Oct 2025 17:43:12 +0200 Subject: [PATCH] fix: move file type guessing out of project_manager thread Performing hundreds of thousands of file type guessing operations can block the project manager for seconds leading to slow exits. With this change we move the file type guessing into the tree walker thread leaving the project manager to respond to other requests including shutdown messages. --- src/Project.zig | 36 +++++++++++++++++++++++++++++------- src/project_manager.zig | 7 ++----- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/Project.zig b/src/Project.zig index 5496358..ce09226 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -421,22 +421,44 @@ pub fn query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []co return @min(max, matches.items.len); } -pub fn walk_tree_entry( - self: *Self, - file_path: []const u8, - mtime: i128, -) OutOfMemoryError!void { +fn walk_tree_entry_callback(parent: tp.pid_ref, root_path: []const u8, file_path: []const u8, mtime_high: i64, mtime_low: i64) error{Exit}!void { const file_type: []const u8, const file_icon: []const u8, const file_color: u24 = guess_file_type(file_path); + try parent.send(.{ "walk_tree_entry", root_path, file_path, mtime_high, mtime_low, file_type, file_icon, file_color }); +} + +pub fn walk_tree_entry(self: *Self, m: tp.message) OutOfMemoryError!void { + var file_path: []const u8 = undefined; + var mtime_high: i64 = 0; + var mtime_low: i64 = 0; + var file_type: []const u8 = undefined; + var file_icon: []const u8 = undefined; + var file_color: u32 = 0; + if (!(cbor.match(m.buf, .{ + tp.string, + tp.string, + tp.extract(&file_path), + tp.extract(&mtime_high), + tp.extract(&mtime_low), + tp.extract(&file_type), + tp.extract(&file_icon), + tp.extract(&file_color), + }) catch return)) return; + const mtime = (@as(i128, @intCast(mtime_high)) << 64) | @as(i128, @intCast(mtime_low)); + self.longest_file_path = @max(self.longest_file_path, file_path.len); (try self.pending.addOne(self.allocator)).* = .{ .path = try self.allocator.dupe(u8, file_path), .type = file_type, .icon = file_icon, - .color = file_color, + .color = @intCast(file_color), .mtime = mtime, }; } +fn walk_tree_done_callback(parent: tp.pid_ref, root_path: []const u8) error{Exit}!void { + try parent.send(.{ "walk_tree_done", root_path }); +} + pub fn walk_tree_done(self: *Self, parent: tp.pid_ref) OutOfMemoryError!void { self.state.walk_tree = .done; if (self.walker) |pid| pid.deinit(); @@ -1987,7 +2009,7 @@ pub fn query_git(self: *Self) void { fn start_walker(self: *Self) void { self.state.walk_tree = .running; - self.walker = walk_tree.start(self.allocator, self.name) catch blk: { + self.walker = walk_tree.start(self.allocator, self.name, walk_tree_entry_callback, walk_tree_done_callback) catch blk: { self.state.walk_tree = .failed; break :blk null; }; diff --git a/src/project_manager.zig b/src/project_manager.zig index 0dbf440..1f15b1a 100644 --- a/src/project_manager.zig +++ b/src/project_manager.zig @@ -318,8 +318,6 @@ const Process = struct { var method: []const u8 = undefined; var cbor_id: []const u8 = undefined; var params_cb: []const u8 = undefined; - var high: i64 = 0; - var low: i64 = 0; var max: usize = 0; var row: usize = 0; var col: usize = 0; @@ -338,10 +336,9 @@ const Process = struct { var eol_mode: Buffer.EolModeTag = @intFromEnum(Buffer.EolMode.lf); - if (try cbor.match(m.buf, .{ "walk_tree_entry", tp.extract(&project_directory), tp.extract(&path), tp.extract(&high), tp.extract(&low) })) { - const mtime = (@as(i128, @intCast(high)) << 64) | @as(i128, @intCast(low)); + if (try cbor.match(m.buf, .{ "walk_tree_entry", tp.extract(&project_directory), tp.more })) { if (self.projects.get(project_directory)) |project| - project.walk_tree_entry(path, mtime) catch |e| self.logger.err("walk_tree_entry", e); + project.walk_tree_entry(m) catch |e| self.logger.err("walk_tree_entry", e); } else if (try cbor.match(m.buf, .{ "walk_tree_done", tp.extract(&project_directory) })) { if (self.projects.get(project_directory)) |project| project.walk_tree_done(self.parent.ref()) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;