authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-08 15:05:57-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-08 15:06:32-04:00
log8b456927be372bfe776e021da273db3227a568a0
tree401d97637455bb865c97f8c9954441b1a471b9ab
parentac12f0df7160ab19290b975aa3e2eb38ae83cc31

std.event.fs.pwritev windows implementation

also fix 2 bugs where the function didn't call allocator.shrink: * std.mem.join * std.os.path.resolve

7 files changed, 284 insertions(+), 63 deletions(-)

std/event/fs.zig+238-43
...@@ -5,6 +5,8 @@ const assert = std.debug.assert;...@@ -5,6 +5,8 @@ const assert = std.debug.assert;
5const os = std.os;5const os = std.os;
6const mem = std.mem;6const mem = std.mem;
7const posix = os.posix;7const posix = os.posix;
8const windows = os.windows;
9const Loop = event.Loop;
810
9pub const RequestNode = std.atomic.Queue(Request).Node;11pub const RequestNode = std.atomic.Queue(Request).Node;
1012
...@@ -13,7 +15,7 @@ pub const Request = struct {...@@ -13,7 +15,7 @@ pub const Request = struct {
13 finish: Finish,15 finish: Finish,
1416
15 pub const Finish = union(enum) {17 pub const Finish = union(enum) {
16 TickNode: event.Loop.NextTickNode,18 TickNode: Loop.NextTickNode,
17 DeallocCloseOperation: *CloseOperation,19 DeallocCloseOperation: *CloseOperation,
18 NoAction,20 NoAction,
19 };21 };
...@@ -71,7 +73,77 @@ pub const Request = struct {...@@ -71,7 +73,77 @@ pub const Request = struct {
71};73};
7274
73/// data - just the inner references - must live until pwritev promise completes.75/// data - just the inner references - must live until pwritev promise completes.
74pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {76pub async fn pwritev(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {
77 switch (builtin.os) {
78 builtin.Os.macosx,
79 builtin.Os.linux,
80 => return await (async pwritevPosix(loop, fd, data, offset) catch unreachable),
81 builtin.Os.windows,
82 => return await (async pwritevWindows(loop, fd, data, offset) catch unreachable),
83 else => @compileError("Unsupported OS"),
84 }
85}
86
87/// data - just the inner references - must live until pwritev promise completes.
88pub async fn pwritevWindows(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {
89 if (data.len == 0) return;
90 if (data.len == 1) return await (async pwriteWindows(loop, fd, data[0], offset) catch unreachable);
91
92 const data_copy = std.mem.dupe(loop.allocator, []const u8, data);
93 defer loop.allocator.free(data_copy);
94
95 var off = offset;
96 for (data_copy) |buf| {
97 try await (async pwriteWindows(loop, fd, buf, off) catch unreachable);
98 off += buf.len;
99 }
100}
101
102pub async fn pwriteWindows(loop: *Loop, fd: os.FileHandle, data: []const u8, offset: u64) os.WindowsWriteError!void {
103 // workaround for https://github.com/ziglang/zig/issues/1194
104 suspend {
105 resume @handle();
106 }
107
108 var resume_node = Loop.ResumeNode.Basic{
109 .base = Loop.ResumeNode{
110 .id = Loop.ResumeNode.Id.Basic,
111 .handle = @handle(),
112 },
113 };
114 const completion_key = @ptrToInt(&resume_node.base);
115 _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, completion_key, undefined);
116 var overlapped = windows.OVERLAPPED{
117 .Internal = 0,
118 .InternalHigh = 0,
119 .Offset = @truncate(u32, offset),
120 .OffsetHigh = @truncate(u32, offset >> 32),
121 .hEvent = null,
122 };
123 errdefer {
124 _ = windows.CancelIoEx(fd, &overlapped);
125 }
126 suspend {
127 _ = windows.WriteFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &overlapped);
128 }
129 var bytes_transferred: windows.DWORD = undefined;
130 if (windows.GetOverlappedResult(fd, &overlapped, &bytes_transferred, windows.FALSE) == 0) {
131 const err = windows.GetLastError();
132 return switch (err) {
133 windows.ERROR.IO_PENDING => unreachable,
134 windows.ERROR.INVALID_USER_BUFFER => error.SystemResources,
135 windows.ERROR.NOT_ENOUGH_MEMORY => error.SystemResources,
136 windows.ERROR.OPERATION_ABORTED => error.OperationAborted,
137 windows.ERROR.NOT_ENOUGH_QUOTA => error.SystemResources,
138 windows.ERROR.BROKEN_PIPE => error.BrokenPipe,
139 else => os.unexpectedErrorWindows(err),
140 };
141 }
142}
143
144
145/// data - just the inner references - must live until pwritev promise completes.
146pub async fn pwritevPosix(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {
75 // workaround for https://github.com/ziglang/zig/issues/1194147 // workaround for https://github.com/ziglang/zig/issues/1194
76 suspend {148 suspend {
77 resume @handle();149 resume @handle();
...@@ -100,7 +172,7 @@ pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, data: []const []const...@@ -100,7 +172,7 @@ pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, data: []const []const
100 },172 },
101 },173 },
102 .finish = Request.Finish{174 .finish = Request.Finish{
103 .TickNode = event.Loop.NextTickNode{175 .TickNode = Loop.NextTickNode{
104 .prev = null,176 .prev = null,
105 .next = null,177 .next = null,
106 .data = @handle(),178 .data = @handle(),
...@@ -118,8 +190,8 @@ pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, data: []const []const...@@ -118,8 +190,8 @@ pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, data: []const []const
118 return req_node.data.msg.PWriteV.result;190 return req_node.data.msg.PWriteV.result;
119}191}
120192
121/// data - just the inner references - must live until pwritev promise completes.193/// data - just the inner references - must live until preadv promise completes.
122pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, data: []const []u8, offset: usize) !usize {194pub async fn preadv(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset: usize) !usize {
123 //const data_dupe = try mem.dupe(loop.allocator, []const u8, data);195 //const data_dupe = try mem.dupe(loop.allocator, []const u8, data);
124 //defer loop.allocator.free(data_dupe);196 //defer loop.allocator.free(data_dupe);
125197
...@@ -151,7 +223,7 @@ pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, data: []const []u8, of...@@ -151,7 +223,7 @@ pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, data: []const []u8, of
151 },223 },
152 },224 },
153 .finish = Request.Finish{225 .finish = Request.Finish{
154 .TickNode = event.Loop.NextTickNode{226 .TickNode = Loop.NextTickNode{
155 .prev = null,227 .prev = null,
156 .next = null,228 .next = null,
157 .data = @handle(),229 .data = @handle(),
...@@ -169,8 +241,8 @@ pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, data: []const []u8, of...@@ -169,8 +241,8 @@ pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, data: []const []u8, of
169 return req_node.data.msg.PReadV.result;241 return req_node.data.msg.PReadV.result;
170}242}
171243
172pub async fn open(244pub async fn openPosix(
173 loop: *event.Loop,245 loop: *Loop,
174 path: []const u8,246 path: []const u8,
175 flags: u32,247 flags: u32,
176 mode: os.File.Mode,248 mode: os.File.Mode,
...@@ -196,7 +268,7 @@ pub async fn open(...@@ -196,7 +268,7 @@ pub async fn open(
196 },268 },
197 },269 },
198 .finish = Request.Finish{270 .finish = Request.Finish{
199 .TickNode = event.Loop.NextTickNode{271 .TickNode = Loop.NextTickNode{
200 .prev = null,272 .prev = null,
201 .next = null,273 .next = null,
202 .data = @handle(),274 .data = @handle(),
...@@ -214,19 +286,47 @@ pub async fn open(...@@ -214,19 +286,47 @@ pub async fn open(
214 return req_node.data.msg.Open.result;286 return req_node.data.msg.Open.result;
215}287}
216288
217pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os.FileHandle {289pub async fn openRead(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle {
218 const flags = posix.O_LARGEFILE | posix.O_RDONLY | posix.O_CLOEXEC;290 const flags = posix.O_LARGEFILE | posix.O_RDONLY | posix.O_CLOEXEC;
219 return await (async open(loop, path, flags, 0) catch unreachable);291 return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable);
292}
293
294/// Creates if does not exist. Truncates the file if it exists.
295/// Uses the default mode.
296pub async fn openWrite(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle {
297 return await (async openWriteMode(loop, path, os.File.default_mode) catch unreachable);
298}
299
300/// Creates if does not exist. Truncates the file if it exists.
301pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os.File.OpenError!os.FileHandle {
302 switch (builtin.os) {
303 builtin.Os.macosx,
304 builtin.Os.linux,
305 => {
306 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
307 return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable);
308 },
309 builtin.Os.windows,
310 => return os.windowsOpen(
311 loop.allocator,
312 path,
313 windows.GENERIC_WRITE,
314 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
315 windows.CREATE_ALWAYS,
316 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,
317 ),
318 else => @compileError("Unsupported OS"),
319 }
220}320}
221321
222/// Creates if does not exist. Does not truncate.322/// Creates if does not exist. Does not truncate.
223pub async fn openReadWrite(323pub async fn openReadWrite(
224 loop: *event.Loop,324 loop: *Loop,
225 path: []const u8,325 path: []const u8,
226 mode: os.File.Mode,326 mode: os.File.Mode,
227) os.File.OpenError!os.FileHandle {327) os.File.OpenError!os.FileHandle {
228 const flags = posix.O_LARGEFILE | posix.O_RDWR | posix.O_CREAT | posix.O_CLOEXEC;328 const flags = posix.O_LARGEFILE | posix.O_RDWR | posix.O_CREAT | posix.O_CLOEXEC;
229 return await (async open(loop, path, flags, mode) catch unreachable);329 return await (async openPosix(loop, path, flags, mode) catch unreachable);
230}330}
231331
232/// This abstraction helps to close file handles in defer expressions332/// This abstraction helps to close file handles in defer expressions
...@@ -236,24 +336,46 @@ pub async fn openReadWrite(...@@ -236,24 +336,46 @@ pub async fn openReadWrite(
236/// If you call `setHandle` then finishing will close the fd; otherwise finishing336/// If you call `setHandle` then finishing will close the fd; otherwise finishing
237/// will deallocate the `CloseOperation`.337/// will deallocate the `CloseOperation`.
238pub const CloseOperation = struct {338pub const CloseOperation = struct {
239 loop: *event.Loop,339 loop: *Loop,
240 have_fd: bool,340 os_data: OsData,
241 close_req_node: RequestNode,341
342 const OsData = switch (builtin.os) {
343 builtin.Os.linux,
344 builtin.Os.macosx,
345 => struct {
346 have_fd: bool,
347 close_req_node: RequestNode,
348 },
349 builtin.Os.windows,
350 => struct {
351 handle: ?os.FileHandle,
352 },
353 else => @compileError("Unsupported OS"),
354 };
242355
243 pub fn start(loop: *event.Loop) (error{OutOfMemory}!*CloseOperation) {356 pub fn start(loop: *Loop) (error{OutOfMemory}!*CloseOperation) {
244 const self = try loop.allocator.createOne(CloseOperation);357 const self = try loop.allocator.createOne(CloseOperation);
245 self.* = CloseOperation{358 self.* = CloseOperation{
246 .loop = loop,359 .loop = loop,
247 .have_fd = false,360 .os_data = switch (builtin.os) {
248 .close_req_node = RequestNode{361 builtin.Os.linux,
249 .prev = null,362 builtin.Os.macosx,
250 .next = null,363 => OsData{
251 .data = Request{364 .have_fd = false,
252 .msg = Request.Msg{365 .close_req_node = RequestNode{
253 .Close = Request.Msg.Close{ .fd = undefined },366 .prev = null,
367 .next = null,
368 .data = Request{
369 .msg = Request.Msg{
370 .Close = Request.Msg.Close{ .fd = undefined },
371 },
372 .finish = Request.Finish{ .DeallocCloseOperation = self },
373 },
254 },374 },
255 .finish = Request.Finish{ .DeallocCloseOperation = self },
256 },375 },
376 builtin.Os.windows,
377 => OsData{ .handle = null },
378 else => @compileError("Unsupported OS"),
257 },379 },
258 };380 };
259 return self;381 return self;
...@@ -261,36 +383,109 @@ pub const CloseOperation = struct {...@@ -261,36 +383,109 @@ pub const CloseOperation = struct {
261383
262 /// Defer this after creating.384 /// Defer this after creating.
263 pub fn finish(self: *CloseOperation) void {385 pub fn finish(self: *CloseOperation) void {
264 if (self.have_fd) {386 switch (builtin.os) {
265 self.loop.posixFsRequest(&self.close_req_node);387 builtin.Os.linux,
266 } else {388 builtin.Os.macosx,
267 self.loop.allocator.destroy(self);389 => {
390 if (self.have_fd) {
391 self.loop.posixFsRequest(&self.close_req_node);
392 } else {
393 self.loop.allocator.destroy(self);
394 }
395 },
396 builtin.Os.windows,
397 => {
398 if (self.handle) |handle| {
399 os.close(handle);
400 }
401 self.loop.allocator.destroy(self);
402 },
403 else => @compileError("Unsupported OS"),
268 }404 }
269 }405 }
270406
271 pub fn setHandle(self: *CloseOperation, handle: os.FileHandle) void {407 pub fn setHandle(self: *CloseOperation, handle: os.FileHandle) void {
272 self.close_req_node.data.msg.Close.fd = handle;408 switch (builtin.os) {
273 self.have_fd = true;409 builtin.Os.linux,
410 builtin.Os.macosx,
411 => {
412 self.close_req_node.data.msg.Close.fd = handle;
413 self.have_fd = true;
414 },
415 builtin.Os.windows,
416 => {
417 self.handle = handle;
418 },
419 else => @compileError("Unsupported OS"),
420 }
274 }421 }
275422
276 /// Undo a `setHandle`.423 /// Undo a `setHandle`.
277 pub fn clearHandle(self: *CloseOperation) void {424 pub fn clearHandle(self: *CloseOperation) void {
278 self.have_fd = false;425 switch (builtin.os) {
426 builtin.Os.linux,
427 builtin.Os.macosx,
428 => {
429 self.have_fd = false;
430 },
431 builtin.Os.windows,
432 => {
433 self.handle = null;
434 },
435 else => @compileError("Unsupported OS"),
436 }
279 }437 }
280438
281 pub fn getHandle(self: *CloseOperation) os.FileHandle {439 pub fn getHandle(self: *CloseOperation) os.FileHandle {
282 assert(self.have_fd);440 switch (builtin.os) {
283 return self.close_req_node.data.msg.Close.fd;441 builtin.Os.linux,
442 builtin.Os.macosx,
443 => {
444 assert(self.have_fd);
445 return self.close_req_node.data.msg.Close.fd;
446 },
447 builtin.Os.windows,
448 => {
449 return self.handle.?;
450 },
451 else => @compileError("Unsupported OS"),
452 }
284 }453 }
285};454};
286455
287/// contents must remain alive until writeFile completes.456/// contents must remain alive until writeFile completes.
288pub async fn writeFile(loop: *event.Loop, path: []const u8, contents: []const u8) !void {457/// TODO make this atomic or provide writeFileAtomic and rename this one to writeFileTruncate
458pub async fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void {
289 return await (async writeFileMode(loop, path, contents, os.File.default_mode) catch unreachable);459 return await (async writeFileMode(loop, path, contents, os.File.default_mode) catch unreachable);
290}460}
291461
292/// contents must remain alive until writeFile completes.462/// contents must remain alive until writeFile completes.
293pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []const u8, mode: os.File.Mode) !void {463pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: os.File.Mode) !void {
464 switch (builtin.os) {
465 builtin.Os.linux,
466 builtin.Os.macosx,
467 => return await (async writeFileModeThread(loop, path, contents, mode) catch unreachable),
468 builtin.Os.windows,
469 => return await (async writeFileWindows(loop, path, contents) catch unreachable),
470 else => @compileError("Unsupported OS"),
471 }
472}
473
474async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void {
475 const handle = try os.windowsOpen(
476 loop.allocator,
477 path,
478 windows.GENERIC_WRITE,
479 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
480 windows.CREATE_ALWAYS,
481 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,
482 );
483 defer os.close(handle);
484
485 try await (async pwriteWindows(loop, handle, contents, 0) catch unreachable);
486}
487
488async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: os.File.Mode) !void {
294 // workaround for https://github.com/ziglang/zig/issues/1194489 // workaround for https://github.com/ziglang/zig/issues/1194
295 suspend {490 suspend {
296 resume @handle();491 resume @handle();
...@@ -312,7 +507,7 @@ pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []cons...@@ -312,7 +507,7 @@ pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []cons
312 },507 },
313 },508 },
314 .finish = Request.Finish{509 .finish = Request.Finish{
315 .TickNode = event.Loop.NextTickNode{510 .TickNode = Loop.NextTickNode{
316 .prev = null,511 .prev = null,
317 .next = null,512 .next = null,
318 .data = @handle(),513 .data = @handle(),
...@@ -333,7 +528,7 @@ pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []cons...@@ -333,7 +528,7 @@ pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []cons
333/// The promise resumes when the last data has been confirmed written, but before the file handle528/// The promise resumes when the last data has been confirmed written, but before the file handle
334/// is closed.529/// is closed.
335/// Caller owns returned memory.530/// Caller owns returned memory.
336pub async fn readFile(loop: *event.Loop, file_path: []const u8, max_size: usize) ![]u8 {531pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 {
337 var close_op = try CloseOperation.start(loop);532 var close_op = try CloseOperation.start(loop);
338 defer close_op.finish();533 defer close_op.finish();
339534
...@@ -417,7 +612,7 @@ pub fn Watch(comptime V: type) type {...@@ -417,7 +612,7 @@ pub fn Watch(comptime V: type) type {
417 pub const Error = WatchEventError;612 pub const Error = WatchEventError;
418 };613 };
419614
420 pub fn create(loop: *event.Loop, event_buf_count: usize) !*Self {615 pub fn create(loop: *Loop, event_buf_count: usize) !*Self {
421 const channel = try event.Channel(Self.Event.Error!Self.Event).create(loop, event_buf_count);616 const channel = try event.Channel(Self.Event.Error!Self.Event).create(loop, event_buf_count);
422 errdefer channel.destroy();617 errdefer channel.destroy();
423618
...@@ -482,7 +677,7 @@ pub fn Watch(comptime V: type) type {...@@ -482,7 +677,7 @@ pub fn Watch(comptime V: type) type {
482677
483 const flags = posix.O_SYMLINK | posix.O_EVTONLY;678 const flags = posix.O_SYMLINK | posix.O_EVTONLY;
484 const mode = 0;679 const mode = 0;
485 const fd = try await (async open(self.channel.loop, resolved_path, flags, mode) catch unreachable);680 const fd = try await (async openPosix(self.channel.loop, resolved_path, flags, mode) catch unreachable);
486 close_op.setHandle(fd);681 close_op.setHandle(fd);
487682
488 var put_data: *OsData.Put = undefined;683 var put_data: *OsData.Put = undefined;
...@@ -722,7 +917,7 @@ test "write a file, watch it, write it again" {...@@ -722,7 +917,7 @@ test "write a file, watch it, write it again" {
722 try os.makePath(allocator, test_tmp_dir);917 try os.makePath(allocator, test_tmp_dir);
723 defer os.deleteTree(allocator, test_tmp_dir) catch {};918 defer os.deleteTree(allocator, test_tmp_dir) catch {};
724919
725 var loop: event.Loop = undefined;920 var loop: Loop = undefined;
726 try loop.initMultiThreaded(allocator);921 try loop.initMultiThreaded(allocator);
727 defer loop.deinit();922 defer loop.deinit();
728923
...@@ -734,11 +929,11 @@ test "write a file, watch it, write it again" {...@@ -734,11 +929,11 @@ test "write a file, watch it, write it again" {
734 return result;929 return result;
735}930}
736931
737async fn testFsWatchCantFail(loop: *event.Loop, result: *(error!void)) void {932async fn testFsWatchCantFail(loop: *Loop, result: *(error!void)) void {
738 result.* = await async testFsWatch(loop) catch unreachable;933 result.* = await async testFsWatch(loop) catch unreachable;
739}934}
740935
741async fn testFsWatch(loop: *event.Loop) !void {936async fn testFsWatch(loop: *Loop) !void {
742 const file_path = try os.path.join(loop.allocator, test_tmp_dir, "file.txt");937 const file_path = try os.path.join(loop.allocator, test_tmp_dir, "file.txt");
743 defer loop.allocator.free(file_path);938 defer loop.allocator.free(file_path);
744939
std/event/loop.zig+18-12
...@@ -301,7 +301,7 @@ pub const Loop = struct {...@@ -301,7 +301,7 @@ pub const Loop = struct {
301 windows.INVALID_HANDLE_VALUE,301 windows.INVALID_HANDLE_VALUE,
302 null,302 null,
303 undefined,303 undefined,
304 undefined,304 @maxValue(windows.DWORD),
305 );305 );
306 errdefer os.close(self.os_data.io_port);306 errdefer os.close(self.os_data.io_port);
307307
...@@ -315,7 +315,6 @@ pub const Loop = struct {...@@ -315,7 +315,6 @@ pub const Loop = struct {
315 // this one is for sending events315 // this one is for sending events
316 .completion_key = @ptrToInt(&eventfd_node.data.base),316 .completion_key = @ptrToInt(&eventfd_node.data.base),
317 },317 },
318 .prev = undefined,
319 .next = undefined,318 .next = undefined,
320 };319 };
321 self.available_eventfd_resume_nodes.push(eventfd_node);320 self.available_eventfd_resume_nodes.push(eventfd_node);
...@@ -528,7 +527,12 @@ pub const Loop = struct {...@@ -528,7 +527,12 @@ pub const Loop = struct {
528527
529 self.workerRun();528 self.workerRun();
530529
531 self.os_data.fs_thread.wait();530 switch (builtin.os) {
531 builtin.Os.linux,
532 builtin.Os.macosx,
533 => self.os_data.fs_thread.wait(),
534 else => {},
535 }
532536
533 for (self.extra_threads) |extra_thread| {537 for (self.extra_threads) |extra_thread| {
534 extra_thread.wait();538 extra_thread.wait();
...@@ -794,15 +798,7 @@ pub const Loop = struct {...@@ -794,15 +798,7 @@ pub const Loop = struct {
794 }798 }
795799
796 const OsData = switch (builtin.os) {800 const OsData = switch (builtin.os) {
797 builtin.Os.linux => struct {801 builtin.Os.linux => LinuxOsData,
798 epollfd: i32,
799 final_eventfd: i32,
800 final_eventfd_event: os.linux.epoll_event,
801 fs_thread: *os.Thread,
802 fs_queue_item: u8,
803 fs_queue: std.atomic.Queue(fs.Request),
804 fs_end_request: fs.RequestNode,
805 },
806 builtin.Os.macosx => MacOsData,802 builtin.Os.macosx => MacOsData,
807 builtin.Os.windows => struct {803 builtin.Os.windows => struct {
808 io_port: windows.HANDLE,804 io_port: windows.HANDLE,
...@@ -821,6 +817,16 @@ pub const Loop = struct {...@@ -821,6 +817,16 @@ pub const Loop = struct {
821 fs_queue: std.atomic.Queue(fs.Request),817 fs_queue: std.atomic.Queue(fs.Request),
822 fs_end_request: fs.RequestNode,818 fs_end_request: fs.RequestNode,
823 };819 };
820
821 const LinuxOsData = struct {
822 epollfd: i32,
823 final_eventfd: i32,
824 final_eventfd_event: os.linux.epoll_event,
825 fs_thread: *os.Thread,
826 fs_queue_item: u8,
827 fs_queue: std.atomic.Queue(fs.Request),
828 fs_end_request: fs.RequestNode,
829 };
824};830};
825831
826test "std.event.Loop - basic" {832test "std.event.Loop - basic" {
std/mem.zig+1-1
...@@ -541,7 +541,7 @@ pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {...@@ -541,7 +541,7 @@ pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {
541 }541 }
542 }542 }
543543
544 return buf[0..buf_index];544 return allocator.shrink(u8, buf, buf_index);
545}545}
546546
547test "mem.join" {547test "mem.join" {
std/os/path.zig+1-1
...@@ -506,7 +506,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -506,7 +506,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
506 result_index += 1;506 result_index += 1;
507 }507 }
508508
509 return result[0..result_index];509 return allocator.shrink(u8, result, result_index);
510}510}
511511
512/// This function is like a series of `cd` statements executed one after another.512/// This function is like a series of `cd` statements executed one after another.
std/os/windows/index.zig+15-2
...@@ -67,8 +67,9 @@ pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD));...@@ -67,8 +67,9 @@ pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD));
67pub const OVERLAPPED = extern struct {67pub const OVERLAPPED = extern struct {
68 Internal: ULONG_PTR,68 Internal: ULONG_PTR,
69 InternalHigh: ULONG_PTR,69 InternalHigh: ULONG_PTR,
70 Pointer: PVOID,70 Offset: DWORD,
71 hEvent: HANDLE,71 OffsetHigh: DWORD,
72 hEvent: ?HANDLE,
72};73};
73pub const LPOVERLAPPED = *OVERLAPPED;74pub const LPOVERLAPPED = *OVERLAPPED;
7475
...@@ -350,3 +351,15 @@ pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));...@@ -350,3 +351,15 @@ pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));
350pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));351pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));
351pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));352pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));
352pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057));353pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057));
354
355pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
356pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
357pub const FILE_FLAG_NO_BUFFERING = 0x20000000;
358pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000;
359pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
360pub const FILE_FLAG_OVERLAPPED = 0x40000000;
361pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000;
362pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
363pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
364pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
365pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
std/os/windows/kernel32.zig+9-1
...@@ -1,5 +1,8 @@...@@ -1,5 +1,8 @@
1use @import("index.zig");1use @import("index.zig");
22
3
4pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL;
5
3pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;6pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
47
5pub extern "kernel32" stdcallcc fn CreateDirectoryA(8pub extern "kernel32" stdcallcc fn CreateDirectoryA(
...@@ -91,6 +94,9 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(...@@ -91,6 +94,9 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
91 dwFlags: DWORD,94 dwFlags: DWORD,
92) DWORD;95) DWORD;
9396
97
98pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL;
99
94pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;100pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
95pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;101pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
96102
...@@ -150,12 +156,14 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis...@@ -150,12 +156,14 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis
150156
151pub extern "kernel32" stdcallcc fn WriteFile(157pub extern "kernel32" stdcallcc fn WriteFile(
152 in_hFile: HANDLE,158 in_hFile: HANDLE,
153 in_lpBuffer: *const c_void,159 in_lpBuffer: [*]const u8,
154 in_nNumberOfBytesToWrite: DWORD,160 in_nNumberOfBytesToWrite: DWORD,
155 out_lpNumberOfBytesWritten: ?*DWORD,161 out_lpNumberOfBytesWritten: ?*DWORD,
156 in_out_lpOverlapped: ?*OVERLAPPED,162 in_out_lpOverlapped: ?*OVERLAPPED,
157) BOOL;163) BOOL;
158164
165pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
166
159//TODO: call unicode versions instead of relying on ANSI code page167//TODO: call unicode versions instead of relying on ANSI code page
160pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;168pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
161169
std/os/windows/util.zig+2-3
...@@ -36,20 +36,19 @@ pub fn windowsClose(handle: windows.HANDLE) void {...@@ -36,20 +36,19 @@ pub fn windowsClose(handle: windows.HANDLE) void {
36pub const WriteError = error{36pub const WriteError = error{
37 SystemResources,37 SystemResources,
38 OperationAborted,38 OperationAborted,
39 IoPending,
40 BrokenPipe,39 BrokenPipe,
41 Unexpected,40 Unexpected,
42};41};
4342
44pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void {43pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void {
45 if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), @intCast(u32, bytes.len), null, null) == 0) {44 if (windows.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), null, null) == 0) {
46 const err = windows.GetLastError();45 const err = windows.GetLastError();
47 return switch (err) {46 return switch (err) {
48 windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,47 windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,
49 windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,48 windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,
50 windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,49 windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,
51 windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,50 windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,
52 windows.ERROR.IO_PENDING => WriteError.IoPending,51 windows.ERROR.IO_PENDING => unreachable,
53 windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,52 windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,
54 else => os.unexpectedErrorWindows(err),53 else => os.unexpectedErrorWindows(err),
55 };54 };