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;
99const Loop = event.Loop;
1010const fd_t = os.fd_t;
1111const File = std.fs.File;
12const Allocator = mem.Allocator;
13
14//! TODO mege this with `std.fs`
1215
1316const global_event_loop = Loop.instance orelse
1417 @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
681684/// is closed.
682685/// Caller owns returned memory.
683686pub 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);
685688 defer close_op.finish();
686689
687690 const fd = try openRead(file_path);
......@@ -694,7 +697,7 @@ pub fn readFile(allocator: *Allocator, file_path: []const u8, max_size: usize) !
694697 try list.ensureCapacity(list.len + mem.page_size);
695698 const buf = list.items[list.len..];
696699 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);
698701 list.len += amt;
699702 if (list.len > max_size) {
700703 return error.FileTooBig;
......@@ -731,16 +734,18 @@ pub fn Watch(comptime V: type) type {
731734 return struct {
732735 channel: *event.Channel(Event.Error!Event),
733736 os_data: OsData,
737 allocator: *Allocator,
734738
735739 const OsData = switch (builtin.os) {
736740 .macosx, .freebsd, .netbsd, .dragonfly => struct {
737741 file_table: FileTable,
738742 table_lock: event.Lock,
739743
740 const FileTable = std.StringHashmap(*Put);
744 const FileTable = std.StringHashMap(*Put);
741745 const Put = struct {
742 putter: anyframe,
743 value_ptr: *V,
746 putter_frame: @Frame(kqPutEvents),
747 cancelled: bool = false,
748 value: V,
744749 };
745750 },
746751
......@@ -753,24 +758,30 @@ pub fn Watch(comptime V: type) type {
753758 const WindowsOsData = struct {
754759 table_lock: event.Lock,
755760 dir_table: DirTable,
756 all_putters: std.atomic.Queue(anyframe),
761 all_putters: std.atomic.Queue(Put),
757762 ref_count: std.atomic.Int(usize),
758763
764 const Put = struct {
765 putter: anyframe,
766 cancelled: bool = false,
767 };
768
759769 const DirTable = std.StringHashMap(*Dir);
760770 const FileTable = std.HashMap([]const u16, V, hashString, eqlString);
761771
762772 const Dir = struct {
763 putter: anyframe,
773 putter_frame: @Frame(windowsDirReader),
764774 file_table: FileTable,
765775 table_lock: event.Lock,
766776 };
767777 };
768778
769779 const LinuxOsData = struct {
770 putter: anyframe,
780 putter_frame: @Frame(linuxEventPutter),
771781 inotify_fd: i32,
772782 wd_table: WdTable,
773783 table_lock: event.Lock,
784 cancelled: bool = false,
774785
775786 const WdTable = std.AutoHashMap(i32, Dir);
776787 const FileTable = std.StringHashMap(V);
......@@ -781,8 +792,6 @@ pub fn Watch(comptime V: type) type {
781792 };
782793 };
783794
784 const FileToHandle = std.StringHashMap(anyframe);
785
786795 const Self = @This();
787796
788797 pub const Event = struct {
......@@ -793,28 +802,44 @@ pub fn Watch(comptime V: type) type {
793802 pub const Error = WatchEventError;
794803 };
795804
796 pub fn create(loop: *Loop, event_buf_count: usize) !*Self {
797 const channel = try event.Channel(Self.Event.Error!Self.Event).create(loop, event_buf_count);
798 errdefer channel.destroy();
805 pub fn init(allocator: *Allocator, event_buf_count: usize) !*Self {
806 const channel = try allocator.create(event.Channel(Event.Error!Event));
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
800816 switch (builtin.os) {
801817 .linux => {
802818 const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
803819 errdefer os.close(inotify_fd);
804820
805 var result: *Self = undefined;
806// _ = try async<loop.allocator> linuxEventPutter(inotify_fd, channel, &result);
807 return result;
821 self.* = Self{
822 .allocator = allocator,
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;
808834 },
809835
810836 .windows => {
811 const self = try loop.allocator.create(Self);
812 errdefer loop.allocator.destroy(self);
813837 self.* = Self{
838 .allocator = allocator,
814839 .channel = channel,
815840 .os_data = OsData{
816 .table_lock = event.Lock.init(loop),
817 .dir_table = OsData.DirTable.init(loop.allocator),
841 .table_lock = event.Lock.init(),
842 .dir_table = OsData.DirTable.init(allocator),
818843 .ref_count = std.atomic.Int(usize).init(1),
819844 .all_putters = std.atomic.Queue(anyframe).init(),
820845 },
......@@ -823,14 +848,12 @@ pub fn Watch(comptime V: type) type {
823848 },
824849
825850 .macosx, .freebsd, .netbsd, .dragonfly => {
826 const self = try loop.allocator.create(Self);
827 errdefer loop.allocator.destroy(self);
828
829851 self.* = Self{
852 .allocator = allocator,
830853 .channel = channel,
831854 .os_data = OsData{
832 .table_lock = event.Lock.init(loop),
833 .file_table = OsData.FileTable.init(loop.allocator),
855 .table_lock = event.Lock.init(),
856 .file_table = OsData.FileTable.init(allocator),
834857 },
835858 };
836859 return self;
......@@ -840,22 +863,31 @@ pub fn Watch(comptime V: type) type {
840863 }
841864
842865 /// All addFile calls and removeFile calls must have completed.
843 pub fn destroy(self: *Self) void {
866 pub fn deinit(self: *Self) void {
844867 switch (builtin.os) {
845868 .macosx, .freebsd, .netbsd, .dragonfly => {
846869 // TODO we need to cancel the frames before destroying the lock
847870 self.os_data.table_lock.deinit();
848871 var it = self.os_data.file_table.iterator();
849872 while (it.next()) |entry| {
850// cancel entry.value.putter;
851 self.channel.loop.allocator.free(entry.key);
873 entry.cancelled = true;
874 await entry.value.putter;
875 self.allocator.free(entry.key);
876 self.allocator.free(entry.value);
852877 }
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);
854886 },
855// .linux => cancel self.os_data.putter,
856887 .windows => {
857888 while (self.os_data.all_putters.get()) |putter_node| {
858// cancel putter_node.data;
889 putter_node.cancelled = true;
890 await putter_node.frame;
859891 }
860892 self.deref();
861893 },
......@@ -869,60 +901,68 @@ pub fn Watch(comptime V: type) type {
869901
870902 fn deref(self: *Self) void {
871903 if (self.os_data.ref_count.decr() == 1) {
872 const allocator = self.channel.loop.allocator;
873904 self.os_data.table_lock.deinit();
874905 var it = self.os_data.dir_table.iterator();
875906 while (it.next()) |entry| {
876 allocator.free(entry.key);
877 allocator.destroy(entry.value);
907 self.allocator.free(entry.key);
908 self.allocator.destroy(entry.value);
878909 }
879910 self.os_data.dir_table.deinit();
880 self.channel.destroy();
881 allocator.destroy(self);
911 self.channel.deinit();
912 self.allocator.destroy(self.channel.buffer_nodes);
913 self.allocator.destroy(self);
882914 }
883915 }
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 {
886918 switch (builtin.os) {
887 .macosx, .freebsd, .netbsd, .dragonfly => return await (async addFileKEvent(self, file_path, value) catch unreachable),
888 .linux => return await (async addFileLinux(self, file_path, value) catch unreachable),
889 .windows => return await (async addFileWindows(self, file_path, value) catch unreachable),
919 .macosx, .freebsd, .netbsd, .dragonfly => return addFileKEvent(self, file_path, value),
920 .linux => return addFileLinux(self, file_path, value),
921 .windows => return addFileWindows(self, file_path, value),
890922 else => @compileError("Unsupported OS"),
891923 }
892924 }
893925
894 async 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});
926 fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V {
927 const resolved_path = try std.fs.path.resolve(self.allocator, [_][]const u8{file_path});
896928 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);
900932 var close_op_consumed = false;
901933 defer if (!close_op_consumed) close_op.finish();
902934
903935 const flags = if (comptime std.Target.current.isDarwin()) os.O_SYMLINK | os.O_EVTONLY else 0;
904936 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);
906938 close_op.setHandle(fd);
907939
908 var put_data: *OsData.Put = undefined;
909 const putter = try async self.kqPutEvents(close_op, value, &put_data);
940 var put = try self.allocator.create(OsData.Put);
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);
910947 close_op_consumed = true;
911// errdefer cancel putter;
948 errdefer {
949 put.cancelled = true;
950 await put.putter_frame;
951 }
912952
913953 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();
915955 defer held.release();
916956
917957 const gop = try self.os_data.file_table.getOrPut(resolved_path);
918958 if (gop.found_existing) {
919 const prev_value = gop.kv.value.value_ptr.*;
920// cancel gop.kv.value.putter;
921 gop.kv.value = put_data;
959 const prev_value = gop.kv.value.value;
960 await gop.kv.value.putter_frame;
961 gop.kv.value = put;
922962 break :blk prev_value;
923963 } else {
924964 resolved_path_consumed = true;
925 gop.kv.value = put_data;
965 gop.kv.value = put;
926966 break :blk null;
927967 }
928968 };
......@@ -930,61 +970,53 @@ pub fn Watch(comptime V: type) type {
930970 return result;
931971 }
932972
933 async fn kqPutEvents(self: *Self, close_op: *CloseOperation, value: V, out_put: **OsData.Put) void {
934 var value_copy = value;
935 var put = OsData.Put{
936 .putter = @frame(),
937 .value_ptr = &value_copy,
938 };
939 out_put.* = &put;
940 self.channel.loop.beginOneEvent();
973 fn kqPutEvents(self: *Self, close_op: *CloseOperation, put: *OsData.Put) void {
974 global_event_loop.beginOneEvent();
941975
942976 defer {
943977 close_op.finish();
944 self.channel.loop.finishOneEvent();
978 global_event_loop.finishOneEvent();
945979 }
946980
947 while (true) {
948 if (await (async self.channel.loop.bsdWaitKev(
981 while (!put.cancelled) {
982 if (global_event_loop.bsdWaitKev(
949983 @intCast(usize, close_op.getHandle()),
950984 os.EVFILT_VNODE,
951985 os.NOTE_WRITE | os.NOTE_DELETE,
952 ) catch unreachable)) |kev| {
986 )) |kev| {
953987 // TODO handle EV_ERROR
954988 if (kev.fflags & os.NOTE_DELETE != 0) {
955 await (async self.channel.put(Self.Event{
989 self.channel.put(Self.Event{
956990 .id = Event.Id.Delete,
957 .data = value_copy,
958 }) catch unreachable);
991 .data = put.value,
992 });
959993 } else if (kev.fflags & os.NOTE_WRITE != 0) {
960 await (async self.channel.put(Self.Event{
994 self.channel.put(Self.Event{
961995 .id = Event.Id.CloseWrite,
962 .data = value_copy,
963 }) catch unreachable);
996 .data = put.value,
997 });
964998 }
965999 } else |err| switch (err) {
9661000 error.EventNotFound => unreachable,
9671001 error.ProcessNotFound => unreachable,
9681002 error.Overflow => unreachable,
9691003 error.AccessDenied, error.SystemResources => |casted_err| {
970 await (async self.channel.put(casted_err) catch unreachable);
1004 self.channel.put(casted_err);
9711005 },
9721006 }
9731007 }
9741008 }
9751009
976 async fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V {
977 const value_copy = value;
978
1010 fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V {
9791011 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);
9811013 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
9841016 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);
9861018 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
9891021 const wd = try os.inotify_add_watchC(
9901022 self.os_data.inotify_fd,
......@@ -993,14 +1025,14 @@ pub fn Watch(comptime V: type) type {
9931025 );
9941026 // 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();
9971029 defer held.release();
9981030
9991031 const gop = try self.os_data.wd_table.getOrPut(wd);
10001032 if (!gop.found_existing) {
10011033 gop.kv.value = OsData.Dir{
10021034 .dirname = dirname_with_null,
1003 .file_table = OsData.FileTable.init(self.channel.loop.allocator),
1035 .file_table = OsData.FileTable.init(self.allocator),
10041036 };
10051037 dirname_with_null_consumed = true;
10061038 }
......@@ -1009,31 +1041,29 @@ pub fn Watch(comptime V: type) type {
10091041 const file_table_gop = try dir.file_table.getOrPut(basename_with_null);
10101042 if (file_table_gop.found_existing) {
10111043 const prev_value = file_table_gop.kv.value;
1012 file_table_gop.kv.value = value_copy;
1044 file_table_gop.kv.value = value;
10131045 return prev_value;
10141046 } else {
1015 file_table_gop.kv.value = value_copy;
1047 file_table_gop.kv.value = value;
10161048 basename_with_null_consumed = true;
10171049 return null;
10181050 }
10191051 }
10201052
1021 async fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {
1022 const value_copy = value;
1053 fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {
10231054 // TODO we might need to convert dirname and basename to canonical file paths ("short"?)
1024
1025 const dirname = try std.mem.dupe(self.channel.loop.allocator, u8, std.fs.path.dirname(file_path) orelse ".");
1055 const dirname = try std.mem.dupe(self.allocator, u8, std.fs.path.dirname(file_path) orelse ".");
10261056 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);
1030 defer self.channel.loop.allocator.free(dirname_utf16le);
1059 const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.allocator, dirname);
1060 defer self.allocator.free(dirname_utf16le);
10311061
10321062 // TODO https://github.com/ziglang/zig/issues/265
10331063 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);
10351065 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);
10371067 const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
10381068
10391069 const dir_handle = try windows.CreateFileW(
......@@ -1048,40 +1078,40 @@ pub fn Watch(comptime V: type) type {
10481078 var dir_handle_consumed = false;
10491079 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();
10521082 defer held.release();
10531083
10541084 const gop = try self.os_data.dir_table.getOrPut(dirname);
10551085 if (gop.found_existing) {
10561086 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();
10581088 defer held_dir_lock.release();
10591089
10601090 const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null);
10611091 if (file_gop.found_existing) {
10621092 const prev_value = file_gop.kv.value;
1063 file_gop.kv.value = value_copy;
1093 file_gop.kv.value = value;
10641094 return prev_value;
10651095 } else {
1066 file_gop.kv.value = value_copy;
1096 file_gop.kv.value = value;
10671097 basename_utf16le_null_consumed = true;
10681098 return null;
10691099 }
10701100 } else {
10711101 errdefer _ = self.os_data.dir_table.remove(dirname);
1072 const dir = try self.channel.loop.allocator.create(OsData.Dir);
1073 errdefer self.channel.loop.allocator.destroy(dir);
1102 const dir = try self.allocator.create(OsData.Dir);
1103 errdefer self.allocator.destroy(dir);
10741104
10751105 dir.* = OsData.Dir{
1076 .file_table = OsData.FileTable.init(self.channel.loop.allocator),
1077 .table_lock = event.Lock.init(self.channel.loop),
1078 .putter = undefined,
1106 .file_table = OsData.FileTable.init(self.allocator),
1107 .table_lock = event.Lock.init(),
1108 .putter_frame = undefined,
10791109 };
10801110 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);
10821112 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);
10851115 dir_handle_consumed = true;
10861116
10871117 dirname_consumed = true;
......@@ -1090,14 +1120,14 @@ pub fn Watch(comptime V: type) type {
10901120 }
10911121 }
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 {
10941124 self.ref();
10951125 defer self.deref();
10961126
10971127 defer os.close(dir_handle);
10981128
10991129 var putter_node = std.atomic.Queue(anyframe).Node{
1100 .data = @frame(),
1130 .data = .{ .putter = @frame() },
11011131 .prev = null,
11021132 .next = null,
11031133 };
......@@ -1122,19 +1152,19 @@ pub fn Watch(comptime V: type) type {
11221152 // TODO handle this error not in the channel but in the setup
11231153 _ = windows.CreateIoCompletionPort(
11241154 dir_handle,
1125 self.channel.loop.os_data.io_port,
1155 global_event_loop.os_data.io_port,
11261156 undefined,
11271157 undefined,
11281158 ) catch |err| {
1129 await (async self.channel.put(err) catch unreachable);
1159 self.channel.put(err);
11301160 return;
11311161 };
11321162
1133 while (true) {
1163 while (!putter_node.data.cancelled) {
11341164 {
11351165 // TODO only 1 beginOneEvent for the whole function
1136 self.channel.loop.beginOneEvent();
1137 errdefer self.channel.loop.finishOneEvent();
1166 global_event_loop.beginOneEvent();
1167 errdefer global_event_loop.finishOneEvent();
11381168 errdefer {
11391169 _ = windows.kernel32.CancelIoEx(dir_handle, &resume_node.base.overlapped);
11401170 }
......@@ -1159,7 +1189,7 @@ pub fn Watch(comptime V: type) type {
11591189 const err = switch (windows.kernel32.GetLastError()) {
11601190 else => |err| windows.unexpectedError(err),
11611191 };
1162 await (async self.channel.put(err) catch unreachable);
1192 self.channel.put(err);
11631193 } else {
11641194 // can't use @bytesToSlice because of the special variable length name field
11651195 var ptr = event_buf[0..].ptr;
......@@ -1175,7 +1205,7 @@ pub fn Watch(comptime V: type) type {
11751205 if (emit) |id| {
11761206 const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2];
11771207 const user_value = blk: {
1178 const held = await (async dir.table_lock.acquire() catch unreachable);
1208 const held = dir.table_lock.acquire();
11791209 defer held.release();
11801210
11811211 if (dir.file_table.get(basename_utf16le)) |entry| {
......@@ -1185,10 +1215,10 @@ pub fn Watch(comptime V: type) type {
11851215 }
11861216 };
11871217 if (user_value) |v| {
1188 await (async self.channel.put(Event{
1218 self.channel.put(Event{
11891219 .id = id,
11901220 .data = v,
1191 }) catch unreachable);
1221 });
11921222 }
11931223 }
11941224 if (ev.NextEntryOffset == 0) break;
......@@ -1197,45 +1227,35 @@ pub fn Watch(comptime V: type) type {
11971227 }
11981228 }
11991229
1200 pub async fn removeFile(self: *Self, file_path: []const u8) ?V {
1230 pub fn removeFile(self: *Self, file_path: []const u8) ?V {
12011231 @panic("TODO");
12021232 }
12031233
1204 async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event.Error!Event), out_watch: **Self) void {
1205 const loop = channel.loop;
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();
1234 fn linuxEventPutter(self: *Self) void {
1235 global_event_loop.beginOneEvent();
12191236
12201237 defer {
1221 watch.os_data.table_lock.deinit();
1222 var wd_it = watch.os_data.wd_table.iterator();
1238 self.os_data.table_lock.deinit();
1239 var wd_it = self.os_data.wd_table.iterator();
12231240 while (wd_it.next()) |wd_entry| {
12241241 var file_it = wd_entry.value.file_table.iterator();
12251242 while (file_it.next()) |file_entry| {
1226 loop.allocator.free(file_entry.key);
1243 self.allocator.free(file_entry.key);
12271244 }
1228 loop.allocator.free(wd_entry.value.dirname);
1245 self.allocator.free(wd_entry.value.dirname);
1246 wd_entry.value.file_table.deinit();
12291247 }
1230 loop.finishOneEvent();
1231 os.close(inotify_fd);
1232 channel.destroy();
1248 self.os_data.wd_table.deinit();
1249 global_event_loop.finishOneEvent();
1250 os.close(self.os_data.inotify_fd);
1251 self.channel.deinit();
1252 self.allocator.free(self.channel.buffer_nodes);
12331253 }
12341254
12351255 var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
12361256
1237 while (true) {
1238 const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);
1257 while (!self.os_data.cancelled) {
1258 const rc = os.linux.read(self.os_data.inotify_fd, &event_buf, event_buf.len);
12391259 const errno = os.linux.getErrno(rc);
12401260 switch (errno) {
12411261 0 => {
......@@ -1247,12 +1267,13 @@ pub fn Watch(comptime V: type) type {
12471267 ev = @ptrCast(*os.linux.inotify_event, ptr);
12481268 if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
12491269 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];
12511272 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();
12531274 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;
12561277 if (dir.file_table.get(basename_with_null)) |entry| {
12571278 break :blk entry.value;
12581279 } else {
......@@ -1260,10 +1281,10 @@ pub fn Watch(comptime V: type) type {
12601281 }
12611282 };
12621283 if (user_value) |v| {
1263 await (async channel.put(Event{
1284 self.channel.put(Event{
12641285 .id = WatchEventId.CloseWrite,
12651286 .data = v,
1266 }) catch unreachable);
1287 });
12671288 }
12681289 }
12691290 }
......@@ -1272,20 +1293,7 @@ pub fn Watch(comptime V: type) type {
12721293 os.linux.EINVAL => unreachable,
12731294 os.linux.EFAULT => unreachable,
12741295 os.linux.EAGAIN => {
1275 (await (async loop.linuxWaitFd(
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 };
1296 global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN);
12891297 },
12901298 else => unreachable,
12911299 }
......@@ -1296,34 +1304,22 @@ pub fn Watch(comptime V: type) type {
12961304
12971305const test_tmp_dir = "std_event_fs_test";
12981306
1299// TODO this test is disabled until the async function rewrite is finished.
13001307test "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
13021311 const allocator = std.heap.direct_allocator;
13031312
13041313 // TODO move this into event loop too
13051314 try os.makePath(allocator, test_tmp_dir);
13061315 defer os.deleteTree(test_tmp_dir) catch {};
13071316
1308 var loop: Loop = undefined;
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);
1317 return testFsWatch(&allocator);
13221318}
13231319
1324fn testFsWatch(loop: *Loop) !void {
1325 const file_path = try std.fs.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" });
1326 defer loop.allocator.free(file_path);
1320fn testFsWatch(allocator: *Allocator) !void {
1321 const file_path = try std.fs.path.join(allocator, [_][]const u8{ test_tmp_dir, "file.txt" });
1322 defer allocator.free(file_path);
13271323
13281324 const contents =
13291325 \\line 1
......@@ -1332,27 +1328,27 @@ fn testFsWatch(loop: *Loop) !void {
13321328 const line2_offset = 7;
13331329
13341330 // 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);
13381334 testing.expectEqualSlices(u8, contents, read_contents);
13391335
13401336 // now watch the file
1341 var watch = try Watch(void).create(loop, 0);
1342 defer watch.destroy();
1337 var watch = try Watch(void).init(allocator, 0);
1338 defer watch.deinit();
13431339
13441340 testing.expect((try watch.addFile(file_path, {})) == null);
13451341
1346 const ev = async watch.channel.get();
1342 const ev = watch.channel.get();
13471343 var ev_consumed = false;
13481344 defer if (!ev_consumed) await ev;
13491345
13501346 // 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);
13521348 {
13531349 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);
13561352 }
13571353
13581354 ev_consumed = true;
......@@ -1360,7 +1356,7 @@ fn testFsWatch(loop: *Loop) !void {
13601356 WatchEventId.CloseWrite => {},
13611357 WatchEventId.Delete => @panic("wrong event"),
13621358 }
1363 const contents_updated = try readFile(loop, file_path, 1024 * 1024);
1359 const contents_updated = try readFile(allocator, file_path, 1024 * 1024);
13641360 testing.expectEqualSlices(u8,
13651361 \\line 1
13661362 \\lorem ipsum