diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index fd600bc558..712b3bbc87 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -758,32 +758,28 @@ pub const Compilation = struct { // First, get an item from the watch channel, waiting on the channel. var group = event.Group(BuildError!void).init(self.loop); { - const ev = await (async self.fs_watch.channel.get() catch unreachable); - const root_scope = switch (ev) { - fs.Watch(*Scope.Root).Event.CloseWrite => |x| x, - fs.Watch(*Scope.Root).Event.Err => |err| { - build_result = err; - continue; - }, + const ev = (await (async self.fs_watch.channel.get() catch unreachable)) catch |err| { + build_result = err; + continue; }; + const root_scope = ev.data; group.call(rebuildFile, self, root_scope) catch |err| { build_result = err; continue; }; } // Next, get all the items from the channel that are buffered up. - while (await (async self.fs_watch.channel.getOrNull() catch unreachable)) |ev| { - const root_scope = switch (ev) { - fs.Watch(*Scope.Root).Event.CloseWrite => |x| x, - fs.Watch(*Scope.Root).Event.Err => |err| { + while (await (async self.fs_watch.channel.getOrNull() catch unreachable)) |ev_or_err| { + if (ev_or_err) |ev| { + const root_scope = ev.data; + group.call(rebuildFile, self, root_scope) catch |err| { build_result = err; continue; - }, - }; - group.call(rebuildFile, self, root_scope) catch |err| { + }; + } else |err| { build_result = err; continue; - }; + } } build_result = await (async group.wait() catch unreachable); } diff --git a/std/event/fs.zig b/std/event/fs.zig index e468af2e67..3791e66e34 100644 --- a/std/event/fs.zig +++ b/std/event/fs.zig @@ -358,9 +358,20 @@ pub async fn readFile(loop: *event.Loop, file_path: []const u8, max_size: usize) } } +pub const WatchEventId = enum { + CloseWrite, + Delete, +}; + +pub const WatchEventError = error{ + UserResourceLimitReached, + SystemResources, + AccessDenied, +}; + pub fn Watch(comptime V: type) type { return struct { - channel: *event.Channel(Event), + channel: *event.Channel(Event.Error!Event), os_data: OsData, const OsData = switch (builtin.os) { @@ -395,19 +406,16 @@ pub fn Watch(comptime V: type) type { file_table: OsData.FileTable, }; - pub const Event = union(enum) { - CloseWrite: V, - Err: Error, + pub const Event = struct { + id: Id, + data: V, - pub const Error = error{ - UserResourceLimitReached, - SystemResources, - AccessDenied, - }; + pub const Id = WatchEventId; + pub const Error = WatchEventError; }; pub fn create(loop: *event.Loop, event_buf_count: usize) !*Self { - const channel = try event.Channel(Self.Event).create(loop, event_buf_count); + const channel = try event.Channel(Self.Event.Error!Self.Event).create(loop, event_buf_count); errdefer channel.destroy(); switch (builtin.os) { @@ -519,19 +527,32 @@ pub fn Watch(comptime V: type) type { } while (true) { - (await (async self.channel.loop.bsdWaitKev( - @intCast(usize, close_op.getHandle()), posix.EVFILT_VNODE, posix.NOTE_WRITE, - ) catch unreachable)) catch |err| switch (err) { + if (await (async self.channel.loop.bsdWaitKev( + @intCast(usize, close_op.getHandle()), + posix.EVFILT_VNODE, + posix.NOTE_WRITE | posix.NOTE_DELETE, + ) catch unreachable)) |kev| { + // TODO handle EV_ERROR + if (kev.fflags & posix.NOTE_DELETE != 0) { + await (async self.channel.put(Self.Event{ + .id = Event.Id.Delete, + .data = value_copy, + }) catch unreachable); + } else if (kev.fflags & posix.NOTE_WRITE != 0) { + await (async self.channel.put(Self.Event{ + .id = Event.Id.CloseWrite, + .data = value_copy, + }) catch unreachable); + } + } else |err| switch (err) { error.EventNotFound => unreachable, error.ProcessNotFound => unreachable, error.AccessDenied, error.SystemResources => { // TODO https://github.com/ziglang/zig/issues/769 const casted_err = @errSetCast(error{AccessDenied,SystemResources}, err); - await (async self.channel.put(Self.Event{ .Err = casted_err }) catch unreachable); + await (async self.channel.put(casted_err) catch unreachable); }, - }; - - await (async self.channel.put(Self.Event{ .CloseWrite = value_copy }) catch unreachable); + } } } @@ -582,7 +603,7 @@ pub fn Watch(comptime V: type) type { @panic("TODO"); } - async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event), out_watch: **Self) void { + async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event.Error!Event), out_watch: **Self) void { // TODO https://github.com/ziglang/zig/issues/1194 suspend { resume @handle(); @@ -743,9 +764,9 @@ async fn testFsWatch(loop: *event.Loop) !void { } ev_consumed = true; - switch (await ev) { - Watch(void).Event.CloseWrite => {}, - Watch(void).Event.Err => |err| return err, + switch ((try await ev).id) { + WatchEventId.CloseWrite => {}, + WatchEventId.Delete => @panic("wrong event"), } const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024); @@ -753,4 +774,6 @@ async fn testFsWatch(loop: *event.Loop) !void { \\line 1 \\lorem ipsum )); + + // TODO test deleting the file and then re-adding it. we should get events for both } diff --git a/std/event/loop.zig b/std/event/loop.zig index 278462409f..a1a5012c5f 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -52,6 +52,20 @@ pub const Loop = struct { base: ResumeNode, kevent: posix.Kevent, }; + + pub const Basic = switch (builtin.os) { + builtin.Os.macosx => struct { + base: ResumeNode, + kev: posix.Kevent, + }, + builtin.Os.linux => struct { + base: ResumeNode, + }, + builtin.Os.windows => struct { + base: ResumeNode, + }, + else => @compileError("unsupported OS"), + }; }; /// After initialization, call run(). @@ -379,28 +393,37 @@ pub const Loop = struct { defer self.linuxRemoveFd(fd); suspend { // TODO explicitly put this memory in the coroutine frame #1194 - var resume_node = ResumeNode{ - .id = ResumeNode.Id.Basic, - .handle = @handle(), + var resume_node = ResumeNode.Basic{ + .base = ResumeNode{ + .id = ResumeNode.Id.Basic, + .handle = @handle(), + }, }; - try self.linuxAddFd(fd, &resume_node, flags); + try self.linuxAddFd(fd, &resume_node.base, flags); } } - pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !void { - defer self.bsdRemoveKev(ident, filter); + pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !posix.Kevent { + // TODO #1194 suspend { - // TODO explicitly put this memory in the coroutine frame #1194 - var resume_node = ResumeNode{ + resume @handle(); + } + var resume_node = ResumeNode.Basic{ + .base = ResumeNode{ .id = ResumeNode.Id.Basic, .handle = @handle(), - }; + }, + .kev = undefined, + }; + defer self.bsdRemoveKev(ident, filter); + suspend { try self.bsdAddKev(&resume_node, ident, filter, fflags); } + return resume_node.kev; } /// resume_node must live longer than the promise that it holds a reference to. - pub fn bsdAddKev(self: *Loop, resume_node: *ResumeNode, ident: usize, filter: i16, fflags: u32) !void { + pub fn bsdAddKev(self: *Loop, resume_node: *ResumeNode.Basic, ident: usize, filter: i16, fflags: u32) !void { self.beginOneEvent(); errdefer self.finishOneEvent(); var kev = posix.Kevent{ @@ -409,7 +432,7 @@ pub const Loop = struct { .flags = posix.EV_ADD|posix.EV_ENABLE|posix.EV_CLEAR, .fflags = fflags, .data = 0, - .udata = @ptrToInt(resume_node), + .udata = @ptrToInt(&resume_node.base), }; const kevent_array = (*[1]posix.Kevent)(&kev); const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; @@ -632,7 +655,10 @@ pub const Loop = struct { const handle = resume_node.handle; const resume_node_id = resume_node.id; switch (resume_node_id) { - ResumeNode.Id.Basic => {}, + ResumeNode.Id.Basic => { + const basic_node = @fieldParentPtr(ResumeNode.Basic, "base", resume_node); + basic_node.kev = ev; + }, ResumeNode.Id.Stop => return, ResumeNode.Id.EventFd => { const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);