authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-07 22:12:47-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-07 22:14:30-04:00
log60955feab82ef256a8983517f7435cde797c4e84
tree10b78e4e2b09254e2777724955d6a96dd9b787b5
parent5cbfe392beb26520554e7ec6ae7c67df47cc7e04

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

3 files changed, 93 insertions(+), 48 deletions(-)

src-self-hosted/compilation.zig+11-15
......@@ -758,32 +758,28 @@ pub const Compilation = struct {
758758 // First, get an item from the watch channel, waiting on the channel.
759759 var group = event.Group(BuildError!void).init(self.loop);
760760 {
761 const ev = await (async self.fs_watch.channel.get() catch unreachable);
762 const root_scope = switch (ev) {
763 fs.Watch(*Scope.Root).Event.CloseWrite => |x| x,
764 fs.Watch(*Scope.Root).Event.Err => |err| {
765 build_result = err;
766 continue;
767 },
761 const ev = (await (async self.fs_watch.channel.get() catch unreachable)) catch |err| {
762 build_result = err;
763 continue;
768764 };
765 const root_scope = ev.data;
769766 group.call(rebuildFile, self, root_scope) catch |err| {
770767 build_result = err;
771768 continue;
772769 };
773770 }
774771 // Next, get all the items from the channel that are buffered up.
775 while (await (async self.fs_watch.channel.getOrNull() catch unreachable)) |ev| {
776 const root_scope = switch (ev) {
777 fs.Watch(*Scope.Root).Event.CloseWrite => |x| x,
778 fs.Watch(*Scope.Root).Event.Err => |err| {
772 while (await (async self.fs_watch.channel.getOrNull() catch unreachable)) |ev_or_err| {
773 if (ev_or_err) |ev| {
774 const root_scope = ev.data;
775 group.call(rebuildFile, self, root_scope) catch |err| {
779776 build_result = err;
780777 continue;
781 },
782 };
783 group.call(rebuildFile, self, root_scope) catch |err| {
778 };
779 } else |err| {
784780 build_result = err;
785781 continue;
786 };
782 }
787783 }
788784 build_result = await (async group.wait() catch unreachable);
789785 }
std/event/fs.zig+44-21
......@@ -358,9 +358,20 @@ pub async fn readFile(loop: *event.Loop, file_path: []const u8, max_size: usize)
358358 }
359359}
360360
361pub const WatchEventId = enum {
362 CloseWrite,
363 Delete,
364};
365
366pub const WatchEventError = error{
367 UserResourceLimitReached,
368 SystemResources,
369 AccessDenied,
370};
371
361372pub fn Watch(comptime V: type) type {
362373 return struct {
363 channel: *event.Channel(Event),
374 channel: *event.Channel(Event.Error!Event),
364375 os_data: OsData,
365376
366377 const OsData = switch (builtin.os) {
......@@ -395,19 +406,16 @@ pub fn Watch(comptime V: type) type {
395406 file_table: OsData.FileTable,
396407 };
397408
398 pub const Event = union(enum) {
399 CloseWrite: V,
400 Err: Error,
409 pub const Event = struct {
410 id: Id,
411 data: V,
401412
402 pub const Error = error{
403 UserResourceLimitReached,
404 SystemResources,
405 AccessDenied,
406 };
413 pub const Id = WatchEventId;
414 pub const Error = WatchEventError;
407415 };
408416
409417 pub fn create(loop: *event.Loop, event_buf_count: usize) !*Self {
410 const channel = try event.Channel(Self.Event).create(loop, event_buf_count);
418 const channel = try event.Channel(Self.Event.Error!Self.Event).create(loop, event_buf_count);
411419 errdefer channel.destroy();
412420
413421 switch (builtin.os) {
......@@ -519,19 +527,32 @@ pub fn Watch(comptime V: type) type {
519527 }
520528
521529 while (true) {
522 (await (async self.channel.loop.bsdWaitKev(
523 @intCast(usize, close_op.getHandle()), posix.EVFILT_VNODE, posix.NOTE_WRITE,
524 ) catch unreachable)) catch |err| switch (err) {
530 if (await (async self.channel.loop.bsdWaitKev(
531 @intCast(usize, close_op.getHandle()),
532 posix.EVFILT_VNODE,
533 posix.NOTE_WRITE | posix.NOTE_DELETE,
534 ) catch unreachable)) |kev| {
535 // TODO handle EV_ERROR
536 if (kev.fflags & posix.NOTE_DELETE != 0) {
537 await (async self.channel.put(Self.Event{
538 .id = Event.Id.Delete,
539 .data = value_copy,
540 }) catch unreachable);
541 } else if (kev.fflags & posix.NOTE_WRITE != 0) {
542 await (async self.channel.put(Self.Event{
543 .id = Event.Id.CloseWrite,
544 .data = value_copy,
545 }) catch unreachable);
546 }
547 } else |err| switch (err) {
525548 error.EventNotFound => unreachable,
526549 error.ProcessNotFound => unreachable,
527550 error.AccessDenied, error.SystemResources => {
528551 // TODO https://github.com/ziglang/zig/issues/769
529552 const casted_err = @errSetCast(error{AccessDenied,SystemResources}, err);
530 await (async self.channel.put(Self.Event{ .Err = casted_err }) catch unreachable);
553 await (async self.channel.put(casted_err) catch unreachable);
531554 },
532 };
533
534 await (async self.channel.put(Self.Event{ .CloseWrite = value_copy }) catch unreachable);
555 }
535556 }
536557 }
537558
......@@ -582,7 +603,7 @@ pub fn Watch(comptime V: type) type {
582603 @panic("TODO");
583604 }
584605
585 async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event), out_watch: **Self) void {
606 async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event.Error!Event), out_watch: **Self) void {
586607 // TODO https://github.com/ziglang/zig/issues/1194
587608 suspend {
588609 resume @handle();
......@@ -743,9 +764,9 @@ async fn testFsWatch(loop: *event.Loop) !void {
743764 }
744765
745766 ev_consumed = true;
746 switch (await ev) {
747 Watch(void).Event.CloseWrite => {},
748 Watch(void).Event.Err => |err| return err,
767 switch ((try await ev).id) {
768 WatchEventId.CloseWrite => {},
769 WatchEventId.Delete => @panic("wrong event"),
749770 }
750771
751772 const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);
......@@ -753,4 +774,6 @@ async fn testFsWatch(loop: *event.Loop) !void {
753774 \\line 1
754775 \\lorem ipsum
755776 ));
777
778 // TODO test deleting the file and then re-adding it. we should get events for both
756779}
std/event/loop.zig+38-12
......@@ -52,6 +52,20 @@ pub const Loop = struct {
5252 base: ResumeNode,
5353 kevent: posix.Kevent,
5454 };
55
56 pub const Basic = switch (builtin.os) {
57 builtin.Os.macosx => struct {
58 base: ResumeNode,
59 kev: posix.Kevent,
60 },
61 builtin.Os.linux => struct {
62 base: ResumeNode,
63 },
64 builtin.Os.windows => struct {
65 base: ResumeNode,
66 },
67 else => @compileError("unsupported OS"),
68 };
5569 };
5670
5771 /// After initialization, call run().
......@@ -379,28 +393,37 @@ pub const Loop = struct {
379393 defer self.linuxRemoveFd(fd);
380394 suspend {
381395 // TODO explicitly put this memory in the coroutine frame #1194
382 var resume_node = ResumeNode{
383 .id = ResumeNode.Id.Basic,
384 .handle = @handle(),
396 var resume_node = ResumeNode.Basic{
397 .base = ResumeNode{
398 .id = ResumeNode.Id.Basic,
399 .handle = @handle(),
400 },
385401 };
386 try self.linuxAddFd(fd, &resume_node, flags);
402 try self.linuxAddFd(fd, &resume_node.base, flags);
387403 }
388404 }
389405
390 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !void {
391 defer self.bsdRemoveKev(ident, filter);
406 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !posix.Kevent {
407 // TODO #1194
392408 suspend {
393 // TODO explicitly put this memory in the coroutine frame #1194
394 var resume_node = ResumeNode{
409 resume @handle();
410 }
411 var resume_node = ResumeNode.Basic{
412 .base = ResumeNode{
395413 .id = ResumeNode.Id.Basic,
396414 .handle = @handle(),
397 };
415 },
416 .kev = undefined,
417 };
418 defer self.bsdRemoveKev(ident, filter);
419 suspend {
398420 try self.bsdAddKev(&resume_node, ident, filter, fflags);
399421 }
422 return resume_node.kev;
400423 }
401424
402425 /// resume_node must live longer than the promise that it holds a reference to.
403 pub fn bsdAddKev(self: *Loop, resume_node: *ResumeNode, ident: usize, filter: i16, fflags: u32) !void {
426 pub fn bsdAddKev(self: *Loop, resume_node: *ResumeNode.Basic, ident: usize, filter: i16, fflags: u32) !void {
404427 self.beginOneEvent();
405428 errdefer self.finishOneEvent();
406429 var kev = posix.Kevent{
......@@ -409,7 +432,7 @@ pub const Loop = struct {
409432 .flags = posix.EV_ADD|posix.EV_ENABLE|posix.EV_CLEAR,
410433 .fflags = fflags,
411434 .data = 0,
412 .udata = @ptrToInt(resume_node),
435 .udata = @ptrToInt(&resume_node.base),
413436 };
414437 const kevent_array = (*[1]posix.Kevent)(&kev);
415438 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
......@@ -632,7 +655,10 @@ pub const Loop = struct {
632655 const handle = resume_node.handle;
633656 const resume_node_id = resume_node.id;
634657 switch (resume_node_id) {
635 ResumeNode.Id.Basic => {},
658 ResumeNode.Id.Basic => {
659 const basic_node = @fieldParentPtr(ResumeNode.Basic, "base", resume_node);
660 basic_node.kev = ev;
661 },
636662 ResumeNode.Id.Stop => return,
637663 ResumeNode.Id.EventFd => {
638664 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);