| author | |
| committer | |
| log | 178d69191ba008dffd70d2854df09cec556b59dd |
| tree | bfbe9686e9059c37f3a78f5d5af0f74d7bca4710 |
| parent | 0cfd019377c4e91924d8f57a4c7400a2d62f8751 |
when reading and writing the same file descriptors3 files changed, 67 insertions(+), 56 deletions(-)
src-self-hosted/main.zig+1-1| ... | @@ -737,7 +737,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void { | ... | @@ -737,7 +737,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void { |
| 737 | file_path, | 737 | file_path, |
| 738 | max_src_size, | 738 | max_src_size, |
| 739 | )) catch |err| switch (err) { | 739 | )) catch |err| switch (err) { |
| 740 | error.IsDir => { | 740 | error.IsDir, error.AccessDenied => { |
| 741 | // TODO make event based (and dir.next()) | 741 | // TODO make event based (and dir.next()) |
| 742 | var dir = try std.os.Dir.open(fmt.loop.allocator, file_path); | 742 | var dir = try std.os.Dir.open(fmt.loop.allocator, file_path); |
| 743 | defer dir.close(); | 743 | defer dir.close(); |
std/event/fs.zig+35-40| ... | @@ -109,30 +109,28 @@ pub async fn pwriteWindows(loop: *Loop, fd: os.FileHandle, data: []const u8, off | ... | @@ -109,30 +109,28 @@ pub async fn pwriteWindows(loop: *Loop, fd: os.FileHandle, data: []const u8, off |
| 109 | .base = Loop.ResumeNode{ | 109 | .base = Loop.ResumeNode{ |
| 110 | .id = Loop.ResumeNode.Id.Basic, | 110 | .id = Loop.ResumeNode.Id.Basic, |
| 111 | .handle = @handle(), | 111 | .handle = @handle(), |
| 112 | .overlapped = windows.OVERLAPPED{ | ||
| 113 | .Internal = 0, | ||
| 114 | .InternalHigh = 0, | ||
| 115 | .Offset = @truncate(u32, offset), | ||
| 116 | .OffsetHigh = @truncate(u32, offset >> 32), | ||
| 117 | .hEvent = null, | ||
| 118 | }, | ||
| 112 | }, | 119 | }, |
| 113 | }; | 120 | }; |
| 114 | const completion_key = @ptrToInt(&resume_node.base); | 121 | // TODO only call create io completion port once per fd |
| 115 | // TODO support concurrent async ops on the file handle | 122 | _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined); |
| 116 | // we can do this by ignoring completion key and using @fieldParentPtr with the *Overlapped | ||
| 117 | _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, completion_key, undefined); | ||
| 118 | var overlapped = windows.OVERLAPPED{ | ||
| 119 | .Internal = 0, | ||
| 120 | .InternalHigh = 0, | ||
| 121 | .Offset = @truncate(u32, offset), | ||
| 122 | .OffsetHigh = @truncate(u32, offset >> 32), | ||
| 123 | .hEvent = null, | ||
| 124 | }; | ||
| 125 | loop.beginOneEvent(); | 123 | loop.beginOneEvent(); |
| 126 | errdefer loop.finishOneEvent(); | 124 | errdefer loop.finishOneEvent(); |
| 127 | 125 | ||
| 128 | errdefer { | 126 | errdefer { |
| 129 | _ = windows.CancelIoEx(fd, &overlapped); | 127 | _ = windows.CancelIoEx(fd, &resume_node.base.overlapped); |
| 130 | } | 128 | } |
| 131 | suspend { | 129 | suspend { |
| 132 | _ = windows.WriteFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &overlapped); | 130 | _ = windows.WriteFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &resume_node.base.overlapped); |
| 133 | } | 131 | } |
| 134 | var bytes_transferred: windows.DWORD = undefined; | 132 | var bytes_transferred: windows.DWORD = undefined; |
| 135 | if (windows.GetOverlappedResult(fd, &overlapped, &bytes_transferred, windows.FALSE) == 0) { | 133 | if (windows.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { |
| 136 | const err = windows.GetLastError(); | 134 | const err = windows.GetLastError(); |
| 137 | return switch (err) { | 135 | return switch (err) { |
| 138 | windows.ERROR.IO_PENDING => unreachable, | 136 | windows.ERROR.IO_PENDING => unreachable, |
| ... | @@ -243,30 +241,28 @@ pub async fn preadWindows(loop: *Loop, fd: os.FileHandle, data: []u8, offset: u6 | ... | @@ -243,30 +241,28 @@ pub async fn preadWindows(loop: *Loop, fd: os.FileHandle, data: []u8, offset: u6 |
| 243 | .base = Loop.ResumeNode{ | 241 | .base = Loop.ResumeNode{ |
| 244 | .id = Loop.ResumeNode.Id.Basic, | 242 | .id = Loop.ResumeNode.Id.Basic, |
| 245 | .handle = @handle(), | 243 | .handle = @handle(), |
| 244 | .overlapped = windows.OVERLAPPED{ | ||
| 245 | .Internal = 0, | ||
| 246 | .InternalHigh = 0, | ||
| 247 | .Offset = @truncate(u32, offset), | ||
| 248 | .OffsetHigh = @truncate(u32, offset >> 32), | ||
| 249 | .hEvent = null, | ||
| 250 | }, | ||
| 246 | }, | 251 | }, |
| 247 | }; | 252 | }; |
| 248 | const completion_key = @ptrToInt(&resume_node.base); | 253 | // TODO only call create io completion port once per fd |
| 249 | // TODO support concurrent async ops on the file handle | 254 | _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined); |
| 250 | // we can do this by ignoring completion key and using @fieldParentPtr with the *Overlapped | ||
| 251 | _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, completion_key, undefined); | ||
| 252 | var overlapped = windows.OVERLAPPED{ | ||
| 253 | .Internal = 0, | ||
| 254 | .InternalHigh = 0, | ||
| 255 | .Offset = @truncate(u32, offset), | ||
| 256 | .OffsetHigh = @truncate(u32, offset >> 32), | ||
| 257 | .hEvent = null, | ||
| 258 | }; | ||
| 259 | loop.beginOneEvent(); | 255 | loop.beginOneEvent(); |
| 260 | errdefer loop.finishOneEvent(); | 256 | errdefer loop.finishOneEvent(); |
| 261 | 257 | ||
| 262 | errdefer { | 258 | errdefer { |
| 263 | _ = windows.CancelIoEx(fd, &overlapped); | 259 | _ = windows.CancelIoEx(fd, &resume_node.base.overlapped); |
| 264 | } | 260 | } |
| 265 | suspend { | 261 | suspend { |
| 266 | _ = windows.ReadFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &overlapped); | 262 | _ = windows.ReadFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &resume_node.base.overlapped); |
| 267 | } | 263 | } |
| 268 | var bytes_transferred: windows.DWORD = undefined; | 264 | var bytes_transferred: windows.DWORD = undefined; |
| 269 | if (windows.GetOverlappedResult(fd, &overlapped, &bytes_transferred, windows.FALSE) == 0) { | 265 | if (windows.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { |
| 270 | const err = windows.GetLastError(); | 266 | const err = windows.GetLastError(); |
| 271 | return switch (err) { | 267 | return switch (err) { |
| 272 | windows.ERROR.IO_PENDING => unreachable, | 268 | windows.ERROR.IO_PENDING => unreachable, |
| ... | @@ -1074,23 +1070,22 @@ pub fn Watch(comptime V: type) type { | ... | @@ -1074,23 +1070,22 @@ pub fn Watch(comptime V: type) type { |
| 1074 | .base = Loop.ResumeNode{ | 1070 | .base = Loop.ResumeNode{ |
| 1075 | .id = Loop.ResumeNode.Id.Basic, | 1071 | .id = Loop.ResumeNode.Id.Basic, |
| 1076 | .handle = @handle(), | 1072 | .handle = @handle(), |
| 1073 | .overlapped = windows.OVERLAPPED{ | ||
| 1074 | .Internal = 0, | ||
| 1075 | .InternalHigh = 0, | ||
| 1076 | .Offset = 0, | ||
| 1077 | .OffsetHigh = 0, | ||
| 1078 | .hEvent = null, | ||
| 1079 | }, | ||
| 1077 | }, | 1080 | }, |
| 1078 | }; | 1081 | }; |
| 1079 | const completion_key = @ptrToInt(&resume_node.base); | ||
| 1080 | var overlapped = windows.OVERLAPPED{ | ||
| 1081 | .Internal = 0, | ||
| 1082 | .InternalHigh = 0, | ||
| 1083 | .Offset = 0, | ||
| 1084 | .OffsetHigh = 0, | ||
| 1085 | .hEvent = null, | ||
| 1086 | }; | ||
| 1087 | var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined; | 1082 | var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined; |
| 1088 | 1083 | ||
| 1089 | // TODO handle this error not in the channel but in the setup | 1084 | // TODO handle this error not in the channel but in the setup |
| 1090 | _ = os.windowsCreateIoCompletionPort( | 1085 | _ = os.windowsCreateIoCompletionPort( |
| 1091 | dir_handle, | 1086 | dir_handle, |
| 1092 | self.channel.loop.os_data.io_port, | 1087 | self.channel.loop.os_data.io_port, |
| 1093 | completion_key, | 1088 | undefined, |
| 1094 | undefined, | 1089 | undefined, |
| 1095 | ) catch |err| { | 1090 | ) catch |err| { |
| 1096 | await (async self.channel.put(err) catch unreachable); | 1091 | await (async self.channel.put(err) catch unreachable); |
| ... | @@ -1103,7 +1098,7 @@ pub fn Watch(comptime V: type) type { | ... | @@ -1103,7 +1098,7 @@ pub fn Watch(comptime V: type) type { |
| 1103 | self.channel.loop.beginOneEvent(); | 1098 | self.channel.loop.beginOneEvent(); |
| 1104 | errdefer self.channel.loop.finishOneEvent(); | 1099 | errdefer self.channel.loop.finishOneEvent(); |
| 1105 | errdefer { | 1100 | errdefer { |
| 1106 | _ = windows.CancelIoEx(dir_handle, &overlapped); | 1101 | _ = windows.CancelIoEx(dir_handle, &resume_node.base.overlapped); |
| 1107 | } | 1102 | } |
| 1108 | suspend { | 1103 | suspend { |
| 1109 | _ = windows.ReadDirectoryChangesW( | 1104 | _ = windows.ReadDirectoryChangesW( |
| ... | @@ -1116,13 +1111,13 @@ pub fn Watch(comptime V: type) type { | ... | @@ -1116,13 +1111,13 @@ pub fn Watch(comptime V: type) type { |
| 1116 | windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS | | 1111 | windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS | |
| 1117 | windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY, | 1112 | windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY, |
| 1118 | null, // number of bytes transferred (unused for async) | 1113 | null, // number of bytes transferred (unused for async) |
| 1119 | &overlapped, | 1114 | &resume_node.base.overlapped, |
| 1120 | null, // completion routine - unused because we use IOCP | 1115 | null, // completion routine - unused because we use IOCP |
| 1121 | ); | 1116 | ); |
| 1122 | } | 1117 | } |
| 1123 | } | 1118 | } |
| 1124 | var bytes_transferred: windows.DWORD = undefined; | 1119 | var bytes_transferred: windows.DWORD = undefined; |
| 1125 | if (windows.GetOverlappedResult(dir_handle, &overlapped, &bytes_transferred, windows.FALSE) == 0) { | 1120 | if (windows.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { |
| 1126 | const errno = windows.GetLastError(); | 1121 | const errno = windows.GetLastError(); |
| 1127 | const err = switch (errno) { | 1122 | const err = switch (errno) { |
| 1128 | else => os.unexpectedErrorWindows(errno), | 1123 | else => os.unexpectedErrorWindows(errno), |
std/event/loop.zig+31-15| ... | @@ -27,6 +27,19 @@ pub const Loop = struct { | ... | @@ -27,6 +27,19 @@ pub const Loop = struct { |
| 27 | pub const ResumeNode = struct { | 27 | pub const ResumeNode = struct { |
| 28 | id: Id, | 28 | id: Id, |
| 29 | handle: promise, | 29 | handle: promise, |
| 30 | overlapped: Overlapped, | ||
| 31 | |||
| 32 | const overlapped_init = switch (builtin.os) { | ||
| 33 | builtin.Os.windows => windows.OVERLAPPED{ | ||
| 34 | .Internal = 0, | ||
| 35 | .InternalHigh = 0, | ||
| 36 | .Offset = 0, | ||
| 37 | .OffsetHigh = 0, | ||
| 38 | .hEvent = null, | ||
| 39 | }, | ||
| 40 | else => {}, | ||
| 41 | }; | ||
| 42 | const Overlapped = @typeOf(overlapped_init); | ||
| 30 | 43 | ||
| 31 | pub const Id = enum { | 44 | pub const Id = enum { |
| 32 | Basic, | 45 | Basic, |
| ... | @@ -101,6 +114,7 @@ pub const Loop = struct { | ... | @@ -101,6 +114,7 @@ pub const Loop = struct { |
| 101 | .final_resume_node = ResumeNode{ | 114 | .final_resume_node = ResumeNode{ |
| 102 | .id = ResumeNode.Id.Stop, | 115 | .id = ResumeNode.Id.Stop, |
| 103 | .handle = undefined, | 116 | .handle = undefined, |
| 117 | .overlapped = ResumeNode.overlapped_init, | ||
| 104 | }, | 118 | }, |
| 105 | }; | 119 | }; |
| 106 | const extra_thread_count = thread_count - 1; | 120 | const extra_thread_count = thread_count - 1; |
| ... | @@ -153,6 +167,7 @@ pub const Loop = struct { | ... | @@ -153,6 +167,7 @@ pub const Loop = struct { |
| 153 | .base = ResumeNode{ | 167 | .base = ResumeNode{ |
| 154 | .id = ResumeNode.Id.EventFd, | 168 | .id = ResumeNode.Id.EventFd, |
| 155 | .handle = undefined, | 169 | .handle = undefined, |
| 170 | .overlapped = ResumeNode.overlapped_init, | ||
| 156 | }, | 171 | }, |
| 157 | .eventfd = try os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK), | 172 | .eventfd = try os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK), |
| 158 | .epoll_op = posix.EPOLL_CTL_ADD, | 173 | .epoll_op = posix.EPOLL_CTL_ADD, |
| ... | @@ -225,6 +240,7 @@ pub const Loop = struct { | ... | @@ -225,6 +240,7 @@ pub const Loop = struct { |
| 225 | .base = ResumeNode{ | 240 | .base = ResumeNode{ |
| 226 | .id = ResumeNode.Id.EventFd, | 241 | .id = ResumeNode.Id.EventFd, |
| 227 | .handle = undefined, | 242 | .handle = undefined, |
| 243 | .overlapped = ResumeNode.overlapped_init, | ||
| 228 | }, | 244 | }, |
| 229 | // this one is for sending events | 245 | // this one is for sending events |
| 230 | .kevent = posix.Kevent{ | 246 | .kevent = posix.Kevent{ |
| ... | @@ -311,6 +327,7 @@ pub const Loop = struct { | ... | @@ -311,6 +327,7 @@ pub const Loop = struct { |
| 311 | .base = ResumeNode{ | 327 | .base = ResumeNode{ |
| 312 | .id = ResumeNode.Id.EventFd, | 328 | .id = ResumeNode.Id.EventFd, |
| 313 | .handle = undefined, | 329 | .handle = undefined, |
| 330 | .overlapped = ResumeNode.overlapped_init, | ||
| 314 | }, | 331 | }, |
| 315 | // this one is for sending events | 332 | // this one is for sending events |
| 316 | .completion_key = @ptrToInt(&eventfd_node.data.base), | 333 | .completion_key = @ptrToInt(&eventfd_node.data.base), |
| ... | @@ -325,8 +342,8 @@ pub const Loop = struct { | ... | @@ -325,8 +342,8 @@ pub const Loop = struct { |
| 325 | var i: usize = 0; | 342 | var i: usize = 0; |
| 326 | while (i < extra_thread_index) : (i += 1) { | 343 | while (i < extra_thread_index) : (i += 1) { |
| 327 | while (true) { | 344 | while (true) { |
| 328 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); | 345 | const overlapped = &self.final_resume_node.overlapped; |
| 329 | os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; | 346 | os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, undefined, overlapped) catch continue; |
| 330 | break; | 347 | break; |
| 331 | } | 348 | } |
| 332 | } | 349 | } |
| ... | @@ -413,6 +430,7 @@ pub const Loop = struct { | ... | @@ -413,6 +430,7 @@ pub const Loop = struct { |
| 413 | .base = ResumeNode{ | 430 | .base = ResumeNode{ |
| 414 | .id = ResumeNode.Id.Basic, | 431 | .id = ResumeNode.Id.Basic, |
| 415 | .handle = @handle(), | 432 | .handle = @handle(), |
| 433 | .overlapped = ResumeNode.overlapped_init, | ||
| 416 | }, | 434 | }, |
| 417 | .kev = undefined, | 435 | .kev = undefined, |
| 418 | }; | 436 | }; |
| ... | @@ -489,15 +507,11 @@ pub const Loop = struct { | ... | @@ -489,15 +507,11 @@ pub const Loop = struct { |
| 489 | }; | 507 | }; |
| 490 | }, | 508 | }, |
| 491 | builtin.Os.windows => { | 509 | builtin.Os.windows => { |
| 492 | // this value is never dereferenced but we need it to be non-null so that | ||
| 493 | // the consumer code can decide whether to read the completion key. | ||
| 494 | // it has to do this for normal I/O, so we match that behavior here. | ||
| 495 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); | ||
| 496 | os.windowsPostQueuedCompletionStatus( | 510 | os.windowsPostQueuedCompletionStatus( |
| 497 | self.os_data.io_port, | 511 | self.os_data.io_port, |
| 498 | undefined, | 512 | undefined, |
| 499 | eventfd_node.completion_key, | 513 | undefined, |
| 500 | overlapped, | 514 | &eventfd_node.base.overlapped, |
| 501 | ) catch { | 515 | ) catch { |
| 502 | self.next_tick_queue.unget(next_tick_node); | 516 | self.next_tick_queue.unget(next_tick_node); |
| 503 | self.available_eventfd_resume_nodes.push(resume_stack_node); | 517 | self.available_eventfd_resume_nodes.push(resume_stack_node); |
| ... | @@ -606,8 +620,8 @@ pub const Loop = struct { | ... | @@ -606,8 +620,8 @@ pub const Loop = struct { |
| 606 | var i: usize = 0; | 620 | var i: usize = 0; |
| 607 | while (i < self.extra_threads.len + 1) : (i += 1) { | 621 | while (i < self.extra_threads.len + 1) : (i += 1) { |
| 608 | while (true) { | 622 | while (true) { |
| 609 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); | 623 | const overlapped = &self.final_resume_node.overlapped; |
| 610 | os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; | 624 | os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, undefined, overlapped) catch continue; |
| 611 | break; | 625 | break; |
| 612 | } | 626 | } |
| 613 | } | 627 | } |
| ... | @@ -680,17 +694,19 @@ pub const Loop = struct { | ... | @@ -680,17 +694,19 @@ pub const Loop = struct { |
| 680 | }, | 694 | }, |
| 681 | builtin.Os.windows => { | 695 | builtin.Os.windows => { |
| 682 | var completion_key: usize = undefined; | 696 | var completion_key: usize = undefined; |
| 683 | while (true) { | 697 | const overlapped = while (true) { |
| 684 | var nbytes: windows.DWORD = undefined; | 698 | var nbytes: windows.DWORD = undefined; |
| 685 | var overlapped: ?*windows.OVERLAPPED = undefined; | 699 | var overlapped: ?*windows.OVERLAPPED = undefined; |
| 686 | switch (os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { | 700 | switch (os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, |
| 701 | &overlapped, windows.INFINITE)) | ||
| 702 | { | ||
| 687 | os.WindowsWaitResult.Aborted => return, | 703 | os.WindowsWaitResult.Aborted => return, |
| 688 | os.WindowsWaitResult.Normal => {}, | 704 | os.WindowsWaitResult.Normal => {}, |
| 689 | os.WindowsWaitResult.Cancelled => continue, | 705 | os.WindowsWaitResult.Cancelled => continue, |
| 690 | } | 706 | } |
| 691 | if (overlapped != null) break; | 707 | if (overlapped) |o| break o; |
| 692 | } | 708 | } else unreachable; // TODO else unreachable should not be necessary |
| 693 | const resume_node = @intToPtr(*ResumeNode, completion_key); | 709 | const resume_node = @fieldParentPtr(ResumeNode, "overlapped", overlapped); |
| 694 | const handle = resume_node.handle; | 710 | const handle = resume_node.handle; |
| 695 | const resume_node_id = resume_node.id; | 711 | const resume_node_id = resume_node.id; |
| 696 | switch (resume_node_id) { | 712 | switch (resume_node_id) { |