authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-08 16:41:38-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-08 16:41:38-04:00
logda56959a9a7dd7b83a8d2bc6b1454ae546a48be6
tree3d069358f719eb604aa527597dbcc3215d4829a9
parent93840f8610974109d129e6940a851c1f7a8c9fce
signature Commit is signed but in an unrecognized format.

closer to std lib event stuff working


3 files changed, 642 insertions(+), 654 deletions(-)

std/event/channel.zig+13-33
...@@ -89,12 +89,7 @@ pub fn Channel(comptime T: type) type {...@@ -89,12 +89,7 @@ pub fn Channel(comptime T: type) type {
89 /// puts a data item in the channel. The promise completes when the value has been added to the89 /// puts a data item in the channel. The promise completes when the value has been added to the
90 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.90 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
91 pub async fn put(self: *SelfChannel, data: T) void {91 pub async fn put(self: *SelfChannel, data: T) void {
92 // TODO fix this workaround92 var my_tick_node = Loop.NextTickNode.init(@frame());
93 suspend {
94 resume @handle();
95 }
96
97 var my_tick_node = Loop.NextTickNode.init(@handle());
98 var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{93 var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{
99 .tick_node = &my_tick_node,94 .tick_node = &my_tick_node,
100 .data = data,95 .data = data,
...@@ -122,15 +117,10 @@ pub fn Channel(comptime T: type) type {...@@ -122,15 +117,10 @@ pub fn Channel(comptime T: type) type {
122 /// await this function to get an item from the channel. If the buffer is empty, the promise will117 /// await this function to get an item from the channel. If the buffer is empty, the promise will
123 /// complete when the next item is put in the channel.118 /// complete when the next item is put in the channel.
124 pub async fn get(self: *SelfChannel) T {119 pub async fn get(self: *SelfChannel) T {
125 // TODO fix this workaround
126 suspend {
127 resume @handle();
128 }
129
130 // TODO integrate this function with named return values120 // TODO integrate this function with named return values
131 // so we can get rid of this extra result copy121 // so we can get rid of this extra result copy
132 var result: T = undefined;122 var result: T = undefined;
133 var my_tick_node = Loop.NextTickNode.init(@handle());123 var my_tick_node = Loop.NextTickNode.init(@frame());
134 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{124 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
135 .tick_node = &my_tick_node,125 .tick_node = &my_tick_node,
136 .data = GetNode.Data{126 .data = GetNode.Data{
...@@ -173,15 +163,10 @@ pub fn Channel(comptime T: type) type {...@@ -173,15 +163,10 @@ pub fn Channel(comptime T: type) type {
173 /// Await is necessary for locking purposes. The function will be resumed after checking the channel163 /// Await is necessary for locking purposes. The function will be resumed after checking the channel
174 /// for data and will not wait for data to be available.164 /// for data and will not wait for data to be available.
175 pub async fn getOrNull(self: *SelfChannel) ?T {165 pub async fn getOrNull(self: *SelfChannel) ?T {
176 // TODO fix this workaround
177 suspend {
178 resume @handle();
179 }
180
181 // TODO integrate this function with named return values166 // TODO integrate this function with named return values
182 // so we can get rid of this extra result copy167 // so we can get rid of this extra result copy
183 var result: ?T = null;168 var result: ?T = null;
184 var my_tick_node = Loop.NextTickNode.init(@handle());169 var my_tick_node = Loop.NextTickNode.init(@frame());
185 var or_null_node = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node.init(undefined);170 var or_null_node = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node.init(undefined);
186 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{171 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
187 .tick_node = &my_tick_node,172 .tick_node = &my_tick_node,
...@@ -334,41 +319,36 @@ test "std.event.Channel" {...@@ -334,41 +319,36 @@ test "std.event.Channel" {
334 const channel = try Channel(i32).create(&loop, 0);319 const channel = try Channel(i32).create(&loop, 0);
335 defer channel.destroy();320 defer channel.destroy();
336321
337 const handle = try async<allocator> testChannelGetter(&loop, channel);322 const handle = async testChannelGetter(&loop, channel);
338 defer cancel handle;323 const putter = async testChannelPutter(channel);
339
340 const putter = try async<allocator> testChannelPutter(channel);
341 defer cancel putter;
342324
343 loop.run();325 loop.run();
344}326}
345327
346async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {328async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {
347 errdefer @panic("test failed");329 const value1_promise = async channel.get();
348
349 const value1_promise = try async channel.get();
350 const value1 = await value1_promise;330 const value1 = await value1_promise;
351 testing.expect(value1 == 1234);331 testing.expect(value1 == 1234);
352332
353 const value2_promise = try async channel.get();333 const value2_promise = async channel.get();
354 const value2 = await value2_promise;334 const value2 = await value2_promise;
355 testing.expect(value2 == 4567);335 testing.expect(value2 == 4567);
356336
357 const value3_promise = try async channel.getOrNull();337 const value3_promise = async channel.getOrNull();
358 const value3 = await value3_promise;338 const value3 = await value3_promise;
359 testing.expect(value3 == null);339 testing.expect(value3 == null);
360340
361 const last_put = try async testPut(channel, 4444);341 const last_put = async testPut(channel, 4444);
362 const value4 = await try async channel.getOrNull();342 const value4 = channel.getOrNull();
363 testing.expect(value4.? == 4444);343 testing.expect(value4.? == 4444);
364 await last_put;344 await last_put;
365}345}
366346
367async fn testChannelPutter(channel: *Channel(i32)) void {347async fn testChannelPutter(channel: *Channel(i32)) void {
368 await (async channel.put(1234) catch @panic("out of memory"));348 channel.put(1234);
369 await (async channel.put(4567) catch @panic("out of memory"));349 channel.put(4567);
370}350}
371351
372async fn testPut(channel: *Channel(i32), value: i32) void {352async fn testPut(channel: *Channel(i32), value: i32) void {
373 await (async channel.put(value) catch @panic("out of memory"));353 channel.put(value);
374}354}
std/event/fs.zig+592-592
...@@ -715,594 +715,594 @@ pub const WatchEventId = enum {...@@ -715,594 +715,594 @@ pub const WatchEventId = enum {
715 Delete,715 Delete,
716};716};
717717
718pub const WatchEventError = error{718//pub const WatchEventError = error{
719 UserResourceLimitReached,719// UserResourceLimitReached,
720 SystemResources,720// SystemResources,
721 AccessDenied,721// AccessDenied,
722 Unexpected, // TODO remove this possibility722// Unexpected, // TODO remove this possibility
723};723//};
724724//
725pub fn Watch(comptime V: type) type {725//pub fn Watch(comptime V: type) type {
726 return struct {726// return struct {
727 channel: *event.Channel(Event.Error!Event),727// channel: *event.Channel(Event.Error!Event),
728 os_data: OsData,728// os_data: OsData,
729729//
730 const OsData = switch (builtin.os) {730// const OsData = switch (builtin.os) {
731 .macosx, .freebsd, .netbsd => struct {731// .macosx, .freebsd, .netbsd => struct {
732 file_table: FileTable,732// file_table: FileTable,
733 table_lock: event.Lock,733// table_lock: event.Lock,
734734//
735 const FileTable = std.AutoHashMap([]const u8, *Put);735// const FileTable = std.AutoHashMap([]const u8, *Put);
736 const Put = struct {736// const Put = struct {
737 putter: promise,737// putter: promise,
738 value_ptr: *V,738// value_ptr: *V,
739 };739// };
740 },740// },
741741//
742 .linux => LinuxOsData,742// .linux => LinuxOsData,
743 .windows => WindowsOsData,743// .windows => WindowsOsData,
744744//
745 else => @compileError("Unsupported OS"),745// else => @compileError("Unsupported OS"),
746 };746// };
747747//
748 const WindowsOsData = struct {748// const WindowsOsData = struct {
749 table_lock: event.Lock,749// table_lock: event.Lock,
750 dir_table: DirTable,750// dir_table: DirTable,
751 all_putters: std.atomic.Queue(promise),751// all_putters: std.atomic.Queue(promise),
752 ref_count: std.atomic.Int(usize),752// ref_count: std.atomic.Int(usize),
753753//
754 const DirTable = std.AutoHashMap([]const u8, *Dir);754// const DirTable = std.AutoHashMap([]const u8, *Dir);
755 const FileTable = std.AutoHashMap([]const u16, V);755// const FileTable = std.AutoHashMap([]const u16, V);
756756//
757 const Dir = struct {757// const Dir = struct {
758 putter: promise,758// putter: promise,
759 file_table: FileTable,759// file_table: FileTable,
760 table_lock: event.Lock,760// table_lock: event.Lock,
761 };761// };
762 };762// };
763763//
764 const LinuxOsData = struct {764// const LinuxOsData = struct {
765 putter: promise,765// putter: promise,
766 inotify_fd: i32,766// inotify_fd: i32,
767 wd_table: WdTable,767// wd_table: WdTable,
768 table_lock: event.Lock,768// table_lock: event.Lock,
769769//
770 const WdTable = std.AutoHashMap(i32, Dir);770// const WdTable = std.AutoHashMap(i32, Dir);
771 const FileTable = std.AutoHashMap([]const u8, V);771// const FileTable = std.AutoHashMap([]const u8, V);
772772//
773 const Dir = struct {773// const Dir = struct {
774 dirname: []const u8,774// dirname: []const u8,
775 file_table: FileTable,775// file_table: FileTable,
776 };776// };
777 };777// };
778778//
779 const FileToHandle = std.AutoHashMap([]const u8, promise);779// const FileToHandle = std.AutoHashMap([]const u8, promise);
780780//
781 const Self = @This();781// const Self = @This();
782782//
783 pub const Event = struct {783// pub const Event = struct {
784 id: Id,784// id: Id,
785 data: V,785// data: V,
786786//
787 pub const Id = WatchEventId;787// pub const Id = WatchEventId;
788 pub const Error = WatchEventError;788// pub const Error = WatchEventError;
789 };789// };
790790//
791 pub fn create(loop: *Loop, event_buf_count: usize) !*Self {791// pub fn create(loop: *Loop, event_buf_count: usize) !*Self {
792 const channel = try event.Channel(Self.Event.Error!Self.Event).create(loop, event_buf_count);792// const channel = try event.Channel(Self.Event.Error!Self.Event).create(loop, event_buf_count);
793 errdefer channel.destroy();793// errdefer channel.destroy();
794794//
795 switch (builtin.os) {795// switch (builtin.os) {
796 .linux => {796// .linux => {
797 const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);797// const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
798 errdefer os.close(inotify_fd);798// errdefer os.close(inotify_fd);
799799//
800 var result: *Self = undefined;800// var result: *Self = undefined;
801 _ = try async<loop.allocator> linuxEventPutter(inotify_fd, channel, &result);801// _ = try async<loop.allocator> linuxEventPutter(inotify_fd, channel, &result);
802 return result;802// return result;
803 },803// },
804804//
805 .windows => {805// .windows => {
806 const self = try loop.allocator.create(Self);806// const self = try loop.allocator.create(Self);
807 errdefer loop.allocator.destroy(self);807// errdefer loop.allocator.destroy(self);
808 self.* = Self{808// self.* = Self{
809 .channel = channel,809// .channel = channel,
810 .os_data = OsData{810// .os_data = OsData{
811 .table_lock = event.Lock.init(loop),811// .table_lock = event.Lock.init(loop),
812 .dir_table = OsData.DirTable.init(loop.allocator),812// .dir_table = OsData.DirTable.init(loop.allocator),
813 .ref_count = std.atomic.Int(usize).init(1),813// .ref_count = std.atomic.Int(usize).init(1),
814 .all_putters = std.atomic.Queue(promise).init(),814// .all_putters = std.atomic.Queue(promise).init(),
815 },815// },
816 };816// };
817 return self;817// return self;
818 },818// },
819819//
820 .macosx, .freebsd, .netbsd => {820// .macosx, .freebsd, .netbsd => {
821 const self = try loop.allocator.create(Self);821// const self = try loop.allocator.create(Self);
822 errdefer loop.allocator.destroy(self);822// errdefer loop.allocator.destroy(self);
823823//
824 self.* = Self{824// self.* = Self{
825 .channel = channel,825// .channel = channel,
826 .os_data = OsData{826// .os_data = OsData{
827 .table_lock = event.Lock.init(loop),827// .table_lock = event.Lock.init(loop),
828 .file_table = OsData.FileTable.init(loop.allocator),828// .file_table = OsData.FileTable.init(loop.allocator),
829 },829// },
830 };830// };
831 return self;831// return self;
832 },832// },
833 else => @compileError("Unsupported OS"),833// else => @compileError("Unsupported OS"),
834 }834// }
835 }835// }
836836//
837 /// All addFile calls and removeFile calls must have completed.837// /// All addFile calls and removeFile calls must have completed.
838 pub fn destroy(self: *Self) void {838// pub fn destroy(self: *Self) void {
839 switch (builtin.os) {839// switch (builtin.os) {
840 .macosx, .freebsd, .netbsd => {840// .macosx, .freebsd, .netbsd => {
841 // TODO we need to cancel the coroutines before destroying the lock841// // TODO we need to cancel the coroutines before destroying the lock
842 self.os_data.table_lock.deinit();842// self.os_data.table_lock.deinit();
843 var it = self.os_data.file_table.iterator();843// var it = self.os_data.file_table.iterator();
844 while (it.next()) |entry| {844// while (it.next()) |entry| {
845 cancel entry.value.putter;845// cancel entry.value.putter;
846 self.channel.loop.allocator.free(entry.key);846// self.channel.loop.allocator.free(entry.key);
847 }847// }
848 self.channel.destroy();848// self.channel.destroy();
849 },849// },
850 .linux => cancel self.os_data.putter,850// .linux => cancel self.os_data.putter,
851 .windows => {851// .windows => {
852 while (self.os_data.all_putters.get()) |putter_node| {852// while (self.os_data.all_putters.get()) |putter_node| {
853 cancel putter_node.data;853// cancel putter_node.data;
854 }854// }
855 self.deref();855// self.deref();
856 },856// },
857 else => @compileError("Unsupported OS"),857// else => @compileError("Unsupported OS"),
858 }858// }
859 }859// }
860860//
861 fn ref(self: *Self) void {861// fn ref(self: *Self) void {
862 _ = self.os_data.ref_count.incr();862// _ = self.os_data.ref_count.incr();
863 }863// }
864864//
865 fn deref(self: *Self) void {865// fn deref(self: *Self) void {
866 if (self.os_data.ref_count.decr() == 1) {866// if (self.os_data.ref_count.decr() == 1) {
867 const allocator = self.channel.loop.allocator;867// const allocator = self.channel.loop.allocator;
868 self.os_data.table_lock.deinit();868// self.os_data.table_lock.deinit();
869 var it = self.os_data.dir_table.iterator();869// var it = self.os_data.dir_table.iterator();
870 while (it.next()) |entry| {870// while (it.next()) |entry| {
871 allocator.free(entry.key);871// allocator.free(entry.key);
872 allocator.destroy(entry.value);872// allocator.destroy(entry.value);
873 }873// }
874 self.os_data.dir_table.deinit();874// self.os_data.dir_table.deinit();
875 self.channel.destroy();875// self.channel.destroy();
876 allocator.destroy(self);876// allocator.destroy(self);
877 }877// }
878 }878// }
879879//
880 pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V {880// pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
881 switch (builtin.os) {881// switch (builtin.os) {
882 .macosx, .freebsd, .netbsd => return await (async addFileKEvent(self, file_path, value) catch unreachable),882// .macosx, .freebsd, .netbsd => return await (async addFileKEvent(self, file_path, value) catch unreachable),
883 .linux => return await (async addFileLinux(self, file_path, value) catch unreachable),883// .linux => return await (async addFileLinux(self, file_path, value) catch unreachable),
884 .windows => return await (async addFileWindows(self, file_path, value) catch unreachable),884// .windows => return await (async addFileWindows(self, file_path, value) catch unreachable),
885 else => @compileError("Unsupported OS"),885// else => @compileError("Unsupported OS"),
886 }886// }
887 }887// }
888888//
889 async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V {889// async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V {
890 const resolved_path = try std.fs.path.resolve(self.channel.loop.allocator, [_][]const u8{file_path});890// const resolved_path = try std.fs.path.resolve(self.channel.loop.allocator, [_][]const u8{file_path});
891 var resolved_path_consumed = false;891// var resolved_path_consumed = false;
892 defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path);892// defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path);
893893//
894 var close_op = try CloseOperation.start(self.channel.loop);894// var close_op = try CloseOperation.start(self.channel.loop);
895 var close_op_consumed = false;895// var close_op_consumed = false;
896 defer if (!close_op_consumed) close_op.finish();896// defer if (!close_op_consumed) close_op.finish();
897897//
898 const flags = if (os.darwin.is_the_target) os.O_SYMLINK | os.O_EVTONLY else 0;898// const flags = if (os.darwin.is_the_target) os.O_SYMLINK | os.O_EVTONLY else 0;
899 const mode = 0;899// const mode = 0;
900 const fd = try await (async openPosix(self.channel.loop, resolved_path, flags, mode) catch unreachable);900// const fd = try await (async openPosix(self.channel.loop, resolved_path, flags, mode) catch unreachable);
901 close_op.setHandle(fd);901// close_op.setHandle(fd);
902902//
903 var put_data: *OsData.Put = undefined;903// var put_data: *OsData.Put = undefined;
904 const putter = try async self.kqPutEvents(close_op, value, &put_data);904// const putter = try async self.kqPutEvents(close_op, value, &put_data);
905 close_op_consumed = true;905// close_op_consumed = true;
906 errdefer cancel putter;906// errdefer cancel putter;
907907//
908 const result = blk: {908// const result = blk: {
909 const held = await (async self.os_data.table_lock.acquire() catch unreachable);909// const held = await (async self.os_data.table_lock.acquire() catch unreachable);
910 defer held.release();910// defer held.release();
911911//
912 const gop = try self.os_data.file_table.getOrPut(resolved_path);912// const gop = try self.os_data.file_table.getOrPut(resolved_path);
913 if (gop.found_existing) {913// if (gop.found_existing) {
914 const prev_value = gop.kv.value.value_ptr.*;914// const prev_value = gop.kv.value.value_ptr.*;
915 cancel gop.kv.value.putter;915// cancel gop.kv.value.putter;
916 gop.kv.value = put_data;916// gop.kv.value = put_data;
917 break :blk prev_value;917// break :blk prev_value;
918 } else {918// } else {
919 resolved_path_consumed = true;919// resolved_path_consumed = true;
920 gop.kv.value = put_data;920// gop.kv.value = put_data;
921 break :blk null;921// break :blk null;
922 }922// }
923 };923// };
924924//
925 return result;925// return result;
926 }926// }
927927//
928 async fn kqPutEvents(self: *Self, close_op: *CloseOperation, value: V, out_put: **OsData.Put) void {928// async fn kqPutEvents(self: *Self, close_op: *CloseOperation, value: V, out_put: **OsData.Put) void {
929 // TODO https://github.com/ziglang/zig/issues/1194929// // TODO https://github.com/ziglang/zig/issues/1194
930 suspend {930// suspend {
931 resume @handle();931// resume @handle();
932 }932// }
933933//
934 var value_copy = value;934// var value_copy = value;
935 var put = OsData.Put{935// var put = OsData.Put{
936 .putter = @handle(),936// .putter = @handle(),
937 .value_ptr = &value_copy,937// .value_ptr = &value_copy,
938 };938// };
939 out_put.* = &put;939// out_put.* = &put;
940 self.channel.loop.beginOneEvent();940// self.channel.loop.beginOneEvent();
941941//
942 defer {942// defer {
943 close_op.finish();943// close_op.finish();
944 self.channel.loop.finishOneEvent();944// self.channel.loop.finishOneEvent();
945 }945// }
946946//
947 while (true) {947// while (true) {
948 if (await (async self.channel.loop.bsdWaitKev(948// if (await (async self.channel.loop.bsdWaitKev(
949 @intCast(usize, close_op.getHandle()),949// @intCast(usize, close_op.getHandle()),
950 os.EVFILT_VNODE,950// os.EVFILT_VNODE,
951 os.NOTE_WRITE | os.NOTE_DELETE,951// os.NOTE_WRITE | os.NOTE_DELETE,
952 ) catch unreachable)) |kev| {952// ) catch unreachable)) |kev| {
953 // TODO handle EV_ERROR953// // TODO handle EV_ERROR
954 if (kev.fflags & os.NOTE_DELETE != 0) {954// if (kev.fflags & os.NOTE_DELETE != 0) {
955 await (async self.channel.put(Self.Event{955// await (async self.channel.put(Self.Event{
956 .id = Event.Id.Delete,956// .id = Event.Id.Delete,
957 .data = value_copy,957// .data = value_copy,
958 }) catch unreachable);958// }) catch unreachable);
959 } else if (kev.fflags & os.NOTE_WRITE != 0) {959// } else if (kev.fflags & os.NOTE_WRITE != 0) {
960 await (async self.channel.put(Self.Event{960// await (async self.channel.put(Self.Event{
961 .id = Event.Id.CloseWrite,961// .id = Event.Id.CloseWrite,
962 .data = value_copy,962// .data = value_copy,
963 }) catch unreachable);963// }) catch unreachable);
964 }964// }
965 } else |err| switch (err) {965// } else |err| switch (err) {
966 error.EventNotFound => unreachable,966// error.EventNotFound => unreachable,
967 error.ProcessNotFound => unreachable,967// error.ProcessNotFound => unreachable,
968 error.Overflow => unreachable,968// error.Overflow => unreachable,
969 error.AccessDenied, error.SystemResources => |casted_err| {969// error.AccessDenied, error.SystemResources => |casted_err| {
970 await (async self.channel.put(casted_err) catch unreachable);970// await (async self.channel.put(casted_err) catch unreachable);
971 },971// },
972 }972// }
973 }973// }
974 }974// }
975975//
976 async fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V {976// async fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V {
977 const value_copy = value;977// const value_copy = value;
978978//
979 const dirname = std.fs.path.dirname(file_path) orelse ".";979// const dirname = std.fs.path.dirname(file_path) orelse ".";
980 const dirname_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, dirname);980// const dirname_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, dirname);
981 var dirname_with_null_consumed = false;981// var dirname_with_null_consumed = false;
982 defer if (!dirname_with_null_consumed) self.channel.loop.allocator.free(dirname_with_null);982// defer if (!dirname_with_null_consumed) self.channel.loop.allocator.free(dirname_with_null);
983983//
984 const basename = std.fs.path.basename(file_path);984// const basename = std.fs.path.basename(file_path);
985 const basename_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, basename);985// const basename_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, basename);
986 var basename_with_null_consumed = false;986// var basename_with_null_consumed = false;
987 defer if (!basename_with_null_consumed) self.channel.loop.allocator.free(basename_with_null);987// defer if (!basename_with_null_consumed) self.channel.loop.allocator.free(basename_with_null);
988988//
989 const wd = try os.inotify_add_watchC(989// const wd = try os.inotify_add_watchC(
990 self.os_data.inotify_fd,990// self.os_data.inotify_fd,
991 dirname_with_null.ptr,991// dirname_with_null.ptr,
992 os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_EXCL_UNLINK,992// os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_EXCL_UNLINK,
993 );993// );
994 // wd is either a newly created watch or an existing one.994// // wd is either a newly created watch or an existing one.
995995//
996 const held = await (async self.os_data.table_lock.acquire() catch unreachable);996// const held = await (async self.os_data.table_lock.acquire() catch unreachable);
997 defer held.release();997// defer held.release();
998998//
999 const gop = try self.os_data.wd_table.getOrPut(wd);999// const gop = try self.os_data.wd_table.getOrPut(wd);
1000 if (!gop.found_existing) {1000// if (!gop.found_existing) {
1001 gop.kv.value = OsData.Dir{1001// gop.kv.value = OsData.Dir{
1002 .dirname = dirname_with_null,1002// .dirname = dirname_with_null,
1003 .file_table = OsData.FileTable.init(self.channel.loop.allocator),1003// .file_table = OsData.FileTable.init(self.channel.loop.allocator),
1004 };1004// };
1005 dirname_with_null_consumed = true;1005// dirname_with_null_consumed = true;
1006 }1006// }
1007 const dir = &gop.kv.value;1007// const dir = &gop.kv.value;
10081008//
1009 const file_table_gop = try dir.file_table.getOrPut(basename_with_null);1009// const file_table_gop = try dir.file_table.getOrPut(basename_with_null);
1010 if (file_table_gop.found_existing) {1010// if (file_table_gop.found_existing) {
1011 const prev_value = file_table_gop.kv.value;1011// const prev_value = file_table_gop.kv.value;
1012 file_table_gop.kv.value = value_copy;1012// file_table_gop.kv.value = value_copy;
1013 return prev_value;1013// return prev_value;
1014 } else {1014// } else {
1015 file_table_gop.kv.value = value_copy;1015// file_table_gop.kv.value = value_copy;
1016 basename_with_null_consumed = true;1016// basename_with_null_consumed = true;
1017 return null;1017// return null;
1018 }1018// }
1019 }1019// }
10201020//
1021 async fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {1021// async fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {
1022 const value_copy = value;1022// const value_copy = value;
1023 // TODO we might need to convert dirname and basename to canonical file paths ("short"?)1023// // TODO we might need to convert dirname and basename to canonical file paths ("short"?)
10241024//
1025 const dirname = try std.mem.dupe(self.channel.loop.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;1026// var dirname_consumed = false;
1027 defer if (!dirname_consumed) self.channel.loop.allocator.free(dirname);1027// defer if (!dirname_consumed) self.channel.loop.allocator.free(dirname);
10281028//
1029 const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, dirname);1029// const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, dirname);
1030 defer self.channel.loop.allocator.free(dirname_utf16le);1030// defer self.channel.loop.allocator.free(dirname_utf16le);
10311031//
1032 // TODO https://github.com/ziglang/zig/issues/2651032// // TODO https://github.com/ziglang/zig/issues/265
1033 const basename = std.fs.path.basename(file_path);1033// const basename = std.fs.path.basename(file_path);
1034 const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename);1034// const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename);
1035 var basename_utf16le_null_consumed = false;1035// var basename_utf16le_null_consumed = false;
1036 defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null);1036// defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null);
1037 const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];1037// const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
10381038//
1039 const dir_handle = try windows.CreateFileW(1039// const dir_handle = try windows.CreateFileW(
1040 dirname_utf16le.ptr,1040// dirname_utf16le.ptr,
1041 windows.FILE_LIST_DIRECTORY,1041// windows.FILE_LIST_DIRECTORY,
1042 windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE,1042// windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE,
1043 null,1043// null,
1044 windows.OPEN_EXISTING,1044// windows.OPEN_EXISTING,
1045 windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED,1045// windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED,
1046 null,1046// null,
1047 );1047// );
1048 var dir_handle_consumed = false;1048// var dir_handle_consumed = false;
1049 defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);1049// defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);
10501050//
1051 const held = await (async self.os_data.table_lock.acquire() catch unreachable);1051// const held = await (async self.os_data.table_lock.acquire() catch unreachable);
1052 defer held.release();1052// defer held.release();
10531053//
1054 const gop = try self.os_data.dir_table.getOrPut(dirname);1054// const gop = try self.os_data.dir_table.getOrPut(dirname);
1055 if (gop.found_existing) {1055// if (gop.found_existing) {
1056 const dir = gop.kv.value;1056// const dir = gop.kv.value;
1057 const held_dir_lock = await (async dir.table_lock.acquire() catch unreachable);1057// const held_dir_lock = await (async dir.table_lock.acquire() catch unreachable);
1058 defer held_dir_lock.release();1058// defer held_dir_lock.release();
10591059//
1060 const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null);1060// const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null);
1061 if (file_gop.found_existing) {1061// if (file_gop.found_existing) {
1062 const prev_value = file_gop.kv.value;1062// const prev_value = file_gop.kv.value;
1063 file_gop.kv.value = value_copy;1063// file_gop.kv.value = value_copy;
1064 return prev_value;1064// return prev_value;
1065 } else {1065// } else {
1066 file_gop.kv.value = value_copy;1066// file_gop.kv.value = value_copy;
1067 basename_utf16le_null_consumed = true;1067// basename_utf16le_null_consumed = true;
1068 return null;1068// return null;
1069 }1069// }
1070 } else {1070// } else {
1071 errdefer _ = self.os_data.dir_table.remove(dirname);1071// errdefer _ = self.os_data.dir_table.remove(dirname);
1072 const dir = try self.channel.loop.allocator.create(OsData.Dir);1072// const dir = try self.channel.loop.allocator.create(OsData.Dir);
1073 errdefer self.channel.loop.allocator.destroy(dir);1073// errdefer self.channel.loop.allocator.destroy(dir);
10741074//
1075 dir.* = OsData.Dir{1075// dir.* = OsData.Dir{
1076 .file_table = OsData.FileTable.init(self.channel.loop.allocator),1076// .file_table = OsData.FileTable.init(self.channel.loop.allocator),
1077 .table_lock = event.Lock.init(self.channel.loop),1077// .table_lock = event.Lock.init(self.channel.loop),
1078 .putter = undefined,1078// .putter = undefined,
1079 };1079// };
1080 gop.kv.value = dir;1080// gop.kv.value = dir;
1081 assert((try dir.file_table.put(basename_utf16le_no_null, value_copy)) == null);1081// assert((try dir.file_table.put(basename_utf16le_no_null, value_copy)) == null);
1082 basename_utf16le_null_consumed = true;1082// basename_utf16le_null_consumed = true;
10831083//
1084 dir.putter = try async self.windowsDirReader(dir_handle, dir);1084// dir.putter = try async self.windowsDirReader(dir_handle, dir);
1085 dir_handle_consumed = true;1085// dir_handle_consumed = true;
10861086//
1087 dirname_consumed = true;1087// dirname_consumed = true;
10881088//
1089 return null;1089// return null;
1090 }1090// }
1091 }1091// }
10921092//
1093 async fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void {1093// async fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void {
1094 // TODO https://github.com/ziglang/zig/issues/11941094// // TODO https://github.com/ziglang/zig/issues/1194
1095 suspend {1095// suspend {
1096 resume @handle();1096// resume @handle();
1097 }1097// }
10981098//
1099 self.ref();1099// self.ref();
1100 defer self.deref();1100// defer self.deref();
11011101//
1102 defer os.close(dir_handle);1102// defer os.close(dir_handle);
11031103//
1104 var putter_node = std.atomic.Queue(promise).Node{1104// var putter_node = std.atomic.Queue(promise).Node{
1105 .data = @handle(),1105// .data = @handle(),
1106 .prev = null,1106// .prev = null,
1107 .next = null,1107// .next = null,
1108 };1108// };
1109 self.os_data.all_putters.put(&putter_node);1109// self.os_data.all_putters.put(&putter_node);
1110 defer _ = self.os_data.all_putters.remove(&putter_node);1110// defer _ = self.os_data.all_putters.remove(&putter_node);
11111111//
1112 var resume_node = Loop.ResumeNode.Basic{1112// var resume_node = Loop.ResumeNode.Basic{
1113 .base = Loop.ResumeNode{1113// .base = Loop.ResumeNode{
1114 .id = Loop.ResumeNode.Id.Basic,1114// .id = Loop.ResumeNode.Id.Basic,
1115 .handle = @handle(),1115// .handle = @handle(),
1116 .overlapped = windows.OVERLAPPED{1116// .overlapped = windows.OVERLAPPED{
1117 .Internal = 0,1117// .Internal = 0,
1118 .InternalHigh = 0,1118// .InternalHigh = 0,
1119 .Offset = 0,1119// .Offset = 0,
1120 .OffsetHigh = 0,1120// .OffsetHigh = 0,
1121 .hEvent = null,1121// .hEvent = null,
1122 },1122// },
1123 },1123// },
1124 };1124// };
1125 var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined;1125// var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined;
11261126//
1127 // TODO handle this error not in the channel but in the setup1127// // TODO handle this error not in the channel but in the setup
1128 _ = windows.CreateIoCompletionPort(1128// _ = windows.CreateIoCompletionPort(
1129 dir_handle,1129// dir_handle,
1130 self.channel.loop.os_data.io_port,1130// self.channel.loop.os_data.io_port,
1131 undefined,1131// undefined,
1132 undefined,1132// undefined,
1133 ) catch |err| {1133// ) catch |err| {
1134 await (async self.channel.put(err) catch unreachable);1134// await (async self.channel.put(err) catch unreachable);
1135 return;1135// return;
1136 };1136// };
11371137//
1138 while (true) {1138// while (true) {
1139 {1139// {
1140 // TODO only 1 beginOneEvent for the whole coroutine1140// // TODO only 1 beginOneEvent for the whole coroutine
1141 self.channel.loop.beginOneEvent();1141// self.channel.loop.beginOneEvent();
1142 errdefer self.channel.loop.finishOneEvent();1142// errdefer self.channel.loop.finishOneEvent();
1143 errdefer {1143// errdefer {
1144 _ = windows.kernel32.CancelIoEx(dir_handle, &resume_node.base.overlapped);1144// _ = windows.kernel32.CancelIoEx(dir_handle, &resume_node.base.overlapped);
1145 }1145// }
1146 suspend {1146// suspend {
1147 _ = windows.kernel32.ReadDirectoryChangesW(1147// _ = windows.kernel32.ReadDirectoryChangesW(
1148 dir_handle,1148// dir_handle,
1149 &event_buf,1149// &event_buf,
1150 @intCast(windows.DWORD, event_buf.len),1150// @intCast(windows.DWORD, event_buf.len),
1151 windows.FALSE, // watch subtree1151// windows.FALSE, // watch subtree
1152 windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME |1152// windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME |
1153 windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE |1153// windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE |
1154 windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |1154// windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |
1155 windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,1155// windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,
1156 null, // number of bytes transferred (unused for async)1156// null, // number of bytes transferred (unused for async)
1157 &resume_node.base.overlapped,1157// &resume_node.base.overlapped,
1158 null, // completion routine - unused because we use IOCP1158// null, // completion routine - unused because we use IOCP
1159 );1159// );
1160 }1160// }
1161 }1161// }
1162 var bytes_transferred: windows.DWORD = undefined;1162// var bytes_transferred: windows.DWORD = undefined;
1163 if (windows.kernel32.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) {1163// if (windows.kernel32.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) {
1164 const err = switch (windows.kernel32.GetLastError()) {1164// const err = switch (windows.kernel32.GetLastError()) {
1165 else => |err| windows.unexpectedError(err),1165// else => |err| windows.unexpectedError(err),
1166 };1166// };
1167 await (async self.channel.put(err) catch unreachable);1167// await (async self.channel.put(err) catch unreachable);
1168 } else {1168// } else {
1169 // can't use @bytesToSlice because of the special variable length name field1169// // can't use @bytesToSlice because of the special variable length name field
1170 var ptr = event_buf[0..].ptr;1170// var ptr = event_buf[0..].ptr;
1171 const end_ptr = ptr + bytes_transferred;1171// const end_ptr = ptr + bytes_transferred;
1172 var ev: *windows.FILE_NOTIFY_INFORMATION = undefined;1172// var ev: *windows.FILE_NOTIFY_INFORMATION = undefined;
1173 while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += ev.NextEntryOffset) {1173// while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += ev.NextEntryOffset) {
1174 ev = @ptrCast(*windows.FILE_NOTIFY_INFORMATION, ptr);1174// ev = @ptrCast(*windows.FILE_NOTIFY_INFORMATION, ptr);
1175 const emit = switch (ev.Action) {1175// const emit = switch (ev.Action) {
1176 windows.FILE_ACTION_REMOVED => WatchEventId.Delete,1176// windows.FILE_ACTION_REMOVED => WatchEventId.Delete,
1177 windows.FILE_ACTION_MODIFIED => WatchEventId.CloseWrite,1177// windows.FILE_ACTION_MODIFIED => WatchEventId.CloseWrite,
1178 else => null,1178// else => null,
1179 };1179// };
1180 if (emit) |id| {1180// if (emit) |id| {
1181 const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2];1181// const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2];
1182 const user_value = blk: {1182// const user_value = blk: {
1183 const held = await (async dir.table_lock.acquire() catch unreachable);1183// const held = await (async dir.table_lock.acquire() catch unreachable);
1184 defer held.release();1184// defer held.release();
11851185//
1186 if (dir.file_table.get(basename_utf16le)) |entry| {1186// if (dir.file_table.get(basename_utf16le)) |entry| {
1187 break :blk entry.value;1187// break :blk entry.value;
1188 } else {1188// } else {
1189 break :blk null;1189// break :blk null;
1190 }1190// }
1191 };1191// };
1192 if (user_value) |v| {1192// if (user_value) |v| {
1193 await (async self.channel.put(Event{1193// await (async self.channel.put(Event{
1194 .id = id,1194// .id = id,
1195 .data = v,1195// .data = v,
1196 }) catch unreachable);1196// }) catch unreachable);
1197 }1197// }
1198 }1198// }
1199 if (ev.NextEntryOffset == 0) break;1199// if (ev.NextEntryOffset == 0) break;
1200 }1200// }
1201 }1201// }
1202 }1202// }
1203 }1203// }
12041204//
1205 pub async fn removeFile(self: *Self, file_path: []const u8) ?V {1205// pub async fn removeFile(self: *Self, file_path: []const u8) ?V {
1206 @panic("TODO");1206// @panic("TODO");
1207 }1207// }
12081208//
1209 async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event.Error!Event), out_watch: **Self) void {1209// async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event.Error!Event), out_watch: **Self) void {
1210 // TODO https://github.com/ziglang/zig/issues/11941210// // TODO https://github.com/ziglang/zig/issues/1194
1211 suspend {1211// suspend {
1212 resume @handle();1212// resume @handle();
1213 }1213// }
12141214//
1215 const loop = channel.loop;1215// const loop = channel.loop;
12161216//
1217 var watch = Self{1217// var watch = Self{
1218 .channel = channel,1218// .channel = channel,
1219 .os_data = OsData{1219// .os_data = OsData{
1220 .putter = @handle(),1220// .putter = @handle(),
1221 .inotify_fd = inotify_fd,1221// .inotify_fd = inotify_fd,
1222 .wd_table = OsData.WdTable.init(loop.allocator),1222// .wd_table = OsData.WdTable.init(loop.allocator),
1223 .table_lock = event.Lock.init(loop),1223// .table_lock = event.Lock.init(loop),
1224 },1224// },
1225 };1225// };
1226 out_watch.* = &watch;1226// out_watch.* = &watch;
12271227//
1228 loop.beginOneEvent();1228// loop.beginOneEvent();
12291229//
1230 defer {1230// defer {
1231 watch.os_data.table_lock.deinit();1231// watch.os_data.table_lock.deinit();
1232 var wd_it = watch.os_data.wd_table.iterator();1232// var wd_it = watch.os_data.wd_table.iterator();
1233 while (wd_it.next()) |wd_entry| {1233// while (wd_it.next()) |wd_entry| {
1234 var file_it = wd_entry.value.file_table.iterator();1234// var file_it = wd_entry.value.file_table.iterator();
1235 while (file_it.next()) |file_entry| {1235// while (file_it.next()) |file_entry| {
1236 loop.allocator.free(file_entry.key);1236// loop.allocator.free(file_entry.key);
1237 }1237// }
1238 loop.allocator.free(wd_entry.value.dirname);1238// loop.allocator.free(wd_entry.value.dirname);
1239 }1239// }
1240 loop.finishOneEvent();1240// loop.finishOneEvent();
1241 os.close(inotify_fd);1241// os.close(inotify_fd);
1242 channel.destroy();1242// channel.destroy();
1243 }1243// }
12441244//
1245 var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;1245// var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
12461246//
1247 while (true) {1247// while (true) {
1248 const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);1248// const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);
1249 const errno = os.linux.getErrno(rc);1249// const errno = os.linux.getErrno(rc);
1250 switch (errno) {1250// switch (errno) {
1251 0 => {1251// 0 => {
1252 // can't use @bytesToSlice because of the special variable length name field1252// // can't use @bytesToSlice because of the special variable length name field
1253 var ptr = event_buf[0..].ptr;1253// var ptr = event_buf[0..].ptr;
1254 const end_ptr = ptr + event_buf.len;1254// const end_ptr = ptr + event_buf.len;
1255 var ev: *os.linux.inotify_event = undefined;1255// var ev: *os.linux.inotify_event = undefined;
1256 while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) {1256// while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) {
1257 ev = @ptrCast(*os.linux.inotify_event, ptr);1257// ev = @ptrCast(*os.linux.inotify_event, ptr);
1258 if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {1258// if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
1259 const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);1259// const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);
1260 const basename_with_null = basename_ptr[0 .. std.mem.len(u8, basename_ptr) + 1];1260// const basename_with_null = basename_ptr[0 .. std.mem.len(u8, basename_ptr) + 1];
1261 const user_value = blk: {1261// const user_value = blk: {
1262 const held = await (async watch.os_data.table_lock.acquire() catch unreachable);1262// const held = await (async watch.os_data.table_lock.acquire() catch unreachable);
1263 defer held.release();1263// defer held.release();
12641264//
1265 const dir = &watch.os_data.wd_table.get(ev.wd).?.value;1265// const dir = &watch.os_data.wd_table.get(ev.wd).?.value;
1266 if (dir.file_table.get(basename_with_null)) |entry| {1266// if (dir.file_table.get(basename_with_null)) |entry| {
1267 break :blk entry.value;1267// break :blk entry.value;
1268 } else {1268// } else {
1269 break :blk null;1269// break :blk null;
1270 }1270// }
1271 };1271// };
1272 if (user_value) |v| {1272// if (user_value) |v| {
1273 await (async channel.put(Event{1273// await (async channel.put(Event{
1274 .id = WatchEventId.CloseWrite,1274// .id = WatchEventId.CloseWrite,
1275 .data = v,1275// .data = v,
1276 }) catch unreachable);1276// }) catch unreachable);
1277 }1277// }
1278 }1278// }
1279 }1279// }
1280 },1280// },
1281 os.linux.EINTR => continue,1281// os.linux.EINTR => continue,
1282 os.linux.EINVAL => unreachable,1282// os.linux.EINVAL => unreachable,
1283 os.linux.EFAULT => unreachable,1283// os.linux.EFAULT => unreachable,
1284 os.linux.EAGAIN => {1284// os.linux.EAGAIN => {
1285 (await (async loop.linuxWaitFd(1285// (await (async loop.linuxWaitFd(
1286 inotify_fd,1286// inotify_fd,
1287 os.linux.EPOLLET | os.linux.EPOLLIN,1287// os.linux.EPOLLET | os.linux.EPOLLIN,
1288 ) catch unreachable)) catch |err| {1288// ) catch unreachable)) catch |err| {
1289 const transformed_err = switch (err) {1289// const transformed_err = switch (err) {
1290 error.FileDescriptorAlreadyPresentInSet => unreachable,1290// error.FileDescriptorAlreadyPresentInSet => unreachable,
1291 error.OperationCausesCircularLoop => unreachable,1291// error.OperationCausesCircularLoop => unreachable,
1292 error.FileDescriptorNotRegistered => unreachable,1292// error.FileDescriptorNotRegistered => unreachable,
1293 error.FileDescriptorIncompatibleWithEpoll => unreachable,1293// error.FileDescriptorIncompatibleWithEpoll => unreachable,
1294 error.Unexpected => unreachable,1294// error.Unexpected => unreachable,
1295 else => |e| e,1295// else => |e| e,
1296 };1296// };
1297 await (async channel.put(transformed_err) catch unreachable);1297// await (async channel.put(transformed_err) catch unreachable);
1298 };1298// };
1299 },1299// },
1300 else => unreachable,1300// else => unreachable,
1301 }1301// }
1302 }1302// }
1303 }1303// }
1304 };1304// };
1305}1305//}
13061306
1307const test_tmp_dir = "std_event_fs_test";1307const test_tmp_dir = "std_event_fs_test";
13081308
...@@ -1397,11 +1397,11 @@ pub const OutStream = struct {...@@ -1397,11 +1397,11 @@ pub const OutStream = struct {
1397 };1397 };
1398 }1398 }
13991399
1400 async<*mem.Allocator> fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {1400 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
1401 const self = @fieldParentPtr(OutStream, "stream", out_stream);1401 const self = @fieldParentPtr(OutStream, "stream", out_stream);
1402 const offset = self.offset;1402 const offset = self.offset;
1403 self.offset += bytes.len;1403 self.offset += bytes.len;
1404 return await (async pwritev(self.loop, self.fd, [][]const u8{bytes}, offset) catch unreachable);1404 return pwritev(self.loop, self.fd, [][]const u8{bytes}, offset);
1405 }1405 }
1406};1406};
14071407
...@@ -1423,9 +1423,9 @@ pub const InStream = struct {...@@ -1423,9 +1423,9 @@ pub const InStream = struct {
1423 };1423 };
1424 }1424 }
14251425
1426 async<*mem.Allocator> fn readFn(in_stream: *Stream, bytes: []u8) Error!usize {1426 fn readFn(in_stream: *Stream, bytes: []u8) Error!usize {
1427 const self = @fieldParentPtr(InStream, "stream", in_stream);1427 const self = @fieldParentPtr(InStream, "stream", in_stream);
1428 const amt = try await (async preadv(self.loop, self.fd, [][]u8{bytes}, self.offset) catch unreachable);1428 const amt = try preadv(self.loop, self.fd, [][]u8{bytes}, self.offset);
1429 self.offset += amt;1429 self.offset += amt;
1430 return amt;1430 return amt;
1431 }1431 }
std/event/loop.zig+37-29
...@@ -98,9 +98,21 @@ pub const Loop = struct {...@@ -98,9 +98,21 @@ pub const Loop = struct {
98 };98 };
99 pub const instance: ?*Loop = if (@hasDecl(root, "event_loop")) root.event_loop else default_instance;99 pub const instance: ?*Loop = if (@hasDecl(root, "event_loop")) root.event_loop else default_instance;
100100
101 /// TODO copy elision / named return values so that the threads referencing *Loop
102 /// have the correct pointer value.
103 /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765
104 pub fn init(self: *Loop, allocator: *mem.Allocator) !void {
105 if (builtin.single_threaded) {
106 return self.initSingleThreaded(allocator);
107 } else {
108 return self.initMultiThreaded(allocator);
109 }
110 }
111
101 /// After initialization, call run().112 /// After initialization, call run().
102 /// TODO copy elision / named return values so that the threads referencing *Loop113 /// TODO copy elision / named return values so that the threads referencing *Loop
103 /// have the correct pointer value.114 /// have the correct pointer value.
115 /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765
104 pub fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void {116 pub fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void {
105 return self.initInternal(allocator, 1);117 return self.initInternal(allocator, 1);
106 }118 }
...@@ -110,6 +122,7 @@ pub const Loop = struct {...@@ -110,6 +122,7 @@ pub const Loop = struct {
110 /// After initialization, call run().122 /// After initialization, call run().
111 /// TODO copy elision / named return values so that the threads referencing *Loop123 /// TODO copy elision / named return values so that the threads referencing *Loop
112 /// have the correct pointer value.124 /// have the correct pointer value.
125 /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765
113 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {126 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
114 if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode");127 if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode");
115 const core_count = try Thread.cpuCount();128 const core_count = try Thread.cpuCount();
...@@ -161,18 +174,18 @@ pub const Loop = struct {...@@ -161,18 +174,18 @@ pub const Loop = struct {
161 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {174 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {
162 switch (builtin.os) {175 switch (builtin.os) {
163 .linux => {176 .linux => {
164 // TODO self.os_data.fs_queue = std.atomic.Queue(fs.Request).init();177 self.os_data.fs_queue = std.atomic.Queue(fs.Request).init();
165 // TODO self.os_data.fs_queue_item = 0;178 self.os_data.fs_queue_item = 0;
166 // TODO // we need another thread for the file system because Linux does not have an async179 // we need another thread for the file system because Linux does not have an async
167 // TODO // file system I/O API.180 // file system I/O API.
168 // TODO self.os_data.fs_end_request = fs.RequestNode{181 self.os_data.fs_end_request = fs.RequestNode{
169 // TODO .prev = undefined,182 .prev = undefined,
170 // TODO .next = undefined,183 .next = undefined,
171 // TODO .data = fs.Request{184 .data = fs.Request{
172 // TODO .msg = fs.Request.Msg.End,185 .msg = fs.Request.Msg.End,
173 // TODO .finish = fs.Request.Finish.NoAction,186 .finish = fs.Request.Finish.NoAction,
174 // TODO },187 },
175 // TODO };188 };
176189
177 errdefer {190 errdefer {
178 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);191 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
...@@ -210,10 +223,10 @@ pub const Loop = struct {...@@ -210,10 +223,10 @@ pub const Loop = struct {
210 &self.os_data.final_eventfd_event,223 &self.os_data.final_eventfd_event,
211 );224 );
212225
213 // TODO self.os_data.fs_thread = try Thread.spawn(self, posixFsRun);226 self.os_data.fs_thread = try Thread.spawn(self, posixFsRun);
214 errdefer {227 errdefer {
215 // TODO self.posixFsRequest(&self.os_data.fs_end_request);228 self.posixFsRequest(&self.os_data.fs_end_request);
216 // TODO self.os_data.fs_thread.wait();229 self.os_data.fs_thread.wait();
217 }230 }
218231
219 if (builtin.single_threaded) {232 if (builtin.single_threaded) {
...@@ -315,10 +328,10 @@ pub const Loop = struct {...@@ -315,10 +328,10 @@ pub const Loop = struct {
315 .udata = undefined,328 .udata = undefined,
316 };329 };
317330
318 // TODO self.os_data.fs_thread = try Thread.spawn(self, posixFsRun);331 self.os_data.fs_thread = try Thread.spawn(self, posixFsRun);
319 errdefer {332 errdefer {
320 // TODO self.posixFsRequest(&self.os_data.fs_end_request);333 self.posixFsRequest(&self.os_data.fs_end_request);
321 // TODO self.os_data.fs_thread.wait();334 self.os_data.fs_thread.wait();
322 }335 }
323336
324 if (builtin.single_threaded) {337 if (builtin.single_threaded) {
...@@ -441,7 +454,6 @@ pub const Loop = struct {...@@ -441,7 +454,6 @@ pub const Loop = struct {
441 pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {454 pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
442 defer self.linuxRemoveFd(fd);455 defer self.linuxRemoveFd(fd);
443 suspend {456 suspend {
444 // TODO explicitly put this memory in the coroutine frame #1194
445 var resume_node = ResumeNode.Basic{457 var resume_node = ResumeNode.Basic{
446 .base = ResumeNode{458 .base = ResumeNode{
447 .id = ResumeNode.Id.Basic,459 .id = ResumeNode.Id.Basic,
...@@ -454,10 +466,6 @@ pub const Loop = struct {...@@ -454,10 +466,6 @@ pub const Loop = struct {
454 }466 }
455467
456 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !os.Kevent {468 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !os.Kevent {
457 // TODO #1194
458 suspend {
459 resume @handle();
460 }
461 var resume_node = ResumeNode.Basic{469 var resume_node = ResumeNode.Basic{
462 .base = ResumeNode{470 .base = ResumeNode{
463 .id = ResumeNode.Id.Basic,471 .id = ResumeNode.Id.Basic,
...@@ -578,7 +586,7 @@ pub const Loop = struct {...@@ -578,7 +586,7 @@ pub const Loop = struct {
578 .macosx,586 .macosx,
579 .freebsd,587 .freebsd,
580 .netbsd,588 .netbsd,
581 => {}, // TODO self.os_data.fs_thread.wait(),589 => self.os_data.fs_thread.wait(),
582 else => {},590 else => {},
583 }591 }
584592
...@@ -631,7 +639,7 @@ pub const Loop = struct {...@@ -631,7 +639,7 @@ pub const Loop = struct {
631 // cause all the threads to stop639 // cause all the threads to stop
632 switch (builtin.os) {640 switch (builtin.os) {
633 .linux => {641 .linux => {
634 // TODO self.posixFsRequest(&self.os_data.fs_end_request);642 self.posixFsRequest(&self.os_data.fs_end_request);
635 // writing 8 bytes to an eventfd cannot fail643 // writing 8 bytes to an eventfd cannot fail
636 os.write(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;644 os.write(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
637 return;645 return;
...@@ -862,10 +870,10 @@ pub const Loop = struct {...@@ -862,10 +870,10 @@ pub const Loop = struct {
862 epollfd: i32,870 epollfd: i32,
863 final_eventfd: i32,871 final_eventfd: i32,
864 final_eventfd_event: os.linux.epoll_event,872 final_eventfd_event: os.linux.epoll_event,
865 // TODO fs_thread: *Thread,873 fs_thread: *Thread,
866 // TODO fs_queue_item: i32,874 fs_queue_item: i32,
867 // TODO fs_queue: std.atomic.Queue(fs.Request),875 fs_queue: std.atomic.Queue(fs.Request),
868 // TODO fs_end_request: fs.RequestNode,876 fs_end_request: fs.RequestNode,
869 };877 };
870};878};
871879