| ... | @@ -681,6 +681,7 @@ pub const WatchEventError = error{ | ... | @@ -681,6 +681,7 @@ pub const WatchEventError = error{ |
| 681 | UserResourceLimitReached, | 681 | UserResourceLimitReached, |
| 682 | SystemResources, | 682 | SystemResources, |
| 683 | AccessDenied, | 683 | AccessDenied, |
| | 684 | Unexpected, // TODO remove this possibility |
| 684 | }; | 685 | }; |
| 685 | | 686 | |
| 686 | pub fn Watch(comptime V: type) type { | 687 | pub fn Watch(comptime V: type) type { |
| ... | @@ -699,27 +700,48 @@ pub fn Watch(comptime V: type) type { | ... | @@ -699,27 +700,48 @@ pub fn Watch(comptime V: type) type { |
| 699 | value_ptr: *V, | 700 | value_ptr: *V, |
| 700 | }; | 701 | }; |
| 701 | }, | 702 | }, |
| 702 | builtin.Os.linux => struct { | 703 | |
| | 704 | builtin.Os.linux => LinuxOsData, |
| | 705 | builtin.Os.windows => WindowsOsData, |
| | 706 | |
| | 707 | else => @compileError("Unsupported OS"), |
| | 708 | }; |
| | 709 | |
| | 710 | const WindowsOsData = struct { |
| | 711 | table_lock: event.Lock, |
| | 712 | dir_table: DirTable, |
| | 713 | all_putters: std.atomic.Queue(promise), |
| | 714 | ref_count: std.atomic.Int(usize), |
| | 715 | |
| | 716 | const DirTable = std.AutoHashMap([]const u8, *Dir); |
| | 717 | const FileTable = std.AutoHashMap([]const u16, V); |
| | 718 | |
| | 719 | const Dir = struct { |
| 703 | putter: promise, | 720 | putter: promise, |
| 704 | inotify_fd: i32, | 721 | file_table: FileTable, |
| 705 | wd_table: WdTable, | | |
| 706 | table_lock: event.Lock, | 722 | table_lock: event.Lock, |
| | 723 | }; |
| | 724 | }; |
| 707 | | 725 | |
| 708 | const FileTable = std.AutoHashMap([]const u8, V); | 726 | const LinuxOsData = struct { |
| 709 | }, | 727 | putter: promise, |
| 710 | else => @compileError("Unsupported OS"), | 728 | inotify_fd: i32, |
| | 729 | wd_table: WdTable, |
| | 730 | table_lock: event.Lock, |
| | 731 | |
| | 732 | const WdTable = std.AutoHashMap(i32, Dir); |
| | 733 | const FileTable = std.AutoHashMap([]const u8, V); |
| | 734 | |
| | 735 | const Dir = struct { |
| | 736 | dirname: []const u8, |
| | 737 | file_table: FileTable, |
| | 738 | }; |
| 711 | }; | 739 | }; |
| 712 | | 740 | |
| 713 | const WdTable = std.AutoHashMap(i32, Dir); | | |
| 714 | const FileToHandle = std.AutoHashMap([]const u8, promise); | 741 | const FileToHandle = std.AutoHashMap([]const u8, promise); |
| 715 | | 742 | |
| 716 | const Self = this; | 743 | const Self = this; |
| 717 | | 744 | |
| 718 | const Dir = struct { | | |
| 719 | dirname: []const u8, | | |
| 720 | file_table: OsData.FileTable, | | |
| 721 | }; | | |
| 722 | | | |
| 723 | pub const Event = struct { | 745 | pub const Event = struct { |
| 724 | id: Id, | 746 | id: Id, |
| 725 | data: V, | 747 | data: V, |
| ... | @@ -741,6 +763,22 @@ pub fn Watch(comptime V: type) type { | ... | @@ -741,6 +763,22 @@ pub fn Watch(comptime V: type) type { |
| 741 | _ = try async<loop.allocator> linuxEventPutter(inotify_fd, channel, &result); | 763 | _ = try async<loop.allocator> linuxEventPutter(inotify_fd, channel, &result); |
| 742 | return result; | 764 | return result; |
| 743 | }, | 765 | }, |
| | 766 | |
| | 767 | builtin.Os.windows => { |
| | 768 | const self = try loop.allocator.createOne(Self); |
| | 769 | errdefer loop.allocator.destroy(self); |
| | 770 | self.* = Self{ |
| | 771 | .channel = channel, |
| | 772 | .os_data = OsData{ |
| | 773 | .table_lock = event.Lock.init(loop), |
| | 774 | .dir_table = OsData.DirTable.init(loop.allocator), |
| | 775 | .ref_count = std.atomic.Int(usize).init(1), |
| | 776 | .all_putters = std.atomic.Queue(promise).init(), |
| | 777 | }, |
| | 778 | }; |
| | 779 | return self; |
| | 780 | }, |
| | 781 | |
| 744 | builtin.Os.macosx => { | 782 | builtin.Os.macosx => { |
| 745 | const self = try loop.allocator.createOne(Self); | 783 | const self = try loop.allocator.createOne(Self); |
| 746 | errdefer loop.allocator.destroy(self); | 784 | errdefer loop.allocator.destroy(self); |
| ... | @@ -758,9 +796,11 @@ pub fn Watch(comptime V: type) type { | ... | @@ -758,9 +796,11 @@ pub fn Watch(comptime V: type) type { |
| 758 | } | 796 | } |
| 759 | } | 797 | } |
| 760 | | 798 | |
| | 799 | /// All addFile calls and removeFile calls must have completed. |
| 761 | pub fn destroy(self: *Self) void { | 800 | pub fn destroy(self: *Self) void { |
| 762 | switch (builtin.os) { | 801 | switch (builtin.os) { |
| 763 | builtin.Os.macosx => { | 802 | builtin.Os.macosx => { |
| | 803 | // TODO we need to cancel the coroutines before destroying the lock |
| 764 | self.os_data.table_lock.deinit(); | 804 | self.os_data.table_lock.deinit(); |
| 765 | var it = self.os_data.file_table.iterator(); | 805 | var it = self.os_data.file_table.iterator(); |
| 766 | while (it.next()) |entry| { | 806 | while (it.next()) |entry| { |
| ... | @@ -770,14 +810,41 @@ pub fn Watch(comptime V: type) type { | ... | @@ -770,14 +810,41 @@ pub fn Watch(comptime V: type) type { |
| 770 | self.channel.destroy(); | 810 | self.channel.destroy(); |
| 771 | }, | 811 | }, |
| 772 | builtin.Os.linux => cancel self.os_data.putter, | 812 | builtin.Os.linux => cancel self.os_data.putter, |
| | 813 | builtin.Os.windows => { |
| | 814 | while (self.os_data.all_putters.get()) |putter_node| { |
| | 815 | cancel putter_node.data; |
| | 816 | } |
| | 817 | self.deref(); |
| | 818 | }, |
| 773 | else => @compileError("Unsupported OS"), | 819 | else => @compileError("Unsupported OS"), |
| 774 | } | 820 | } |
| 775 | } | 821 | } |
| 776 | | 822 | |
| | 823 | fn ref(self: *Self) void { |
| | 824 | _ = self.os_data.ref_count.incr(); |
| | 825 | } |
| | 826 | |
| | 827 | fn deref(self: *Self) void { |
| | 828 | if (self.os_data.ref_count.decr() == 1) { |
| | 829 | const allocator = self.channel.loop.allocator; |
| | 830 | self.os_data.table_lock.deinit(); |
| | 831 | var it = self.os_data.dir_table.iterator(); |
| | 832 | while (it.next()) |entry| { |
| | 833 | allocator.free(entry.key); |
| | 834 | // TODO why does freeing this memory crash the test? |
| | 835 | //allocator.destroy(entry.value); |
| | 836 | } |
| | 837 | self.os_data.dir_table.deinit(); |
| | 838 | self.channel.destroy(); |
| | 839 | allocator.destroy(self); |
| | 840 | } |
| | 841 | } |
| | 842 | |
| 777 | pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V { | 843 | pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V { |
| 778 | switch (builtin.os) { | 844 | switch (builtin.os) { |
| 779 | builtin.Os.macosx => return await (async addFileMacosx(self, file_path, value) catch unreachable), | 845 | builtin.Os.macosx => return await (async addFileMacosx(self, file_path, value) catch unreachable), |
| 780 | builtin.Os.linux => return await (async addFileLinux(self, file_path, value) catch unreachable), | 846 | builtin.Os.linux => return await (async addFileLinux(self, file_path, value) catch unreachable), |
| | 847 | builtin.Os.windows => return await (async addFileWindows(self, file_path, value) catch unreachable), |
| 781 | else => @compileError("Unsupported OS"), | 848 | else => @compileError("Unsupported OS"), |
| 782 | } | 849 | } |
| 783 | } | 850 | } |
| ... | @@ -874,6 +941,8 @@ pub fn Watch(comptime V: type) type { | ... | @@ -874,6 +941,8 @@ pub fn Watch(comptime V: type) type { |
| 874 | } | 941 | } |
| 875 | | 942 | |
| 876 | async fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V { | 943 | async fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V { |
| | 944 | const value_copy = value; |
| | 945 | |
| 877 | const dirname = os.path.dirname(file_path) orelse "."; | 946 | const dirname = os.path.dirname(file_path) orelse "."; |
| 878 | const dirname_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, dirname); | 947 | const dirname_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, dirname); |
| 879 | var dirname_with_null_consumed = false; | 948 | var dirname_with_null_consumed = false; |
| ... | @@ -896,7 +965,7 @@ pub fn Watch(comptime V: type) type { | ... | @@ -896,7 +965,7 @@ pub fn Watch(comptime V: type) type { |
| 896 | | 965 | |
| 897 | const gop = try self.os_data.wd_table.getOrPut(wd); | 966 | const gop = try self.os_data.wd_table.getOrPut(wd); |
| 898 | if (!gop.found_existing) { | 967 | if (!gop.found_existing) { |
| 899 | gop.kv.value = Dir{ | 968 | gop.kv.value = OsData.Dir{ |
| 900 | .dirname = dirname_with_null, | 969 | .dirname = dirname_with_null, |
| 901 | .file_table = OsData.FileTable.init(self.channel.loop.allocator), | 970 | .file_table = OsData.FileTable.init(self.channel.loop.allocator), |
| 902 | }; | 971 | }; |
| ... | @@ -907,15 +976,201 @@ pub fn Watch(comptime V: type) type { | ... | @@ -907,15 +976,201 @@ pub fn Watch(comptime V: type) type { |
| 907 | const file_table_gop = try dir.file_table.getOrPut(basename_with_null); | 976 | const file_table_gop = try dir.file_table.getOrPut(basename_with_null); |
| 908 | if (file_table_gop.found_existing) { | 977 | if (file_table_gop.found_existing) { |
| 909 | const prev_value = file_table_gop.kv.value; | 978 | const prev_value = file_table_gop.kv.value; |
| 910 | file_table_gop.kv.value = value; | 979 | file_table_gop.kv.value = value_copy; |
| 911 | return prev_value; | 980 | return prev_value; |
| 912 | } else { | 981 | } else { |
| 913 | file_table_gop.kv.value = value; | 982 | file_table_gop.kv.value = value_copy; |
| 914 | basename_with_null_consumed = true; | 983 | basename_with_null_consumed = true; |
| 915 | return null; | 984 | return null; |
| 916 | } | 985 | } |
| 917 | } | 986 | } |
| 918 | | 987 | |
| | 988 | async fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V { |
| | 989 | const value_copy = value; |
| | 990 | // TODO we might need to convert dirname and basename to canonical file paths ("short"?) |
| | 991 | |
| | 992 | const dirname = try std.mem.dupe(self.channel.loop.allocator, u8, os.path.dirname(file_path) orelse "."); |
| | 993 | var dirname_consumed = false; |
| | 994 | defer if (!dirname_consumed) self.channel.loop.allocator.free(dirname); |
| | 995 | |
| | 996 | const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, dirname); |
| | 997 | defer self.channel.loop.allocator.free(dirname_utf16le); |
| | 998 | |
| | 999 | // TODO https://github.com/ziglang/zig/issues/265 |
| | 1000 | const basename = os.path.basename(file_path); |
| | 1001 | const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename); |
| | 1002 | var basename_utf16le_null_consumed = false; |
| | 1003 | defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null); |
| | 1004 | const basename_utf16le_no_null = basename_utf16le_null[0..basename_utf16le_null.len-1]; |
| | 1005 | |
| | 1006 | const dir_handle = windows.CreateFileW( |
| | 1007 | dirname_utf16le.ptr, |
| | 1008 | windows.FILE_LIST_DIRECTORY, |
| | 1009 | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE, |
| | 1010 | null, |
| | 1011 | windows.OPEN_EXISTING, |
| | 1012 | windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED, |
| | 1013 | null, |
| | 1014 | ); |
| | 1015 | if (dir_handle == windows.INVALID_HANDLE_VALUE) { |
| | 1016 | const err = windows.GetLastError(); |
| | 1017 | switch (err) { |
| | 1018 | windows.ERROR.FILE_NOT_FOUND, |
| | 1019 | windows.ERROR.PATH_NOT_FOUND, |
| | 1020 | => return error.PathNotFound, |
| | 1021 | else => return os.unexpectedErrorWindows(err), |
| | 1022 | } |
| | 1023 | } |
| | 1024 | var dir_handle_consumed = false; |
| | 1025 | defer if (!dir_handle_consumed) os.close(dir_handle); |
| | 1026 | |
| | 1027 | const held = await (async self.os_data.table_lock.acquire() catch unreachable); |
| | 1028 | defer held.release(); |
| | 1029 | |
| | 1030 | const gop = try self.os_data.dir_table.getOrPut(dirname); |
| | 1031 | if (gop.found_existing) { |
| | 1032 | const dir = gop.kv.value; |
| | 1033 | const held_dir_lock = await (async dir.table_lock.acquire() catch unreachable); |
| | 1034 | defer held_dir_lock.release(); |
| | 1035 | |
| | 1036 | const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null); |
| | 1037 | if (file_gop.found_existing) { |
| | 1038 | const prev_value = file_gop.kv.value; |
| | 1039 | file_gop.kv.value = value_copy; |
| | 1040 | return prev_value; |
| | 1041 | } else { |
| | 1042 | file_gop.kv.value = value_copy; |
| | 1043 | basename_utf16le_null_consumed = true; |
| | 1044 | return null; |
| | 1045 | } |
| | 1046 | } else { |
| | 1047 | errdefer _ = self.os_data.dir_table.remove(dirname); |
| | 1048 | const dir = try self.channel.loop.allocator.createOne(OsData.Dir); |
| | 1049 | errdefer self.channel.loop.allocator.destroy(dir); |
| | 1050 | |
| | 1051 | dir.* = OsData.Dir{ |
| | 1052 | .file_table = OsData.FileTable.init(self.channel.loop.allocator), |
| | 1053 | .table_lock = event.Lock.init(self.channel.loop), |
| | 1054 | .putter = undefined, |
| | 1055 | }; |
| | 1056 | assert((try dir.file_table.put(basename_utf16le_no_null, value_copy)) == null); |
| | 1057 | basename_utf16le_null_consumed = true; |
| | 1058 | |
| | 1059 | dir.putter = try async self.windowsDirReader(dir_handle, dir); |
| | 1060 | dir_handle_consumed = true; |
| | 1061 | |
| | 1062 | dirname_consumed = true; |
| | 1063 | |
| | 1064 | return null; |
| | 1065 | } |
| | 1066 | } |
| | 1067 | |
| | 1068 | async fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void { |
| | 1069 | // TODO https://github.com/ziglang/zig/issues/1194 |
| | 1070 | suspend { |
| | 1071 | resume @handle(); |
| | 1072 | } |
| | 1073 | |
| | 1074 | self.ref(); |
| | 1075 | defer self.deref(); |
| | 1076 | |
| | 1077 | defer os.close(dir_handle); |
| | 1078 | |
| | 1079 | var putter_node = std.atomic.Queue(promise).Node{ |
| | 1080 | .data = @handle(), |
| | 1081 | .prev = null, |
| | 1082 | .next = null, |
| | 1083 | }; |
| | 1084 | self.os_data.all_putters.put(&putter_node); |
| | 1085 | defer _ = self.os_data.all_putters.remove(&putter_node); |
| | 1086 | |
| | 1087 | var resume_node = Loop.ResumeNode.Basic{ |
| | 1088 | .base = Loop.ResumeNode{ |
| | 1089 | .id = Loop.ResumeNode.Id.Basic, |
| | 1090 | .handle = @handle(), |
| | 1091 | }, |
| | 1092 | }; |
| | 1093 | const completion_key = @ptrToInt(&resume_node.base); |
| | 1094 | var overlapped = windows.OVERLAPPED{ |
| | 1095 | .Internal = 0, |
| | 1096 | .InternalHigh = 0, |
| | 1097 | .Offset = 0, |
| | 1098 | .OffsetHigh = 0, |
| | 1099 | .hEvent = null, |
| | 1100 | }; |
| | 1101 | var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined; |
| | 1102 | |
| | 1103 | while (true) { |
| | 1104 | _ = os.windowsCreateIoCompletionPort( |
| | 1105 | dir_handle, self.channel.loop.os_data.io_port, completion_key, undefined, |
| | 1106 | ) catch |err| { |
| | 1107 | await (async self.channel.put(err) catch unreachable); |
| | 1108 | return; |
| | 1109 | }; |
| | 1110 | { |
| | 1111 | // TODO only 1 beginOneEvent for the whole coroutine |
| | 1112 | self.channel.loop.beginOneEvent(); |
| | 1113 | errdefer self.channel.loop.finishOneEvent(); |
| | 1114 | suspend { |
| | 1115 | _ = windows.ReadDirectoryChangesW( |
| | 1116 | dir_handle, |
| | 1117 | &event_buf, |
| | 1118 | @intCast(windows.DWORD, event_buf.len), |
| | 1119 | windows.FALSE, // watch subtree |
| | 1120 | windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME | |
| | 1121 | windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE | |
| | 1122 | windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS | |
| | 1123 | windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY, |
| | 1124 | null, // number of bytes transferred (unused for async) |
| | 1125 | &overlapped, |
| | 1126 | null, // completion routine - unused because we use IOCP |
| | 1127 | ); |
| | 1128 | } |
| | 1129 | } |
| | 1130 | var bytes_transferred: windows.DWORD = undefined; |
| | 1131 | if (windows.GetOverlappedResult(dir_handle, &overlapped, &bytes_transferred, windows.FALSE) == 0) { |
| | 1132 | const errno = windows.GetLastError(); |
| | 1133 | const err = switch (errno) { |
| | 1134 | else => os.unexpectedErrorWindows(errno), |
| | 1135 | }; |
| | 1136 | await (async self.channel.put(err) catch unreachable); |
| | 1137 | } else { |
| | 1138 | // can't use @bytesToSlice because of the special variable length name field |
| | 1139 | var ptr = event_buf[0..].ptr; |
| | 1140 | const end_ptr = ptr + bytes_transferred; |
| | 1141 | var ev: *windows.FILE_NOTIFY_INFORMATION = undefined; |
| | 1142 | while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += ev.NextEntryOffset) { |
| | 1143 | ev = @ptrCast(*windows.FILE_NOTIFY_INFORMATION, ptr); |
| | 1144 | const emit = switch (ev.Action) { |
| | 1145 | windows.FILE_ACTION_REMOVED => WatchEventId.Delete, |
| | 1146 | windows.FILE_ACTION_MODIFIED => WatchEventId.CloseWrite, |
| | 1147 | else => null, |
| | 1148 | }; |
| | 1149 | if (emit) |id| { |
| | 1150 | const basename_utf16le = ([*]u16)(&ev.FileName)[0..ev.FileNameLength/2]; |
| | 1151 | const user_value = blk: { |
| | 1152 | const held = await (async dir.table_lock.acquire() catch unreachable); |
| | 1153 | defer held.release(); |
| | 1154 | |
| | 1155 | if (dir.file_table.get(basename_utf16le)) |entry| { |
| | 1156 | break :blk entry.value; |
| | 1157 | } else { |
| | 1158 | break :blk null; |
| | 1159 | } |
| | 1160 | }; |
| | 1161 | if (user_value) |v| { |
| | 1162 | await (async self.channel.put(Event{ |
| | 1163 | .id = id, |
| | 1164 | .data = v, |
| | 1165 | }) catch unreachable); |
| | 1166 | } |
| | 1167 | } |
| | 1168 | if (ev.NextEntryOffset == 0) break; |
| | 1169 | } |
| | 1170 | } |
| | 1171 | } |
| | 1172 | } |
| | 1173 | |
| 919 | pub async fn removeFile(self: *Self, file_path: []const u8) ?V { | 1174 | pub async fn removeFile(self: *Self, file_path: []const u8) ?V { |
| 920 | @panic("TODO"); | 1175 | @panic("TODO"); |
| 921 | } | 1176 | } |
| ... | @@ -933,7 +1188,7 @@ pub fn Watch(comptime V: type) type { | ... | @@ -933,7 +1188,7 @@ pub fn Watch(comptime V: type) type { |
| 933 | .os_data = OsData{ | 1188 | .os_data = OsData{ |
| 934 | .putter = @handle(), | 1189 | .putter = @handle(), |
| 935 | .inotify_fd = inotify_fd, | 1190 | .inotify_fd = inotify_fd, |
| 936 | .wd_table = WdTable.init(loop.allocator), | 1191 | .wd_table = OsData.WdTable.init(loop.allocator), |
| 937 | .table_lock = event.Lock.init(loop), | 1192 | .table_lock = event.Lock.init(loop), |
| 938 | }, | 1193 | }, |
| 939 | }; | 1194 | }; |
| ... | @@ -1065,15 +1320,15 @@ async fn testFsWatch(loop: *Loop) !void { | ... | @@ -1065,15 +1320,15 @@ async fn testFsWatch(loop: *Loop) !void { |
| 1065 | const read_contents = try await try async readFile(loop, file_path, 1024 * 1024); | 1320 | const read_contents = try await try async readFile(loop, file_path, 1024 * 1024); |
| 1066 | assert(mem.eql(u8, read_contents, contents)); | 1321 | assert(mem.eql(u8, read_contents, contents)); |
| 1067 | | 1322 | |
| 1068 | //// now watch the file | 1323 | // now watch the file |
| 1069 | //var watch = try Watch(void).create(loop, 0); | 1324 | var watch = try Watch(void).create(loop, 0); |
| 1070 | //defer watch.destroy(); | 1325 | defer watch.destroy(); |
| 1071 | | 1326 | |
| 1072 | //assert((try await try async watch.addFile(file_path, {})) == null); | 1327 | assert((try await try async watch.addFile(file_path, {})) == null); |
| 1073 | | 1328 | |
| 1074 | //const ev = try async watch.channel.get(); | 1329 | const ev = try async watch.channel.get(); |
| 1075 | //var ev_consumed = false; | 1330 | var ev_consumed = false; |
| 1076 | //defer if (!ev_consumed) cancel ev; | 1331 | defer if (!ev_consumed) cancel ev; |
| 1077 | | 1332 | |
| 1078 | // overwrite line 2 | 1333 | // overwrite line 2 |
| 1079 | const fd = try await try async openReadWrite(loop, file_path, os.File.default_mode); | 1334 | const fd = try await try async openReadWrite(loop, file_path, os.File.default_mode); |
| ... | @@ -1083,11 +1338,11 @@ async fn testFsWatch(loop: *Loop) !void { | ... | @@ -1083,11 +1338,11 @@ async fn testFsWatch(loop: *Loop) !void { |
| 1083 | try await try async pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset); | 1338 | try await try async pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset); |
| 1084 | } | 1339 | } |
| 1085 | | 1340 | |
| 1086 | //ev_consumed = true; | 1341 | ev_consumed = true; |
| 1087 | //switch ((try await ev).id) { | 1342 | switch ((try await ev).id) { |
| 1088 | // WatchEventId.CloseWrite => {}, | 1343 | WatchEventId.CloseWrite => {}, |
| 1089 | // WatchEventId.Delete => @panic("wrong event"), | 1344 | WatchEventId.Delete => @panic("wrong event"), |
| 1090 | //} | 1345 | } |
| 1091 | | 1346 | |
| 1092 | const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024); | 1347 | const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024); |
| 1093 | assert(mem.eql(u8, contents_updated, | 1348 | assert(mem.eql(u8, contents_updated, |