| author | |
| committer | |
| log | 60955feab82ef256a8983517f7435cde797c4e84 |
| tree | 10b78e4e2b09254e2777724955d6a96dd9b787b5 |
| parent | 5cbfe392beb26520554e7ec6ae7c67df47cc7e04 |
TODO: after 1 event emitted for a deleted file, the file is no longer
watched3 files changed, 93 insertions(+), 48 deletions(-)
src-self-hosted/compilation.zig+11-15| ... | ... | @@ -758,32 +758,28 @@ pub const Compilation = struct { |
| 758 | 758 | // First, get an item from the watch channel, waiting on the channel. |
| 759 | 759 | var group = event.Group(BuildError!void).init(self.loop); |
| 760 | 760 | { |
| 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; | |
| 768 | 764 | }; |
| 765 | const root_scope = ev.data; | |
| 769 | 766 | group.call(rebuildFile, self, root_scope) catch |err| { |
| 770 | 767 | build_result = err; |
| 771 | 768 | continue; |
| 772 | 769 | }; |
| 773 | 770 | } |
| 774 | 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| { | |
| 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| { | |
| 779 | 776 | build_result = err; |
| 780 | 777 | continue; |
| 781 | }, | |
| 782 | }; | |
| 783 | group.call(rebuildFile, self, root_scope) catch |err| { | |
| 778 | }; | |
| 779 | } else |err| { | |
| 784 | 780 | build_result = err; |
| 785 | 781 | continue; |
| 786 | }; | |
| 782 | } | |
| 787 | 783 | } |
| 788 | 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 | 358 | } |
| 359 | 359 | } |
| 360 | 360 | |
| 361 | pub const WatchEventId = enum { | |
| 362 | CloseWrite, | |
| 363 | Delete, | |
| 364 | }; | |
| 365 | ||
| 366 | pub const WatchEventError = error{ | |
| 367 | UserResourceLimitReached, | |
| 368 | SystemResources, | |
| 369 | AccessDenied, | |
| 370 | }; | |
| 371 | ||
| 361 | 372 | pub fn Watch(comptime V: type) type { |
| 362 | 373 | return struct { |
| 363 | channel: *event.Channel(Event), | |
| 374 | channel: *event.Channel(Event.Error!Event), | |
| 364 | 375 | os_data: OsData, |
| 365 | 376 | |
| 366 | 377 | const OsData = switch (builtin.os) { |
| ... | ... | @@ -395,19 +406,16 @@ pub fn Watch(comptime V: type) type { |
| 395 | 406 | file_table: OsData.FileTable, |
| 396 | 407 | }; |
| 397 | 408 | |
| 398 | pub const Event = union(enum) { | |
| 399 | CloseWrite: V, | |
| 400 | Err: Error, | |
| 409 | pub const Event = struct { | |
| 410 | id: Id, | |
| 411 | data: V, | |
| 401 | 412 | |
| 402 | pub const Error = error{ | |
| 403 | UserResourceLimitReached, | |
| 404 | SystemResources, | |
| 405 | AccessDenied, | |
| 406 | }; | |
| 413 | pub const Id = WatchEventId; | |
| 414 | pub const Error = WatchEventError; | |
| 407 | 415 | }; |
| 408 | 416 | |
| 409 | 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 | 419 | errdefer channel.destroy(); |
| 412 | 420 | |
| 413 | 421 | switch (builtin.os) { |
| ... | ... | @@ -519,19 +527,32 @@ pub fn Watch(comptime V: type) type { |
| 519 | 527 | } |
| 520 | 528 | |
| 521 | 529 | 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) { | |
| 525 | 548 | error.EventNotFound => unreachable, |
| 526 | 549 | error.ProcessNotFound => unreachable, |
| 527 | 550 | error.AccessDenied, error.SystemResources => { |
| 528 | 551 | // TODO https://github.com/ziglang/zig/issues/769 |
| 529 | 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 | }; | |
| 533 | ||
| 534 | await (async self.channel.put(Self.Event{ .CloseWrite = value_copy }) catch unreachable); | |
| 555 | } | |
| 535 | 556 | } |
| 536 | 557 | } |
| 537 | 558 | |
| ... | ... | @@ -582,7 +603,7 @@ pub fn Watch(comptime V: type) type { |
| 582 | 603 | @panic("TODO"); |
| 583 | 604 | } |
| 584 | 605 | |
| 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 | 607 | // TODO https://github.com/ziglang/zig/issues/1194 |
| 587 | 608 | suspend { |
| 588 | 609 | resume @handle(); |
| ... | ... | @@ -743,9 +764,9 @@ async fn testFsWatch(loop: *event.Loop) !void { |
| 743 | 764 | } |
| 744 | 765 | |
| 745 | 766 | 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"), | |
| 749 | 770 | } |
| 750 | 771 | |
| 751 | 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 | 774 | \\line 1 |
| 754 | 775 | \\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 | 52 | base: ResumeNode, |
| 53 | 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 | }; |
| 56 | 70 | |
| 57 | 71 | /// After initialization, call run(). |
| ... | ... | @@ -379,28 +393,37 @@ pub const Loop = struct { |
| 379 | 393 | defer self.linuxRemoveFd(fd); |
| 380 | 394 | suspend { |
| 381 | 395 | // 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 | }, | |
| 385 | 401 | }; |
| 386 | try self.linuxAddFd(fd, &resume_node, flags); | |
| 402 | try self.linuxAddFd(fd, &resume_node.base, flags); | |
| 387 | 403 | } |
| 388 | 404 | } |
| 389 | 405 | |
| 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 | |
| 392 | 408 | 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{ | |
| 395 | 413 | .id = ResumeNode.Id.Basic, |
| 396 | 414 | .handle = @handle(), |
| 397 | }; | |
| 415 | }, | |
| 416 | .kev = undefined, | |
| 417 | }; | |
| 418 | defer self.bsdRemoveKev(ident, filter); | |
| 419 | suspend { | |
| 398 | 420 | try self.bsdAddKev(&resume_node, ident, filter, fflags); |
| 399 | 421 | } |
| 422 | return resume_node.kev; | |
| 400 | 423 | } |
| 401 | 424 | |
| 402 | 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 | 427 | self.beginOneEvent(); |
| 405 | 428 | errdefer self.finishOneEvent(); |
| 406 | 429 | var kev = posix.Kevent{ |
| ... | ... | @@ -409,7 +432,7 @@ pub const Loop = struct { |
| 409 | 432 | .flags = posix.EV_ADD|posix.EV_ENABLE|posix.EV_CLEAR, |
| 410 | 433 | .fflags = fflags, |
| 411 | 434 | .data = 0, |
| 412 | .udata = @ptrToInt(resume_node), | |
| 435 | .udata = @ptrToInt(&resume_node.base), | |
| 413 | 436 | }; |
| 414 | 437 | const kevent_array = (*[1]posix.Kevent)(&kev); |
| 415 | 438 | const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; |
| ... | ... | @@ -632,7 +655,10 @@ pub const Loop = struct { |
| 632 | 655 | const handle = resume_node.handle; |
| 633 | 656 | const resume_node_id = resume_node.id; |
| 634 | 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 | 662 | ResumeNode.Id.Stop => return, |
| 637 | 663 | ResumeNode.Id.EventFd => { |
| 638 | 664 | const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); |