authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-12 13:55:02-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-12 13:55:35-04:00
log178d69191ba008dffd70d2854df09cec556b59dd
treebfbe9686e9059c37f3a78f5d5af0f74d7bca4710
parent0cfd019377c4e91924d8f57a4c7400a2d62f8751

windows: std.fs functions support concurrent ops

when reading and writing the same file descriptors

3 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 handle122 _ = 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();
127125
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 handle254 _ = 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();
261257
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;
10881083
1089 // TODO handle this error not in the channel but in the setup1084 // 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 IOCP1115 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);
3043
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 events245 // 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 events332 // 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) {