authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2019-11-24 14:09:49+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2019-11-25 17:57:44+02:00
log7dba5ea9cfc197cb3a2bbc84d0bce20d8df5886a
treeaa3465273ce1d61560288244c85371b39560de16
parent20f5f5698600589b32f3c4af19e856b5faf578da
signaturelock-open Commit is signed but in an unrecognized format.

update event.fs.watch


1 files changed, 181 insertions(+), 185 deletions(-)

lib/std/event/fs.zig+181-185
...@@ -9,6 +9,9 @@ const windows = os.windows;...@@ -9,6 +9,9 @@ const windows = os.windows;
9const Loop = event.Loop;9const Loop = event.Loop;
10const fd_t = os.fd_t;10const fd_t = os.fd_t;
11const File = std.fs.File;11const File = std.fs.File;
12const Allocator = mem.Allocator;
13
14//! TODO mege this with `std.fs`
1215
13const global_event_loop = Loop.instance orelse16const global_event_loop = Loop.instance orelse
14 @compileError("std.event.fs currently only works with event-based I/O");17 @compileError("std.event.fs currently only works with event-based I/O");
...@@ -681,7 +684,7 @@ fn writeFileModeThread(allocator: *Allocator, path: []const u8, contents: []cons...@@ -681,7 +684,7 @@ fn writeFileModeThread(allocator: *Allocator, path: []const u8, contents: []cons
681/// is closed.684/// is closed.
682/// Caller owns returned memory.685/// Caller owns returned memory.
683pub fn readFile(allocator: *Allocator, file_path: []const u8, max_size: usize) ![]u8 {686pub fn readFile(allocator: *Allocator, file_path: []const u8, max_size: usize) ![]u8 {
684 var close_op = try CloseOperation.start();687 var close_op = try CloseOperation.start(allocator);
685 defer close_op.finish();688 defer close_op.finish();
686689
687 const fd = try openRead(file_path);690 const fd = try openRead(file_path);
...@@ -694,7 +697,7 @@ pub fn readFile(allocator: *Allocator, file_path: []const u8, max_size: usize) !...@@ -694,7 +697,7 @@ pub fn readFile(allocator: *Allocator, file_path: []const u8, max_size: usize) !
694 try list.ensureCapacity(list.len + mem.page_size);697 try list.ensureCapacity(list.len + mem.page_size);
695 const buf = list.items[list.len..];698 const buf = list.items[list.len..];
696 const buf_array = [_][]u8{buf};699 const buf_array = [_][]u8{buf};
697 const amt = try preadv(fd, buf_array, list.len);700 const amt = try preadv(allocator, fd, buf_array, list.len);
698 list.len += amt;701 list.len += amt;
699 if (list.len > max_size) {702 if (list.len > max_size) {
700 return error.FileTooBig;703 return error.FileTooBig;
...@@ -731,16 +734,18 @@ pub fn Watch(comptime V: type) type {...@@ -731,16 +734,18 @@ pub fn Watch(comptime V: type) type {
731 return struct {734 return struct {
732 channel: *event.Channel(Event.Error!Event),735 channel: *event.Channel(Event.Error!Event),
733 os_data: OsData,736 os_data: OsData,
737 allocator: *Allocator,
734738
735 const OsData = switch (builtin.os) {739 const OsData = switch (builtin.os) {
736 .macosx, .freebsd, .netbsd, .dragonfly => struct {740 .macosx, .freebsd, .netbsd, .dragonfly => struct {
737 file_table: FileTable,741 file_table: FileTable,
738 table_lock: event.Lock,742 table_lock: event.Lock,
739743
740 const FileTable = std.StringHashmap(*Put);744 const FileTable = std.StringHashMap(*Put);
741 const Put = struct {745 const Put = struct {
742 putter: anyframe,746 putter_frame: @Frame(kqPutEvents),
743 value_ptr: *V,747 cancelled: bool = false,
748 value: V,
744 };749 };
745 },750 },
746751
...@@ -753,24 +758,30 @@ pub fn Watch(comptime V: type) type {...@@ -753,24 +758,30 @@ pub fn Watch(comptime V: type) type {
753 const WindowsOsData = struct {758 const WindowsOsData = struct {
754 table_lock: event.Lock,759 table_lock: event.Lock,
755 dir_table: DirTable,760 dir_table: DirTable,
756 all_putters: std.atomic.Queue(anyframe),761 all_putters: std.atomic.Queue(Put),
757 ref_count: std.atomic.Int(usize),762 ref_count: std.atomic.Int(usize),
758763
764 const Put = struct {
765 putter: anyframe,
766 cancelled: bool = false,
767 };
768
759 const DirTable = std.StringHashMap(*Dir);769 const DirTable = std.StringHashMap(*Dir);
760 const FileTable = std.HashMap([]const u16, V, hashString, eqlString);770 const FileTable = std.HashMap([]const u16, V, hashString, eqlString);
761771
762 const Dir = struct {772 const Dir = struct {
763 putter: anyframe,773 putter_frame: @Frame(windowsDirReader),
764 file_table: FileTable,774 file_table: FileTable,
765 table_lock: event.Lock,775 table_lock: event.Lock,
766 };776 };
767 };777 };
768778
769 const LinuxOsData = struct {779 const LinuxOsData = struct {
770 putter: anyframe,780 putter_frame: @Frame(linuxEventPutter),
771 inotify_fd: i32,781 inotify_fd: i32,
772 wd_table: WdTable,782 wd_table: WdTable,
773 table_lock: event.Lock,783 table_lock: event.Lock,
784 cancelled: bool = false,
774785
775 const WdTable = std.AutoHashMap(i32, Dir);786 const WdTable = std.AutoHashMap(i32, Dir);
776 const FileTable = std.StringHashMap(V);787 const FileTable = std.StringHashMap(V);
...@@ -781,8 +792,6 @@ pub fn Watch(comptime V: type) type {...@@ -781,8 +792,6 @@ pub fn Watch(comptime V: type) type {
781 };792 };
782 };793 };
783794
784 const FileToHandle = std.StringHashMap(anyframe);
785
786 const Self = @This();795 const Self = @This();
787796
788 pub const Event = struct {797 pub const Event = struct {
...@@ -793,28 +802,44 @@ pub fn Watch(comptime V: type) type {...@@ -793,28 +802,44 @@ pub fn Watch(comptime V: type) type {
793 pub const Error = WatchEventError;802 pub const Error = WatchEventError;
794 };803 };
795804
796 pub fn create(loop: *Loop, event_buf_count: usize) !*Self {805 pub fn init(allocator: *Allocator, event_buf_count: usize) !*Self {
797 const channel = try event.Channel(Self.Event.Error!Self.Event).create(loop, event_buf_count);806 const channel = try allocator.create(event.Channel(Event.Error!Event));
798 errdefer channel.destroy();807 errdefer allocator.destroy(channel);
808 var buf = try allocator.alloc(Event.Error!Event, event_buf_count);
809 errdefer allocator.free(buf);
810 channel.init(buf);
811 errdefer channel.deinit();
812
813 const self = try allocator.create(Self);
814 errdefer allocator.destroy(self);
799815
800 switch (builtin.os) {816 switch (builtin.os) {
801 .linux => {817 .linux => {
802 const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);818 const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
803 errdefer os.close(inotify_fd);819 errdefer os.close(inotify_fd);
804820
805 var result: *Self = undefined;821 self.* = Self{
806// _ = try async<loop.allocator> linuxEventPutter(inotify_fd, channel, &result);822 .allocator = allocator,
807 return result;823 .channel = channel,
824 .os_data = OsData{
825 .putter_frame = undefined,
826 .inotify_fd = inotify_fd,
827 .wd_table = OsData.WdTable.init(allocator),
828 .table_lock = event.Lock.init(),
829 },
830 };
831
832 self.os_data.putter_frame = async self.linuxEventPutter();
833 return self;
808 },834 },
809835
810 .windows => {836 .windows => {
811 const self = try loop.allocator.create(Self);
812 errdefer loop.allocator.destroy(self);
813 self.* = Self{837 self.* = Self{
838 .allocator = allocator,
814 .channel = channel,839 .channel = channel,
815 .os_data = OsData{840 .os_data = OsData{
816 .table_lock = event.Lock.init(loop),841 .table_lock = event.Lock.init(),
817 .dir_table = OsData.DirTable.init(loop.allocator),842 .dir_table = OsData.DirTable.init(allocator),
818 .ref_count = std.atomic.Int(usize).init(1),843 .ref_count = std.atomic.Int(usize).init(1),
819 .all_putters = std.atomic.Queue(anyframe).init(),844 .all_putters = std.atomic.Queue(anyframe).init(),
820 },845 },
...@@ -823,14 +848,12 @@ pub fn Watch(comptime V: type) type {...@@ -823,14 +848,12 @@ pub fn Watch(comptime V: type) type {
823 },848 },
824849
825 .macosx, .freebsd, .netbsd, .dragonfly => {850 .macosx, .freebsd, .netbsd, .dragonfly => {
826 const self = try loop.allocator.create(Self);
827 errdefer loop.allocator.destroy(self);
828
829 self.* = Self{851 self.* = Self{
852 .allocator = allocator,
830 .channel = channel,853 .channel = channel,
831 .os_data = OsData{854 .os_data = OsData{
832 .table_lock = event.Lock.init(loop),855 .table_lock = event.Lock.init(),
833 .file_table = OsData.FileTable.init(loop.allocator),856 .file_table = OsData.FileTable.init(allocator),
834 },857 },
835 };858 };
836 return self;859 return self;
...@@ -840,22 +863,31 @@ pub fn Watch(comptime V: type) type {...@@ -840,22 +863,31 @@ pub fn Watch(comptime V: type) type {
840 }863 }
841864
842 /// All addFile calls and removeFile calls must have completed.865 /// All addFile calls and removeFile calls must have completed.
843 pub fn destroy(self: *Self) void {866 pub fn deinit(self: *Self) void {
844 switch (builtin.os) {867 switch (builtin.os) {
845 .macosx, .freebsd, .netbsd, .dragonfly => {868 .macosx, .freebsd, .netbsd, .dragonfly => {
846 // TODO we need to cancel the frames before destroying the lock869 // TODO we need to cancel the frames before destroying the lock
847 self.os_data.table_lock.deinit();870 self.os_data.table_lock.deinit();
848 var it = self.os_data.file_table.iterator();871 var it = self.os_data.file_table.iterator();
849 while (it.next()) |entry| {872 while (it.next()) |entry| {
850// cancel entry.value.putter;873 entry.cancelled = true;
851 self.channel.loop.allocator.free(entry.key);874 await entry.value.putter;
875 self.allocator.free(entry.key);
876 self.allocator.free(entry.value);
852 }877 }
853 self.channel.destroy();878 self.channel.deinit();
879 self.allocator.destroy(self.channel.buffer_nodes);
880 self.allocator.destroy(self);
881 },
882 .linux => {
883 self.os_data.cancelled = true;
884 await self.os_data.putter_frame;
885 self.allocator.destroy(self);
854 },886 },
855// .linux => cancel self.os_data.putter,
856 .windows => {887 .windows => {
857 while (self.os_data.all_putters.get()) |putter_node| {888 while (self.os_data.all_putters.get()) |putter_node| {
858// cancel putter_node.data;889 putter_node.cancelled = true;
890 await putter_node.frame;
859 }891 }
860 self.deref();892 self.deref();
861 },893 },
...@@ -869,60 +901,68 @@ pub fn Watch(comptime V: type) type {...@@ -869,60 +901,68 @@ pub fn Watch(comptime V: type) type {
869901
870 fn deref(self: *Self) void {902 fn deref(self: *Self) void {
871 if (self.os_data.ref_count.decr() == 1) {903 if (self.os_data.ref_count.decr() == 1) {
872 const allocator = self.channel.loop.allocator;
873 self.os_data.table_lock.deinit();904 self.os_data.table_lock.deinit();
874 var it = self.os_data.dir_table.iterator();905 var it = self.os_data.dir_table.iterator();
875 while (it.next()) |entry| {906 while (it.next()) |entry| {
876 allocator.free(entry.key);907 self.allocator.free(entry.key);
877 allocator.destroy(entry.value);908 self.allocator.destroy(entry.value);
878 }909 }
879 self.os_data.dir_table.deinit();910 self.os_data.dir_table.deinit();
880 self.channel.destroy();911 self.channel.deinit();
881 allocator.destroy(self);912 self.allocator.destroy(self.channel.buffer_nodes);
913 self.allocator.destroy(self);
882 }914 }
883 }915 }
884916
885 pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V {917 pub fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
886 switch (builtin.os) {918 switch (builtin.os) {
887 .macosx, .freebsd, .netbsd, .dragonfly => return await (async addFileKEvent(self, file_path, value) catch unreachable),919 .macosx, .freebsd, .netbsd, .dragonfly => return addFileKEvent(self, file_path, value),
888 .linux => return await (async addFileLinux(self, file_path, value) catch unreachable),920 .linux => return addFileLinux(self, file_path, value),
889 .windows => return await (async addFileWindows(self, file_path, value) catch unreachable),921 .windows => return addFileWindows(self, file_path, value),
890 else => @compileError("Unsupported OS"),922 else => @compileError("Unsupported OS"),
891 }923 }
892 }924 }
893925
894 async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V {926 fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V {
895 const resolved_path = try std.fs.path.resolve(self.channel.loop.allocator, [_][]const u8{file_path});927 const resolved_path = try std.fs.path.resolve(self.allocator, [_][]const u8{file_path});
896 var resolved_path_consumed = false;928 var resolved_path_consumed = false;
897 defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path);929 defer if (!resolved_path_consumed) self.allocator.free(resolved_path);
898930
899 var close_op = try CloseOperation.start(self.channel.loop);931 var close_op = try CloseOperation.start(self.allocator);
900 var close_op_consumed = false;932 var close_op_consumed = false;
901 defer if (!close_op_consumed) close_op.finish();933 defer if (!close_op_consumed) close_op.finish();
902934
903 const flags = if (comptime std.Target.current.isDarwin()) os.O_SYMLINK | os.O_EVTONLY else 0;935 const flags = if (comptime std.Target.current.isDarwin()) os.O_SYMLINK | os.O_EVTONLY else 0;
904 const mode = 0;936 const mode = 0;
905 const fd = try await (async openPosix(self.channel.loop, resolved_path, flags, mode) catch unreachable);937 const fd = try openPosix(self.allocator, resolved_path, flags, mode);
906 close_op.setHandle(fd);938 close_op.setHandle(fd);
907939
908 var put_data: *OsData.Put = undefined;940 var put = try self.allocator.create(OsData.Put);
909 const putter = try async self.kqPutEvents(close_op, value, &put_data);941 errdefer self.allocator.destroy(put);
942 put.* = OsData.Put{
943 .value = value,
944 .putter_frame = undefined,
945 };
946 put.putter_frame = async self.kqPutEvents(close_op, put);
910 close_op_consumed = true;947 close_op_consumed = true;
911// errdefer cancel putter;948 errdefer {
949 put.cancelled = true;
950 await put.putter_frame;
951 }
912952
913 const result = blk: {953 const result = blk: {
914 const held = await (async self.os_data.table_lock.acquire() catch unreachable);954 const held = self.os_data.table_lock.acquire();
915 defer held.release();955 defer held.release();
916956
917 const gop = try self.os_data.file_table.getOrPut(resolved_path);957 const gop = try self.os_data.file_table.getOrPut(resolved_path);
918 if (gop.found_existing) {958 if (gop.found_existing) {
919 const prev_value = gop.kv.value.value_ptr.*;959 const prev_value = gop.kv.value.value;
920// cancel gop.kv.value.putter;960 await gop.kv.value.putter_frame;
921 gop.kv.value = put_data;961 gop.kv.value = put;
922 break :blk prev_value;962 break :blk prev_value;
923 } else {963 } else {
924 resolved_path_consumed = true;964 resolved_path_consumed = true;
925 gop.kv.value = put_data;965 gop.kv.value = put;
926 break :blk null;966 break :blk null;
927 }967 }
928 };968 };
...@@ -930,61 +970,53 @@ pub fn Watch(comptime V: type) type {...@@ -930,61 +970,53 @@ pub fn Watch(comptime V: type) type {
930 return result;970 return result;
931 }971 }
932972
933 async fn kqPutEvents(self: *Self, close_op: *CloseOperation, value: V, out_put: **OsData.Put) void {973 fn kqPutEvents(self: *Self, close_op: *CloseOperation, put: *OsData.Put) void {
934 var value_copy = value;974 global_event_loop.beginOneEvent();
935 var put = OsData.Put{
936 .putter = @frame(),
937 .value_ptr = &value_copy,
938 };
939 out_put.* = &put;
940 self.channel.loop.beginOneEvent();
941975
942 defer {976 defer {
943 close_op.finish();977 close_op.finish();
944 self.channel.loop.finishOneEvent();978 global_event_loop.finishOneEvent();
945 }979 }
946980
947 while (true) {981 while (!put.cancelled) {
948 if (await (async self.channel.loop.bsdWaitKev(982 if (global_event_loop.bsdWaitKev(
949 @intCast(usize, close_op.getHandle()),983 @intCast(usize, close_op.getHandle()),
950 os.EVFILT_VNODE,984 os.EVFILT_VNODE,
951 os.NOTE_WRITE | os.NOTE_DELETE,985 os.NOTE_WRITE | os.NOTE_DELETE,
952 ) catch unreachable)) |kev| {986 )) |kev| {
953 // TODO handle EV_ERROR987 // TODO handle EV_ERROR
954 if (kev.fflags & os.NOTE_DELETE != 0) {988 if (kev.fflags & os.NOTE_DELETE != 0) {
955 await (async self.channel.put(Self.Event{989 self.channel.put(Self.Event{
956 .id = Event.Id.Delete,990 .id = Event.Id.Delete,
957 .data = value_copy,991 .data = put.value,
958 }) catch unreachable);992 });
959 } else if (kev.fflags & os.NOTE_WRITE != 0) {993 } else if (kev.fflags & os.NOTE_WRITE != 0) {
960 await (async self.channel.put(Self.Event{994 self.channel.put(Self.Event{
961 .id = Event.Id.CloseWrite,995 .id = Event.Id.CloseWrite,
962 .data = value_copy,996 .data = put.value,
963 }) catch unreachable);997 });
964 }998 }
965 } else |err| switch (err) {999 } else |err| switch (err) {
966 error.EventNotFound => unreachable,1000 error.EventNotFound => unreachable,
967 error.ProcessNotFound => unreachable,1001 error.ProcessNotFound => unreachable,
968 error.Overflow => unreachable,1002 error.Overflow => unreachable,
969 error.AccessDenied, error.SystemResources => |casted_err| {1003 error.AccessDenied, error.SystemResources => |casted_err| {
970 await (async self.channel.put(casted_err) catch unreachable);1004 self.channel.put(casted_err);
971 },1005 },
972 }1006 }
973 }1007 }
974 }1008 }
9751009
976 async fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V {1010 fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V {
977 const value_copy = value;
978
979 const dirname = std.fs.path.dirname(file_path) orelse ".";1011 const dirname = std.fs.path.dirname(file_path) orelse ".";
980 const dirname_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, dirname);1012 const dirname_with_null = try std.cstr.addNullByte(self.allocator, dirname);
981 var dirname_with_null_consumed = false;1013 var dirname_with_null_consumed = false;
982 defer if (!dirname_with_null_consumed) self.channel.loop.allocator.free(dirname_with_null);1014 defer if (!dirname_with_null_consumed) self.channel.free(dirname_with_null);
9831015
984 const basename = std.fs.path.basename(file_path);1016 const basename = std.fs.path.basename(file_path);
985 const basename_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, basename);1017 const basename_with_null = try std.cstr.addNullByte(self.allocator, basename);
986 var basename_with_null_consumed = false;1018 var basename_with_null_consumed = false;
987 defer if (!basename_with_null_consumed) self.channel.loop.allocator.free(basename_with_null);1019 defer if (!basename_with_null_consumed) self.allocator.free(basename_with_null);
9881020
989 const wd = try os.inotify_add_watchC(1021 const wd = try os.inotify_add_watchC(
990 self.os_data.inotify_fd,1022 self.os_data.inotify_fd,
...@@ -993,14 +1025,14 @@ pub fn Watch(comptime V: type) type {...@@ -993,14 +1025,14 @@ pub fn Watch(comptime V: type) type {
993 );1025 );
994 // wd is either a newly created watch or an existing one.1026 // wd is either a newly created watch or an existing one.
9951027
996 const held = await (async self.os_data.table_lock.acquire() catch unreachable);1028 const held = self.os_data.table_lock.acquire();
997 defer held.release();1029 defer held.release();
9981030
999 const gop = try self.os_data.wd_table.getOrPut(wd);1031 const gop = try self.os_data.wd_table.getOrPut(wd);
1000 if (!gop.found_existing) {1032 if (!gop.found_existing) {
1001 gop.kv.value = OsData.Dir{1033 gop.kv.value = OsData.Dir{
1002 .dirname = dirname_with_null,1034 .dirname = dirname_with_null,
1003 .file_table = OsData.FileTable.init(self.channel.loop.allocator),1035 .file_table = OsData.FileTable.init(self.allocator),
1004 };1036 };
1005 dirname_with_null_consumed = true;1037 dirname_with_null_consumed = true;
1006 }1038 }
...@@ -1009,31 +1041,29 @@ pub fn Watch(comptime V: type) type {...@@ -1009,31 +1041,29 @@ pub fn Watch(comptime V: type) type {
1009 const file_table_gop = try dir.file_table.getOrPut(basename_with_null);1041 const file_table_gop = try dir.file_table.getOrPut(basename_with_null);
1010 if (file_table_gop.found_existing) {1042 if (file_table_gop.found_existing) {
1011 const prev_value = file_table_gop.kv.value;1043 const prev_value = file_table_gop.kv.value;
1012 file_table_gop.kv.value = value_copy;1044 file_table_gop.kv.value = value;
1013 return prev_value;1045 return prev_value;
1014 } else {1046 } else {
1015 file_table_gop.kv.value = value_copy;1047 file_table_gop.kv.value = value;
1016 basename_with_null_consumed = true;1048 basename_with_null_consumed = true;
1017 return null;1049 return null;
1018 }1050 }
1019 }1051 }
10201052
1021 async fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {1053 fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {
1022 const value_copy = value;
1023 // TODO we might need to convert dirname and basename to canonical file paths ("short"?)1054 // TODO we might need to convert dirname and basename to canonical file paths ("short"?)
10241055 const dirname = try std.mem.dupe(self.allocator, u8, std.fs.path.dirname(file_path) orelse ".");
1025 const dirname = try std.mem.dupe(self.channel.loop.allocator, u8, std.fs.path.dirname(file_path) orelse ".");
1026 var dirname_consumed = false;1056 var dirname_consumed = false;
1027 defer if (!dirname_consumed) self.channel.loop.allocator.free(dirname);1057 defer if (!dirname_consumed) self.allocator.free(dirname);
10281058
1029 const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, dirname);1059 const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.allocator, dirname);
1030 defer self.channel.loop.allocator.free(dirname_utf16le);1060 defer self.allocator.free(dirname_utf16le);
10311061
1032 // TODO https://github.com/ziglang/zig/issues/2651062 // TODO https://github.com/ziglang/zig/issues/265
1033 const basename = std.fs.path.basename(file_path);1063 const basename = std.fs.path.basename(file_path);
1034 const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename);1064 const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.allocator, basename);
1035 var basename_utf16le_null_consumed = false;1065 var basename_utf16le_null_consumed = false;
1036 defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null);1066 defer if (!basename_utf16le_null_consumed) self.allocator.free(basename_utf16le_null);
1037 const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];1067 const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
10381068
1039 const dir_handle = try windows.CreateFileW(1069 const dir_handle = try windows.CreateFileW(
...@@ -1048,40 +1078,40 @@ pub fn Watch(comptime V: type) type {...@@ -1048,40 +1078,40 @@ pub fn Watch(comptime V: type) type {
1048 var dir_handle_consumed = false;1078 var dir_handle_consumed = false;
1049 defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);1079 defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);
10501080
1051 const held = await (async self.os_data.table_lock.acquire() catch unreachable);1081 const held = self.os_data.table_lock.acquire();
1052 defer held.release();1082 defer held.release();
10531083
1054 const gop = try self.os_data.dir_table.getOrPut(dirname);1084 const gop = try self.os_data.dir_table.getOrPut(dirname);
1055 if (gop.found_existing) {1085 if (gop.found_existing) {
1056 const dir = gop.kv.value;1086 const dir = gop.kv.value;
1057 const held_dir_lock = await (async dir.table_lock.acquire() catch unreachable);1087 const held_dir_lock = dir.table_lock.acquire();
1058 defer held_dir_lock.release();1088 defer held_dir_lock.release();
10591089
1060 const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null);1090 const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null);
1061 if (file_gop.found_existing) {1091 if (file_gop.found_existing) {
1062 const prev_value = file_gop.kv.value;1092 const prev_value = file_gop.kv.value;
1063 file_gop.kv.value = value_copy;1093 file_gop.kv.value = value;
1064 return prev_value;1094 return prev_value;
1065 } else {1095 } else {
1066 file_gop.kv.value = value_copy;1096 file_gop.kv.value = value;
1067 basename_utf16le_null_consumed = true;1097 basename_utf16le_null_consumed = true;
1068 return null;1098 return null;
1069 }1099 }
1070 } else {1100 } else {
1071 errdefer _ = self.os_data.dir_table.remove(dirname);1101 errdefer _ = self.os_data.dir_table.remove(dirname);
1072 const dir = try self.channel.loop.allocator.create(OsData.Dir);1102 const dir = try self.allocator.create(OsData.Dir);
1073 errdefer self.channel.loop.allocator.destroy(dir);1103 errdefer self.allocator.destroy(dir);
10741104
1075 dir.* = OsData.Dir{1105 dir.* = OsData.Dir{
1076 .file_table = OsData.FileTable.init(self.channel.loop.allocator),1106 .file_table = OsData.FileTable.init(self.allocator),
1077 .table_lock = event.Lock.init(self.channel.loop),1107 .table_lock = event.Lock.init(),
1078 .putter = undefined,1108 .putter_frame = undefined,
1079 };1109 };
1080 gop.kv.value = dir;1110 gop.kv.value = dir;
1081 assert((try dir.file_table.put(basename_utf16le_no_null, value_copy)) == null);1111 assert((try dir.file_table.put(basename_utf16le_no_null, value)) == null);
1082 basename_utf16le_null_consumed = true;1112 basename_utf16le_null_consumed = true;
10831113
1084 dir.putter = try async self.windowsDirReader(dir_handle, dir);1114 dir.putter_frame = async self.windowsDirReader(dir_handle, dir);
1085 dir_handle_consumed = true;1115 dir_handle_consumed = true;
10861116
1087 dirname_consumed = true;1117 dirname_consumed = true;
...@@ -1090,14 +1120,14 @@ pub fn Watch(comptime V: type) type {...@@ -1090,14 +1120,14 @@ pub fn Watch(comptime V: type) type {
1090 }1120 }
1091 }1121 }
10921122
1093 async fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void {1123 fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void {
1094 self.ref();1124 self.ref();
1095 defer self.deref();1125 defer self.deref();
10961126
1097 defer os.close(dir_handle);1127 defer os.close(dir_handle);
10981128
1099 var putter_node = std.atomic.Queue(anyframe).Node{1129 var putter_node = std.atomic.Queue(anyframe).Node{
1100 .data = @frame(),1130 .data = .{ .putter = @frame() },
1101 .prev = null,1131 .prev = null,
1102 .next = null,1132 .next = null,
1103 };1133 };
...@@ -1122,19 +1152,19 @@ pub fn Watch(comptime V: type) type {...@@ -1122,19 +1152,19 @@ pub fn Watch(comptime V: type) type {
1122 // TODO handle this error not in the channel but in the setup1152 // TODO handle this error not in the channel but in the setup
1123 _ = windows.CreateIoCompletionPort(1153 _ = windows.CreateIoCompletionPort(
1124 dir_handle,1154 dir_handle,
1125 self.channel.loop.os_data.io_port,1155 global_event_loop.os_data.io_port,
1126 undefined,1156 undefined,
1127 undefined,1157 undefined,
1128 ) catch |err| {1158 ) catch |err| {
1129 await (async self.channel.put(err) catch unreachable);1159 self.channel.put(err);
1130 return;1160 return;
1131 };1161 };
11321162
1133 while (true) {1163 while (!putter_node.data.cancelled) {
1134 {1164 {
1135 // TODO only 1 beginOneEvent for the whole function1165 // TODO only 1 beginOneEvent for the whole function
1136 self.channel.loop.beginOneEvent();1166 global_event_loop.beginOneEvent();
1137 errdefer self.channel.loop.finishOneEvent();1167 errdefer global_event_loop.finishOneEvent();
1138 errdefer {1168 errdefer {
1139 _ = windows.kernel32.CancelIoEx(dir_handle, &resume_node.base.overlapped);1169 _ = windows.kernel32.CancelIoEx(dir_handle, &resume_node.base.overlapped);
1140 }1170 }
...@@ -1159,7 +1189,7 @@ pub fn Watch(comptime V: type) type {...@@ -1159,7 +1189,7 @@ pub fn Watch(comptime V: type) type {
1159 const err = switch (windows.kernel32.GetLastError()) {1189 const err = switch (windows.kernel32.GetLastError()) {
1160 else => |err| windows.unexpectedError(err),1190 else => |err| windows.unexpectedError(err),
1161 };1191 };
1162 await (async self.channel.put(err) catch unreachable);1192 self.channel.put(err);
1163 } else {1193 } else {
1164 // can't use @bytesToSlice because of the special variable length name field1194 // can't use @bytesToSlice because of the special variable length name field
1165 var ptr = event_buf[0..].ptr;1195 var ptr = event_buf[0..].ptr;
...@@ -1175,7 +1205,7 @@ pub fn Watch(comptime V: type) type {...@@ -1175,7 +1205,7 @@ pub fn Watch(comptime V: type) type {
1175 if (emit) |id| {1205 if (emit) |id| {
1176 const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2];1206 const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2];
1177 const user_value = blk: {1207 const user_value = blk: {
1178 const held = await (async dir.table_lock.acquire() catch unreachable);1208 const held = dir.table_lock.acquire();
1179 defer held.release();1209 defer held.release();
11801210
1181 if (dir.file_table.get(basename_utf16le)) |entry| {1211 if (dir.file_table.get(basename_utf16le)) |entry| {
...@@ -1185,10 +1215,10 @@ pub fn Watch(comptime V: type) type {...@@ -1185,10 +1215,10 @@ pub fn Watch(comptime V: type) type {
1185 }1215 }
1186 };1216 };
1187 if (user_value) |v| {1217 if (user_value) |v| {
1188 await (async self.channel.put(Event{1218 self.channel.put(Event{
1189 .id = id,1219 .id = id,
1190 .data = v,1220 .data = v,
1191 }) catch unreachable);1221 });
1192 }1222 }
1193 }1223 }
1194 if (ev.NextEntryOffset == 0) break;1224 if (ev.NextEntryOffset == 0) break;
...@@ -1197,45 +1227,35 @@ pub fn Watch(comptime V: type) type {...@@ -1197,45 +1227,35 @@ pub fn Watch(comptime V: type) type {
1197 }1227 }
1198 }1228 }
11991229
1200 pub async fn removeFile(self: *Self, file_path: []const u8) ?V {1230 pub fn removeFile(self: *Self, file_path: []const u8) ?V {
1201 @panic("TODO");1231 @panic("TODO");
1202 }1232 }
12031233
1204 async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event.Error!Event), out_watch: **Self) void {1234 fn linuxEventPutter(self: *Self) void {
1205 const loop = channel.loop;1235 global_event_loop.beginOneEvent();
1206
1207 var watch = Self{
1208 .channel = channel,
1209 .os_data = OsData{
1210 .putter = @frame(),
1211 .inotify_fd = inotify_fd,
1212 .wd_table = OsData.WdTable.init(loop.allocator),
1213 .table_lock = event.Lock.init(loop),
1214 },
1215 };
1216 out_watch.* = &watch;
1217
1218 loop.beginOneEvent();
12191236
1220 defer {1237 defer {
1221 watch.os_data.table_lock.deinit();1238 self.os_data.table_lock.deinit();
1222 var wd_it = watch.os_data.wd_table.iterator();1239 var wd_it = self.os_data.wd_table.iterator();
1223 while (wd_it.next()) |wd_entry| {1240 while (wd_it.next()) |wd_entry| {
1224 var file_it = wd_entry.value.file_table.iterator();1241 var file_it = wd_entry.value.file_table.iterator();
1225 while (file_it.next()) |file_entry| {1242 while (file_it.next()) |file_entry| {
1226 loop.allocator.free(file_entry.key);1243 self.allocator.free(file_entry.key);
1227 }1244 }
1228 loop.allocator.free(wd_entry.value.dirname);1245 self.allocator.free(wd_entry.value.dirname);
1246 wd_entry.value.file_table.deinit();
1229 }1247 }
1230 loop.finishOneEvent();1248 self.os_data.wd_table.deinit();
1231 os.close(inotify_fd);1249 global_event_loop.finishOneEvent();
1232 channel.destroy();1250 os.close(self.os_data.inotify_fd);
1251 self.channel.deinit();
1252 self.allocator.free(self.channel.buffer_nodes);
1233 }1253 }
12341254
1235 var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;1255 var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
12361256
1237 while (true) {1257 while (!self.os_data.cancelled) {
1238 const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);1258 const rc = os.linux.read(self.os_data.inotify_fd, &event_buf, event_buf.len);
1239 const errno = os.linux.getErrno(rc);1259 const errno = os.linux.getErrno(rc);
1240 switch (errno) {1260 switch (errno) {
1241 0 => {1261 0 => {
...@@ -1247,12 +1267,13 @@ pub fn Watch(comptime V: type) type {...@@ -1247,12 +1267,13 @@ pub fn Watch(comptime V: type) type {
1247 ev = @ptrCast(*os.linux.inotify_event, ptr);1267 ev = @ptrCast(*os.linux.inotify_event, ptr);
1248 if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {1268 if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
1249 const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);1269 const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);
1250 const basename_with_null = basename_ptr[0 .. std.mem.len(u8, basename_ptr) + 1];1270 // `ev.len` counts all bytes in `ev.name` including terminating null byte.
1271 const basename_with_null = basename_ptr[0 .. ev.len];
1251 const user_value = blk: {1272 const user_value = blk: {
1252 const held = await (async watch.os_data.table_lock.acquire() catch unreachable);1273 const held = self.os_data.table_lock.acquire();
1253 defer held.release();1274 defer held.release();
12541275
1255 const dir = &watch.os_data.wd_table.get(ev.wd).?.value;1276 const dir = &self.os_data.wd_table.get(ev.wd).?.value;
1256 if (dir.file_table.get(basename_with_null)) |entry| {1277 if (dir.file_table.get(basename_with_null)) |entry| {
1257 break :blk entry.value;1278 break :blk entry.value;
1258 } else {1279 } else {
...@@ -1260,10 +1281,10 @@ pub fn Watch(comptime V: type) type {...@@ -1260,10 +1281,10 @@ pub fn Watch(comptime V: type) type {
1260 }1281 }
1261 };1282 };
1262 if (user_value) |v| {1283 if (user_value) |v| {
1263 await (async channel.put(Event{1284 self.channel.put(Event{
1264 .id = WatchEventId.CloseWrite,1285 .id = WatchEventId.CloseWrite,
1265 .data = v,1286 .data = v,
1266 }) catch unreachable);1287 });
1267 }1288 }
1268 }1289 }
1269 }1290 }
...@@ -1272,20 +1293,7 @@ pub fn Watch(comptime V: type) type {...@@ -1272,20 +1293,7 @@ pub fn Watch(comptime V: type) type {
1272 os.linux.EINVAL => unreachable,1293 os.linux.EINVAL => unreachable,
1273 os.linux.EFAULT => unreachable,1294 os.linux.EFAULT => unreachable,
1274 os.linux.EAGAIN => {1295 os.linux.EAGAIN => {
1275 (await (async loop.linuxWaitFd(1296 global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN);
1276 inotify_fd,
1277 os.linux.EPOLLET | os.linux.EPOLLIN,
1278 ) catch unreachable)) catch |err| {
1279 const transformed_err = switch (err) {
1280 error.FileDescriptorAlreadyPresentInSet => unreachable,
1281 error.OperationCausesCircularLoop => unreachable,
1282 error.FileDescriptorNotRegistered => unreachable,
1283 error.FileDescriptorIncompatibleWithEpoll => unreachable,
1284 error.Unexpected => unreachable,
1285 else => |e| e,
1286 };
1287 await (async channel.put(transformed_err) catch unreachable);
1288 };
1289 },1297 },
1290 else => unreachable,1298 else => unreachable,
1291 }1299 }
...@@ -1296,34 +1304,22 @@ pub fn Watch(comptime V: type) type {...@@ -1296,34 +1304,22 @@ pub fn Watch(comptime V: type) type {
12961304
1297const test_tmp_dir = "std_event_fs_test";1305const test_tmp_dir = "std_event_fs_test";
12981306
1299// TODO this test is disabled until the async function rewrite is finished.
1300test "write a file, watch it, write it again" {1307test "write a file, watch it, write it again" {
1301 return error.SkipZigTest;1308 // TODO provide a way to run tests in evented I/O mode
1309 if (!std.io.is_async) return error.SkipZigTest;
1310
1302 const allocator = std.heap.direct_allocator;1311 const allocator = std.heap.direct_allocator;
13031312
1304 // TODO move this into event loop too1313 // TODO move this into event loop too
1305 try os.makePath(allocator, test_tmp_dir);1314 try os.makePath(allocator, test_tmp_dir);
1306 defer os.deleteTree(test_tmp_dir) catch {};1315 defer os.deleteTree(test_tmp_dir) catch {};
13071316
1308 var loop: Loop = undefined;1317 return testFsWatch(&allocator);
1309 try loop.initMultiThreaded(allocator);
1310 defer loop.deinit();
1311
1312 var result: anyerror!void = error.ResultNeverWritten;
1313// const handle = try async<allocator> testFsWatchCantFail(&loop, &result);
1314// defer cancel handle;
1315
1316 loop.run();
1317 return result;
1318}
1319
1320fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void {
1321 result.* = testFsWatch(loop);
1322}1318}
13231319
1324fn testFsWatch(loop: *Loop) !void {1320fn testFsWatch(allocator: *Allocator) !void {
1325 const file_path = try std.fs.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" });1321 const file_path = try std.fs.path.join(allocator, [_][]const u8{ test_tmp_dir, "file.txt" });
1326 defer loop.allocator.free(file_path);1322 defer allocator.free(file_path);
13271323
1328 const contents =1324 const contents =
1329 \\line 11325 \\line 1
...@@ -1332,27 +1328,27 @@ fn testFsWatch(loop: *Loop) !void {...@@ -1332,27 +1328,27 @@ fn testFsWatch(loop: *Loop) !void {
1332 const line2_offset = 7;1328 const line2_offset = 7;
13331329
1334 // first just write then read the file1330 // first just write then read the file
1335 try writeFile(loop, file_path, contents);1331 try writeFile(allocator, file_path, contents);
13361332
1337 const read_contents = try readFile(loop, file_path, 1024 * 1024);1333 const read_contents = try readFile(allocator, file_path, 1024 * 1024);
1338 testing.expectEqualSlices(u8, contents, read_contents);1334 testing.expectEqualSlices(u8, contents, read_contents);
13391335
1340 // now watch the file1336 // now watch the file
1341 var watch = try Watch(void).create(loop, 0);1337 var watch = try Watch(void).init(allocator, 0);
1342 defer watch.destroy();1338 defer watch.deinit();
13431339
1344 testing.expect((try watch.addFile(file_path, {})) == null);1340 testing.expect((try watch.addFile(file_path, {})) == null);
13451341
1346 const ev = async watch.channel.get();1342 const ev = watch.channel.get();
1347 var ev_consumed = false;1343 var ev_consumed = false;
1348 defer if (!ev_consumed) await ev;1344 defer if (!ev_consumed) await ev;
13491345
1350 // overwrite line 21346 // overwrite line 2
1351 const fd = try await openReadWrite(loop, file_path, File.default_mode);1347 const fd = try await openReadWrite(file_path, File.default_mode);
1352 {1348 {
1353 defer os.close(fd);1349 defer os.close(fd);
13541350
1355 try pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset);1351 try pwritev(allocator, fd, []const []const u8{"lorem ipsum"}, line2_offset);
1356 }1352 }
13571353
1358 ev_consumed = true;1354 ev_consumed = true;
...@@ -1360,7 +1356,7 @@ fn testFsWatch(loop: *Loop) !void {...@@ -1360,7 +1356,7 @@ fn testFsWatch(loop: *Loop) !void {
1360 WatchEventId.CloseWrite => {},1356 WatchEventId.CloseWrite => {},
1361 WatchEventId.Delete => @panic("wrong event"),1357 WatchEventId.Delete => @panic("wrong event"),
1362 }1358 }
1363 const contents_updated = try readFile(loop, file_path, 1024 * 1024);1359 const contents_updated = try readFile(allocator, file_path, 1024 * 1024);
1364 testing.expectEqualSlices(u8,1360 testing.expectEqualSlices(u8,
1365 \\line 11361 \\line 1
1366 \\lorem ipsum1362 \\lorem ipsum