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 {...@@ -758,32 +758,28 @@ pub const Compilation = struct {
758 // First, get an item from the watch channel, waiting on the channel.758 // First, get an item from the watch channel, waiting on the channel.
759 var group = event.Group(BuildError!void).init(self.loop);759 var group = event.Group(BuildError!void).init(self.loop);
760 {760 {
761 const ev = await (async self.fs_watch.channel.get() catch unreachable);761 const ev = (await (async self.fs_watch.channel.get() catch unreachable)) catch |err| {
762 const root_scope = switch (ev) {762 build_result = err;
763 fs.Watch(*Scope.Root).Event.CloseWrite => |x| x,763 continue;
764 fs.Watch(*Scope.Root).Event.Err => |err| {
765 build_result = err;
766 continue;
767 },
768 };764 };
765 const root_scope = ev.data;
769 group.call(rebuildFile, self, root_scope) catch |err| {766 group.call(rebuildFile, self, root_scope) catch |err| {
770 build_result = err;767 build_result = err;
771 continue;768 continue;
772 };769 };
773 }770 }
774 // Next, get all the items from the channel that are buffered up.771 // Next, get all the items from the channel that are buffered up.
775 while (await (async self.fs_watch.channel.getOrNull() catch unreachable)) |ev| {772 while (await (async self.fs_watch.channel.getOrNull() catch unreachable)) |ev_or_err| {
776 const root_scope = switch (ev) {773 if (ev_or_err) |ev| {
777 fs.Watch(*Scope.Root).Event.CloseWrite => |x| x,774 const root_scope = ev.data;
778 fs.Watch(*Scope.Root).Event.Err => |err| {775 group.call(rebuildFile, self, root_scope) catch |err| {
779 build_result = err;776 build_result = err;
780 continue;777 continue;
781 },778 };
782 };779 } else |err| {
783 group.call(rebuildFile, self, root_scope) catch |err| {
784 build_result = err;780 build_result = err;
785 continue;781 continue;
786 };782 }
787 }783 }
788 build_result = await (async group.wait() catch unreachable);784 build_result = await (async group.wait() catch unreachable);
789 }785 }
std/event/fs.zig+44-21
...@@ -358,9 +358,20 @@ pub async fn readFile(loop: *event.Loop, file_path: []const u8, max_size: usize)...@@ -358,9 +358,20 @@ pub async fn readFile(loop: *event.Loop, file_path: []const u8, max_size: usize)
358 }358 }
359}359}
360360
361pub const WatchEventId = enum {
362 CloseWrite,
363 Delete,
364};
365
366pub const WatchEventError = error{
367 UserResourceLimitReached,
368 SystemResources,
369 AccessDenied,
370};
371
361pub fn Watch(comptime V: type) type {372pub fn Watch(comptime V: type) type {
362 return struct {373 return struct {
363 channel: *event.Channel(Event),374 channel: *event.Channel(Event.Error!Event),
364 os_data: OsData,375 os_data: OsData,
365376
366 const OsData = switch (builtin.os) {377 const OsData = switch (builtin.os) {
...@@ -395,19 +406,16 @@ pub fn Watch(comptime V: type) type {...@@ -395,19 +406,16 @@ pub fn Watch(comptime V: type) type {
395 file_table: OsData.FileTable,406 file_table: OsData.FileTable,
396 };407 };
397408
398 pub const Event = union(enum) {409 pub const Event = struct {
399 CloseWrite: V,410 id: Id,
400 Err: Error,411 data: V,
401412
402 pub const Error = error{413 pub const Id = WatchEventId;
403 UserResourceLimitReached,414 pub const Error = WatchEventError;
404 SystemResources,
405 AccessDenied,
406 };
407 };415 };
408416
409 pub fn create(loop: *event.Loop, event_buf_count: usize) !*Self {417 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);
411 errdefer channel.destroy();419 errdefer channel.destroy();
412420
413 switch (builtin.os) {421 switch (builtin.os) {
...@@ -519,19 +527,32 @@ pub fn Watch(comptime V: type) type {...@@ -519,19 +527,32 @@ pub fn Watch(comptime V: type) type {
519 }527 }
520528
521 while (true) {529 while (true) {
522 (await (async self.channel.loop.bsdWaitKev(530 if (await (async self.channel.loop.bsdWaitKev(
523 @intCast(usize, close_op.getHandle()), posix.EVFILT_VNODE, posix.NOTE_WRITE,531 @intCast(usize, close_op.getHandle()),
524 ) catch unreachable)) catch |err| switch (err) {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) {
525 error.EventNotFound => unreachable,548 error.EventNotFound => unreachable,
526 error.ProcessNotFound => unreachable,549 error.ProcessNotFound => unreachable,
527 error.AccessDenied, error.SystemResources => {550 error.AccessDenied, error.SystemResources => {
528 // TODO https://github.com/ziglang/zig/issues/769551 // TODO https://github.com/ziglang/zig/issues/769
529 const casted_err = @errSetCast(error{AccessDenied,SystemResources}, err);552 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);
531 },554 },
532 };555 }
533
534 await (async self.channel.put(Self.Event{ .CloseWrite = value_copy }) catch unreachable);
535 }556 }
536 }557 }
537558
...@@ -582,7 +603,7 @@ pub fn Watch(comptime V: type) type {...@@ -582,7 +603,7 @@ pub fn Watch(comptime V: type) type {
582 @panic("TODO");603 @panic("TODO");
583 }604 }
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 {
586 // TODO https://github.com/ziglang/zig/issues/1194607 // TODO https://github.com/ziglang/zig/issues/1194
587 suspend {608 suspend {
588 resume @handle();609 resume @handle();
...@@ -743,9 +764,9 @@ async fn testFsWatch(loop: *event.Loop) !void {...@@ -743,9 +764,9 @@ async fn testFsWatch(loop: *event.Loop) !void {
743 }764 }
744765
745 ev_consumed = true;766 ev_consumed = true;
746 switch (await ev) {767 switch ((try await ev).id) {
747 Watch(void).Event.CloseWrite => {},768 WatchEventId.CloseWrite => {},
748 Watch(void).Event.Err => |err| return err,769 WatchEventId.Delete => @panic("wrong event"),
749 }770 }
750771
751 const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);772 const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);
...@@ -753,4 +774,6 @@ async fn testFsWatch(loop: *event.Loop) !void {...@@ -753,4 +774,6 @@ async fn testFsWatch(loop: *event.Loop) !void {
753 \\line 1774 \\line 1
754 \\lorem ipsum775 \\lorem ipsum
755 ));776 ));
777
778 // TODO test deleting the file and then re-adding it. we should get events for both
756}779}
std/event/loop.zig+38-12
...@@ -52,6 +52,20 @@ pub const Loop = struct {...@@ -52,6 +52,20 @@ pub const Loop = struct {
52 base: ResumeNode,52 base: ResumeNode,
53 kevent: posix.Kevent,53 kevent: posix.Kevent,
54 };54 };
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 };
55 };69 };
5670
57 /// After initialization, call run().71 /// After initialization, call run().
...@@ -379,28 +393,37 @@ pub const Loop = struct {...@@ -379,28 +393,37 @@ pub const Loop = struct {
379 defer self.linuxRemoveFd(fd);393 defer self.linuxRemoveFd(fd);
380 suspend {394 suspend {
381 // TODO explicitly put this memory in the coroutine frame #1194395 // TODO explicitly put this memory in the coroutine frame #1194
382 var resume_node = ResumeNode{396 var resume_node = ResumeNode.Basic{
383 .id = ResumeNode.Id.Basic,397 .base = ResumeNode{
384 .handle = @handle(),398 .id = ResumeNode.Id.Basic,
399 .handle = @handle(),
400 },
385 };401 };
386 try self.linuxAddFd(fd, &resume_node, flags);402 try self.linuxAddFd(fd, &resume_node.base, flags);
387 }403 }
388 }404 }
389405
390 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !void {406 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !posix.Kevent {
391 defer self.bsdRemoveKev(ident, filter);407 // TODO #1194
392 suspend {408 suspend {
393 // TODO explicitly put this memory in the coroutine frame #1194409 resume @handle();
394 var resume_node = ResumeNode{410 }
411 var resume_node = ResumeNode.Basic{
412 .base = ResumeNode{
395 .id = ResumeNode.Id.Basic,413 .id = ResumeNode.Id.Basic,
396 .handle = @handle(),414 .handle = @handle(),
397 };415 },
416 .kev = undefined,
417 };
418 defer self.bsdRemoveKev(ident, filter);
419 suspend {
398 try self.bsdAddKev(&resume_node, ident, filter, fflags);420 try self.bsdAddKev(&resume_node, ident, filter, fflags);
399 }421 }
422 return resume_node.kev;
400 }423 }
401424
402 /// resume_node must live longer than the promise that it holds a reference to.425 /// 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 {
404 self.beginOneEvent();427 self.beginOneEvent();
405 errdefer self.finishOneEvent();428 errdefer self.finishOneEvent();
406 var kev = posix.Kevent{429 var kev = posix.Kevent{
...@@ -409,7 +432,7 @@ pub const Loop = struct {...@@ -409,7 +432,7 @@ pub const Loop = struct {
409 .flags = posix.EV_ADD|posix.EV_ENABLE|posix.EV_CLEAR,432 .flags = posix.EV_ADD|posix.EV_ENABLE|posix.EV_CLEAR,
410 .fflags = fflags,433 .fflags = fflags,
411 .data = 0,434 .data = 0,
412 .udata = @ptrToInt(resume_node),435 .udata = @ptrToInt(&resume_node.base),
413 };436 };
414 const kevent_array = (*[1]posix.Kevent)(&kev);437 const kevent_array = (*[1]posix.Kevent)(&kev);
415 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];438 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
...@@ -632,7 +655,10 @@ pub const Loop = struct {...@@ -632,7 +655,10 @@ pub const Loop = struct {
632 const handle = resume_node.handle;655 const handle = resume_node.handle;
633 const resume_node_id = resume_node.id;656 const resume_node_id = resume_node.id;
634 switch (resume_node_id) {657 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 },
636 ResumeNode.Id.Stop => return,662 ResumeNode.Id.Stop => return,
637 ResumeNode.Id.EventFd => {663 ResumeNode.Id.EventFd => {
638 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);664 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);