authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-08-12 22:42:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-08-13 11:32:46+02:00
log99bfd07854b4e14e01e32e83302707cacde15cb5
treeff995937616902f03c0aa5517f592bef134ba8d7
parent470f77600d79a5c17b3734f97eaf2462c452718e

Maker: deinit Watch when exiting scope

this wasn't really needed before but now it's pretty important since configuration needs to be rerun sometimes

2 files changed, 76 insertions(+), 18 deletions(-)

lib/compiler/Maker.zig+16-14
......@@ -225,7 +225,7 @@ pub fn main(init: process.Init.Minimal) !void {
225225 var skip_oom_steps = false;
226226 var test_timeout_ns: ?u64 = null;
227227 var color: Color = .settingFromEnvironment(&graph.environ_map);
228 var watch = false;
228 var watch_flag = false;
229229 var fuzz: ?Fuzz.Mode = null;
230230 var debounce_interval_ms: u16 = 50;
231231 var listen: bool = false;
......@@ -474,7 +474,7 @@ pub fn main(init: process.Init.Minimal) !void {
474474 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
475475 graph.verbose_llvm_ir = true;
476476 } else if (mem.eql(u8, arg, "--watch")) {
477 watch = true;
477 watch_flag = true;
478478 } else if (mem.eql(u8, arg, "--time-report")) {
479479 graph.time_report = true;
480480 if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) };
......@@ -574,7 +574,7 @@ pub fn main(init: process.Init.Minimal) !void {
574574 }
575575
576576 const early_exit_mode = fetch_only or help_menu or steps_menu or print_configuration != .none;
577 const server_mode = !early_exit_mode and (watch or webui_listen != null or fuzz != null or listen);
577 const server_mode = !early_exit_mode and (watch_flag or webui_listen != null or fuzz != null or listen);
578578
579579 process.raiseFileDescriptorLimit();
580580
......@@ -701,7 +701,7 @@ pub fn main(init: process.Init.Minimal) !void {
701701 var protocol_server_allocation: AvoidableServer = undefined;
702702 const protocol_server: ?*AvoidableServer = if (listen) s: {
703703 if (builtin.single_threaded) fatal("--listen is not yet supported on single-threaded hosts", .{});
704 if (watch) fatal("using '--watch' and '--listen' together is not supported", .{});
704 if (watch_flag) fatal("using '--watch' and '--listen' together is not supported", .{});
705705 if (fuzz != null) fatal("using '--fuzz' and '--listen' together is not supported", .{});
706706 if (step_names.items.len > 0) fatal("build steps must be provided over the protocol instead of using CLI arguments", .{});
707707 protocol_server_allocation = .{
......@@ -788,7 +788,7 @@ pub fn main(init: process.Init.Minimal) !void {
788788 .skip_oom_steps = skip_oom_steps,
789789 .unit_test_timeout_ns = test_timeout_ns,
790790
791 .watch = watch,
791 .watch = watch_flag,
792792 .web_server = web_server,
793793 .protocol_server = protocol_server,
794794 .protocol_server_mutex = .init,
......@@ -801,7 +801,7 @@ pub fn main(init: process.Init.Minimal) !void {
801801 .multiline_errors = multiline_errors,
802802 .summary = summary orelse if (listen)
803803 .none
804 else if (watch or webui_listen != null)
804 else if (watch_flag or webui_listen != null)
805805 .new
806806 else
807807 .failures,
......@@ -820,7 +820,8 @@ pub fn main(init: process.Init.Minimal) !void {
820820 if (protocol_server) |s| {
821821 try s.serveStringMessage(.bsp_configuration, try arena.print("{f}", .{scanned_config.path}));
822822
823 var w: ?Watch = null;
823 var watch: ?Watch = null;
824 defer if (watch) |*w| w.deinit();
824825
825826 const Event = union(enum) {
826827 message: Reader.Error!Client.Message.Header,
......@@ -869,11 +870,11 @@ pub fn main(init: process.Init.Minimal) !void {
869870
870871 if (body.flags.watch) {
871872 if (!Watch.have_impl) unreachable;
872 if (w == null) w = try .init(&maker);
873 if (watch == null) watch = try .init(&maker);
873874
874 try updateWatch(&maker, &w.?);
875 try updateWatch(&maker, &watch.?);
875876 try select.concurrent(.fs_event, Watch.wait, .{
876 &w.?,
877 &watch.?,
877878 if (in_debounce) .{ .ms = debounce_interval_ms } else .none,
878879 });
879880 }
......@@ -902,7 +903,7 @@ pub fn main(init: process.Init.Minimal) !void {
902903 .clean => {},
903904 }
904905 try select.concurrent(.fs_event, Watch.wait, .{
905 &w.?,
906 &watch.?,
906907 if (in_debounce) .{ .ms = debounce_interval_ms } else .none,
907908 });
908909 continue :loop try select.await();
......@@ -924,10 +925,11 @@ pub fn main(init: process.Init.Minimal) !void {
924925 };
925926
926927 var w: Watch = w: {
927 if (!watch) break :w undefined;
928 if (!watch_flag) break :w undefined;
928929 if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{native_os});
929930 break :w try .init(&maker);
930931 };
932 defer w.deinit();
931933
932934 if (web_server) |ws| try ws.updateConfiguration(&maker);
933935
......@@ -942,7 +944,7 @@ pub fn main(init: process.Init.Minimal) !void {
942944
943945 if (web_server) |ws| {
944946 const c = &scanned_config.configuration;
945 assert(!watch); // fatal error after CLI parsing
947 assert(!watch_flag); // fatal error after CLI parsing
946948 while (true) switch (try ws.wait()) {
947949 .rebuild => {
948950 for (maker.step_stack.keys()) |step_index| {
......@@ -1019,7 +1021,7 @@ pub fn main(init: process.Init.Minimal) !void {
10191021 if (protocol_server != null) {
10201022 fatal("(zig build system) TODO send error messages to client when build.zig compilation fails", .{});
10211023 }
1022 if (watch and can_fs_watch) {
1024 if (watch_flag and can_fs_watch) {
10231025 fatal("(zig build system) TODO set up fs watching even when build.zig compilation fails", .{});
10241026 } else {
10251027 fatal("(zig build system) TODO stay running and wait for user to request rebuild even when build.zig compilation fails", .{});
lib/compiler/Maker/Watch.zig+60-4
......@@ -84,7 +84,7 @@ const Os = switch (builtin.os.tag) {
8484 }
8585
8686 fn destroy(lfh: FileHandle, gpa: Allocator) void {
87 const ptr: [*]u8 = @ptrCast(lfh.handle);
87 const ptr: [*]align(@alignOf(std.os.linux.file_handle)) u8 = @ptrCast(@alignCast(lfh.handle));
8888 const allocated_slice = ptr[0 .. @sizeOf(std.os.linux.file_handle) + lfh.handle.handle_bytes];
8989 return gpa.free(allocated_slice);
9090 }
......@@ -124,6 +124,24 @@ const Os = switch (builtin.os.tag) {
124124 };
125125 }
126126
127 fn deinit(w: *Watch) void {
128 const gpa = w.maker.gpa;
129
130 for (w.os.handle_table.keys(), w.os.handle_table.values()) |fh, *reaction| {
131 fh.destroy(gpa);
132 reaction.reaction_set.deinit(gpa);
133 }
134 w.os.handle_table.deinit(gpa);
135
136 for (w.os.poll_fds.values()) |pollfd| {
137 Io.Threaded.closeFd(pollfd.fd);
138 }
139 w.os.poll_fds.deinit(gpa);
140
141 w.dir_table.deinit(gpa);
142 w.* = undefined;
143 }
144
127145 fn getDirHandle(gpa: Allocator, path: std.Build.Cache.Path, mount_id: *MountId) !FileHandle {
128146 var file_handle_buffer: [@sizeOf(std.os.linux.file_handle) + 128]u8 align(@alignOf(std.os.linux.file_handle)) = undefined;
129147 var buf: [std.fs.max_path_bytes]u8 = undefined;
......@@ -365,7 +383,7 @@ const Os = switch (builtin.os.tag) {
365383 }
366384 }
367385
368 fn notifyApc(apc_context: ?*anyopaque, iosb: *windows.IO_STATUS_BLOCK, _: windows.ULONG) align(std.Io.Threaded.apc_align) callconv(.winapi) void {
386 fn notifyApc(apc_context: ?*anyopaque, iosb: *windows.IO_STATUS_BLOCK, _: windows.ULONG) align(Io.Threaded.apc_align) callconv(.winapi) void {
369387 const w: *Watch = @ptrCast(@alignCast(apc_context));
370388 const dir: *Directory = @fieldParentPtr("iosb", iosb);
371389 assert(iosb.u.Status != .PENDING);
......@@ -485,6 +503,18 @@ const Os = switch (builtin.os.tag) {
485503 };
486504 }
487505
506 fn deinit(w: *Watch) void {
507 const gpa = w.maker.gpa;
508
509 for (w.os.handle_table.keys()) |dir| {
510 dir.deinit(gpa, w);
511 }
512 w.os.handle_table.deinit(gpa);
513
514 w.dir_table.deinit(gpa);
515 w.* = undefined;
516 }
517
488518 fn getFileId(handle: windows.HANDLE) !FileId {
489519 var file_id: FileId = undefined;
490520 var io_status: windows.IO_STATUS_BLOCK = undefined;
......@@ -695,6 +725,21 @@ const Os = switch (builtin.os.tag) {
695725 };
696726 }
697727
728 fn deinit(w: *Watch) void {
729 const gpa = w.maker.gpa;
730
731 for (w.os.handles.items(.rs), w.os.handles.items(.dir_fd)) |rs, dir_fd| {
732 rs.deinit(gpa);
733 Os.Threaded.closeFd(dir_fd);
734 }
735 w.os.handles.deinit(gpa);
736
737 Os.Threaded.closeFd(w.os.kq_fd);
738
739 w.dir_table.deinit(gpa);
740 w.* = undefined;
741 }
742
698743 fn update(w: *Watch, steps: []const Configuration.Step.Index) !void {
699744 const maker = w.maker;
700745 const gpa = maker.gpa;
......@@ -713,7 +758,7 @@ const Os = switch (builtin.os.tag) {
713758 fatal("failed to open directory {f}: {t}", .{ path, err });
714759 };
715760 // Empirically the dir has to stay open or else no events are triggered.
716 errdefer if (!skip_open_dir) std.Io.Threaded.closeFd(dir_fd);
761 errdefer if (!skip_open_dir) Io.Threaded.closeFd(dir_fd);
717762 const changes = [1]posix.Kevent{.{
718763 .ident = @bitCast(@as(isize, dir_fd)),
719764 .filter = std.c.EVFILT.VNODE,
......@@ -813,7 +858,7 @@ const Os = switch (builtin.os.tag) {
813858 };
814859 const filtered_changes = if (i == handles.len - 1) changes[0..1] else &changes;
815860 _ = try Io.Kqueue.kevent(w.os.kq_fd, filtered_changes, &.{}, null);
816 if (path.sub_path.len != 0) std.Io.Threaded.closeFd(dir_fd);
861 if (path.sub_path.len != 0) Io.Threaded.closeFd(dir_fd);
817862
818863 w.dir_table.swapRemoveAt(i);
819864 handles.swapRemove(i);
......@@ -877,6 +922,13 @@ const Os = switch (builtin.os.tag) {
877922 .maker = maker,
878923 };
879924 }
925 fn deinit(w: *Watch) void {
926 const gpa = w.maker.gpa;
927 const io = w.maker.io;
928 w.os.fse.deinit(gpa, io);
929 w.dir_table.deinit(gpa);
930 w.* = undefined;
931 }
880932 fn update(w: *Watch, steps: []const Configuration.Step.Index) !void {
881933 try w.os.fse.setPaths(w.maker, steps);
882934 w.dir_count = w.os.fse.watch_roots.len;
......@@ -970,3 +1022,7 @@ pub const WaitResult = enum {
9701022pub fn wait(w: *Watch, timeout: Timeout) !WaitResult {
9711023 return Os.wait(w, timeout);
9721024}
1025
1026pub fn deinit(w: *Watch) void {
1027 Os.deinit(w);
1028}