mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
std.event.fs.Watch distinguishes between Delete and CloseWrite on darwin
TODO: after 1 event emitted for a deleted file, the file is no longer watched
This commit is contained in:
parent
5cbfe392be
commit
60955feab8
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user