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 {
737737 file_path,
738738 max_src_size,
739739 )) catch |err| switch (err) {
740 error.IsDir => {
740 error.IsDir, error.AccessDenied => {
741741 // TODO make event based (and dir.next())
742742 var dir = try std.os.Dir.open(fmt.loop.allocator, file_path);
743743 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
109109 .base = Loop.ResumeNode{
110110 .id = Loop.ResumeNode.Id.Basic,
111111 .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 },
112119 },
113120 };
114 const completion_key = @ptrToInt(&resume_node.base);
115 // TODO support concurrent async ops on the file handle
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 };
121 // TODO only call create io completion port once per fd
122 _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined);
125123 loop.beginOneEvent();
126124 errdefer loop.finishOneEvent();
127125
128126 errdefer {
129 _ = windows.CancelIoEx(fd, &overlapped);
127 _ = windows.CancelIoEx(fd, &resume_node.base.overlapped);
130128 }
131129 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);
133131 }
134132 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) {
136134 const err = windows.GetLastError();
137135 return switch (err) {
138136 windows.ERROR.IO_PENDING => unreachable,
......@@ -243,30 +241,28 @@ pub async fn preadWindows(loop: *Loop, fd: os.FileHandle, data: []u8, offset: u6
243241 .base = Loop.ResumeNode{
244242 .id = Loop.ResumeNode.Id.Basic,
245243 .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 },
246251 },
247252 };
248 const completion_key = @ptrToInt(&resume_node.base);
249 // TODO support concurrent async ops on the file handle
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 };
253 // TODO only call create io completion port once per fd
254 _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined);
259255 loop.beginOneEvent();
260256 errdefer loop.finishOneEvent();
261257
262258 errdefer {
263 _ = windows.CancelIoEx(fd, &overlapped);
259 _ = windows.CancelIoEx(fd, &resume_node.base.overlapped);
264260 }
265261 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);
267263 }
268264 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) {
270266 const err = windows.GetLastError();
271267 return switch (err) {
272268 windows.ERROR.IO_PENDING => unreachable,
......@@ -1074,23 +1070,22 @@ pub fn Watch(comptime V: type) type {
10741070 .base = Loop.ResumeNode{
10751071 .id = Loop.ResumeNode.Id.Basic,
10761072 .handle = @handle(),
1073 .overlapped = windows.OVERLAPPED{
1074 .Internal = 0,
1075 .InternalHigh = 0,
1076 .Offset = 0,
1077 .OffsetHigh = 0,
1078 .hEvent = null,
1079 },
10771080 },
10781081 };
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 };
10871082 var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined;
10881083
10891084 // TODO handle this error not in the channel but in the setup
10901085 _ = os.windowsCreateIoCompletionPort(
10911086 dir_handle,
10921087 self.channel.loop.os_data.io_port,
1093 completion_key,
1088 undefined,
10941089 undefined,
10951090 ) catch |err| {
10961091 await (async self.channel.put(err) catch unreachable);
......@@ -1103,7 +1098,7 @@ pub fn Watch(comptime V: type) type {
11031098 self.channel.loop.beginOneEvent();
11041099 errdefer self.channel.loop.finishOneEvent();
11051100 errdefer {
1106 _ = windows.CancelIoEx(dir_handle, &overlapped);
1101 _ = windows.CancelIoEx(dir_handle, &resume_node.base.overlapped);
11071102 }
11081103 suspend {
11091104 _ = windows.ReadDirectoryChangesW(
......@@ -1116,13 +1111,13 @@ pub fn Watch(comptime V: type) type {
11161111 windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |
11171112 windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,
11181113 null, // number of bytes transferred (unused for async)
1119 &overlapped,
1114 &resume_node.base.overlapped,
11201115 null, // completion routine - unused because we use IOCP
11211116 );
11221117 }
11231118 }
11241119 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) {
11261121 const errno = windows.GetLastError();
11271122 const err = switch (errno) {
11281123 else => os.unexpectedErrorWindows(errno),
std/event/loop.zig+31-15
......@@ -27,6 +27,19 @@ pub const Loop = struct {
2727 pub const ResumeNode = struct {
2828 id: Id,
2929 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
3144 pub const Id = enum {
3245 Basic,
......@@ -101,6 +114,7 @@ pub const Loop = struct {
101114 .final_resume_node = ResumeNode{
102115 .id = ResumeNode.Id.Stop,
103116 .handle = undefined,
117 .overlapped = ResumeNode.overlapped_init,
104118 },
105119 };
106120 const extra_thread_count = thread_count - 1;
......@@ -153,6 +167,7 @@ pub const Loop = struct {
153167 .base = ResumeNode{
154168 .id = ResumeNode.Id.EventFd,
155169 .handle = undefined,
170 .overlapped = ResumeNode.overlapped_init,
156171 },
157172 .eventfd = try os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK),
158173 .epoll_op = posix.EPOLL_CTL_ADD,
......@@ -225,6 +240,7 @@ pub const Loop = struct {
225240 .base = ResumeNode{
226241 .id = ResumeNode.Id.EventFd,
227242 .handle = undefined,
243 .overlapped = ResumeNode.overlapped_init,
228244 },
229245 // this one is for sending events
230246 .kevent = posix.Kevent{
......@@ -311,6 +327,7 @@ pub const Loop = struct {
311327 .base = ResumeNode{
312328 .id = ResumeNode.Id.EventFd,
313329 .handle = undefined,
330 .overlapped = ResumeNode.overlapped_init,
314331 },
315332 // this one is for sending events
316333 .completion_key = @ptrToInt(&eventfd_node.data.base),
......@@ -325,8 +342,8 @@ pub const Loop = struct {
325342 var i: usize = 0;
326343 while (i < extra_thread_index) : (i += 1) {
327344 while (true) {
328 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
329 os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
345 const overlapped = &self.final_resume_node.overlapped;
346 os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, undefined, overlapped) catch continue;
330347 break;
331348 }
332349 }
......@@ -413,6 +430,7 @@ pub const Loop = struct {
413430 .base = ResumeNode{
414431 .id = ResumeNode.Id.Basic,
415432 .handle = @handle(),
433 .overlapped = ResumeNode.overlapped_init,
416434 },
417435 .kev = undefined,
418436 };
......@@ -489,15 +507,11 @@ pub const Loop = struct {
489507 };
490508 },
491509 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);
496510 os.windowsPostQueuedCompletionStatus(
497511 self.os_data.io_port,
498512 undefined,
499 eventfd_node.completion_key,
500 overlapped,
513 undefined,
514 &eventfd_node.base.overlapped,
501515 ) catch {
502516 self.next_tick_queue.unget(next_tick_node);
503517 self.available_eventfd_resume_nodes.push(resume_stack_node);
......@@ -606,8 +620,8 @@ pub const Loop = struct {
606620 var i: usize = 0;
607621 while (i < self.extra_threads.len + 1) : (i += 1) {
608622 while (true) {
609 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
610 os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
623 const overlapped = &self.final_resume_node.overlapped;
624 os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, undefined, overlapped) catch continue;
611625 break;
612626 }
613627 }
......@@ -680,17 +694,19 @@ pub const Loop = struct {
680694 },
681695 builtin.Os.windows => {
682696 var completion_key: usize = undefined;
683 while (true) {
697 const overlapped = while (true) {
684698 var nbytes: windows.DWORD = undefined;
685699 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 {
687703 os.WindowsWaitResult.Aborted => return,
688704 os.WindowsWaitResult.Normal => {},
689705 os.WindowsWaitResult.Cancelled => continue,
690706 }
691 if (overlapped != null) break;
692 }
693 const resume_node = @intToPtr(*ResumeNode, completion_key);
707 if (overlapped) |o| break o;
708 } else unreachable; // TODO else unreachable should not be necessary
709 const resume_node = @fieldParentPtr(ResumeNode, "overlapped", overlapped);
694710 const handle = resume_node.handle;
695711 const resume_node_id = resume_node.id;
696712 switch (resume_node_id) {