| author | |
| committer | |
| log | 8c4482ed78fb651c0288f0cd2bdaf328564c6a49 |
| tree | efccaf6ea986a840f3cf4df64da174b7bdd18da5 |
| parent | dfbb6e9879693331c7b8ea57eb7aa8bdf934403f |
| parent | 4236ca40cd21895590c580c9a1f56423c2c2f167 |
16 files changed, 428 insertions(+), 315 deletions(-)
lib/std/Build/Fuzz/WebServer.zig+8-7| ... | ... | @@ -282,13 +282,15 @@ fn buildWasmBinary( |
| 282 | 282 | var result: ?Path = null; |
| 283 | 283 | var result_error_bundle = std.zig.ErrorBundle.empty; |
| 284 | 284 | |
| 285 | const stdout_br = poller.reader(.stdout); | |
| 285 | const stdout = poller.reader(.stdout); | |
| 286 | ||
| 286 | 287 | poll: while (true) { |
| 287 | 288 | const Header = std.zig.Server.Message.Header; |
| 288 | while (stdout_br.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll; | |
| 289 | const header = (stdout_br.takeStruct(Header) catch unreachable).*; | |
| 290 | while (stdout_br.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; | |
| 291 | const body = stdout_br.take(header.bytes_len) catch unreachable; | |
| 289 | while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll; | |
| 290 | const header = stdout.takeStruct(Header, .little) catch unreachable; | |
| 291 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; | |
| 292 | const body = stdout.take(header.bytes_len) catch unreachable; | |
| 293 | ||
| 292 | 294 | switch (header.tag) { |
| 293 | 295 | .zig_version => { |
| 294 | 296 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { |
| ... | ... | @@ -327,8 +329,7 @@ fn buildWasmBinary( |
| 327 | 329 | } |
| 328 | 330 | } |
| 329 | 331 | |
| 330 | const stderr_br = poller.reader(.stderr); | |
| 331 | const stderr_contents = stderr_br.buffered(); | |
| 332 | const stderr_contents = try poller.toOwnedSlice(.stderr); | |
| 332 | 333 | if (stderr_contents.len > 0) { |
| 333 | 334 | std.debug.print("{s}", .{stderr_contents}); |
| 334 | 335 | } |
lib/std/Build/Step.zig+17-16| ... | ... | @@ -286,7 +286,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T { |
| 286 | 286 | } |
| 287 | 287 | |
| 288 | 288 | /// For debugging purposes, prints identifying information about this Step. |
| 289 | pub fn dump(step: *Step, w: *std.io.Writer, tty_config: std.io.tty.Config) void { | |
| 289 | pub fn dump(step: *Step, w: *std.Io.Writer, tty_config: std.Io.tty.Config) void { | |
| 290 | 290 | const debug_info = std.debug.getSelfDebugInfo() catch |err| { |
| 291 | 291 | w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{ |
| 292 | 292 | @errorName(err), |
| ... | ... | @@ -359,7 +359,7 @@ pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutO |
| 359 | 359 | |
| 360 | 360 | pub const ZigProcess = struct { |
| 361 | 361 | child: std.process.Child, |
| 362 | poller: std.io.Poller(StreamEnum), | |
| 362 | poller: std.Io.Poller(StreamEnum), | |
| 363 | 363 | progress_ipc_fd: if (std.Progress.have_ipc) ?std.posix.fd_t else void, |
| 364 | 364 | |
| 365 | 365 | pub const StreamEnum = enum { stdout, stderr }; |
| ... | ... | @@ -428,7 +428,7 @@ pub fn evalZigProcess( |
| 428 | 428 | const zp = try gpa.create(ZigProcess); |
| 429 | 429 | zp.* = .{ |
| 430 | 430 | .child = child, |
| 431 | .poller = std.io.poll(gpa, ZigProcess.StreamEnum, .{ | |
| 431 | .poller = std.Io.poll(gpa, ZigProcess.StreamEnum, .{ | |
| 432 | 432 | .stdout = child.stdout.?, |
| 433 | 433 | .stderr = child.stderr.?, |
| 434 | 434 | }), |
| ... | ... | @@ -511,12 +511,14 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { |
| 511 | 511 | var result: ?Path = null; |
| 512 | 512 | |
| 513 | 513 | const stdout = zp.poller.reader(.stdout); |
| 514 | ||
| 514 | 515 | poll: while (true) { |
| 515 | 516 | const Header = std.zig.Server.Message.Header; |
| 516 | 517 | while (stdout.buffered().len < @sizeOf(Header)) if (!try zp.poller.poll()) break :poll; |
| 517 | const header = (stdout.takeStruct(Header) catch unreachable).*; | |
| 518 | const header = stdout.takeStruct(Header, .little) catch unreachable; | |
| 518 | 519 | while (stdout.buffered().len < header.bytes_len) if (!try zp.poller.poll()) break :poll; |
| 519 | 520 | const body = stdout.take(header.bytes_len) catch unreachable; |
| 521 | ||
| 520 | 522 | switch (header.tag) { |
| 521 | 523 | .zig_version => { |
| 522 | 524 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { |
| ... | ... | @@ -606,8 +608,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { |
| 606 | 608 | |
| 607 | 609 | s.result_duration_ns = timer.read(); |
| 608 | 610 | |
| 609 | const stderr = zp.poller.reader(.stderr); | |
| 610 | const stderr_contents = stderr.buffered(); | |
| 611 | const stderr_contents = try zp.poller.toOwnedSlice(.stderr); | |
| 611 | 612 | if (stderr_contents.len > 0) { |
| 612 | 613 | try s.result_error_msgs.append(arena, try arena.dupe(u8, stderr_contents)); |
| 613 | 614 | } |
| ... | ... | @@ -726,7 +727,7 @@ pub fn allocPrintCmd2( |
| 726 | 727 | argv: []const []const u8, |
| 727 | 728 | ) Allocator.Error![]u8 { |
| 728 | 729 | const shell = struct { |
| 729 | fn escape(writer: *std.io.Writer, string: []const u8, is_argv0: bool) !void { | |
| 730 | fn escape(writer: *std.Io.Writer, string: []const u8, is_argv0: bool) !void { | |
| 730 | 731 | for (string) |c| { |
| 731 | 732 | if (switch (c) { |
| 732 | 733 | else => true, |
| ... | ... | @@ -760,9 +761,9 @@ pub fn allocPrintCmd2( |
| 760 | 761 | } |
| 761 | 762 | }; |
| 762 | 763 | |
| 763 | var aw: std.io.Writer.Allocating = .init(arena); | |
| 764 | var aw: std.Io.Writer.Allocating = .init(arena); | |
| 764 | 765 | const writer = &aw.writer; |
| 765 | if (opt_cwd) |cwd| try writer.print(arena, "cd {s} && ", .{cwd}); | |
| 766 | if (opt_cwd) |cwd| writer.print("cd {s} && ", .{cwd}) catch return error.OutOfMemory; | |
| 766 | 767 | if (opt_env) |env| { |
| 767 | 768 | const process_env_map = std.process.getEnvMap(arena) catch std.process.EnvMap.init(arena); |
| 768 | 769 | var it = env.iterator(); |
| ... | ... | @@ -772,17 +773,17 @@ pub fn allocPrintCmd2( |
| 772 | 773 | if (process_env_map.get(key)) |process_value| { |
| 773 | 774 | if (std.mem.eql(u8, value, process_value)) continue; |
| 774 | 775 | } |
| 775 | try writer.print(arena, "{s}=", .{key}); | |
| 776 | try shell.escape(writer, value, false); | |
| 777 | try writer.writeByte(arena, ' '); | |
| 776 | writer.print("{s}=", .{key}) catch return error.OutOfMemory; | |
| 777 | shell.escape(writer, value, false) catch return error.OutOfMemory; | |
| 778 | writer.writeByte(' ') catch return error.OutOfMemory; | |
| 778 | 779 | } |
| 779 | 780 | } |
| 780 | try shell.escape(writer, argv[0], true); | |
| 781 | shell.escape(writer, argv[0], true) catch return error.OutOfMemory; | |
| 781 | 782 | for (argv[1..]) |arg| { |
| 782 | try writer.writeByte(arena, ' '); | |
| 783 | try shell.escape(writer, arg, false); | |
| 783 | writer.writeByte(' ') catch return error.OutOfMemory; | |
| 784 | shell.escape(writer, arg, false) catch return error.OutOfMemory; | |
| 784 | 785 | } |
| 785 | return aw.getWritten(); | |
| 786 | return aw.toOwnedSlice(); | |
| 786 | 787 | } |
| 787 | 788 | |
| 788 | 789 | /// Prefer `cacheHitAndWatch` unless you already added watch inputs |
lib/std/Build/Step/Run.zig+11-9| ... | ... | @@ -1545,7 +1545,7 @@ fn evalZigTest( |
| 1545 | 1545 | const any_write_failed = first_write_failed or poll: while (true) { |
| 1546 | 1546 | const Header = std.zig.Server.Message.Header; |
| 1547 | 1547 | while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll false; |
| 1548 | const header = (stdout.takeStruct(Header, .little) catch unreachable).*; | |
| 1548 | const header = stdout.takeStruct(Header, .little) catch unreachable; | |
| 1549 | 1549 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll false; |
| 1550 | 1550 | const body = stdout.take(header.bytes_len) catch unreachable; |
| 1551 | 1551 | switch (header.tag) { |
| ... | ... | @@ -1808,21 +1808,23 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult { |
| 1808 | 1808 | } |
| 1809 | 1809 | } |
| 1810 | 1810 | |
| 1811 | stdout_bytes = poller.reader(.stdout).buffered(); | |
| 1812 | stderr_bytes = poller.reader(.stderr).buffered(); | |
| 1811 | stdout_bytes = try poller.toOwnedSlice(.stdout); | |
| 1812 | stderr_bytes = try poller.toOwnedSlice(.stderr); | |
| 1813 | 1813 | } else { |
| 1814 | var fr = stdout.readerStreaming(); | |
| 1815 | stdout_bytes = fr.interface().allocRemaining(arena, run.stdio_limit) catch |err| switch (err) { | |
| 1814 | var small_buffer: [1]u8 = undefined; | |
| 1815 | var stdout_reader = stdout.readerStreaming(&small_buffer); | |
| 1816 | stdout_bytes = stdout_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) { | |
| 1816 | 1817 | error.OutOfMemory => return error.OutOfMemory, |
| 1817 | error.ReadFailed => return fr.err.?, | |
| 1818 | error.ReadFailed => return stdout_reader.err.?, | |
| 1818 | 1819 | error.StreamTooLong => return error.StdoutStreamTooLong, |
| 1819 | 1820 | }; |
| 1820 | 1821 | } |
| 1821 | 1822 | } else if (child.stderr) |stderr| { |
| 1822 | var fr = stderr.readerStreaming(); | |
| 1823 | stderr_bytes = fr.interface().allocRemaining(arena, run.stdio_limit) catch |err| switch (err) { | |
| 1823 | var small_buffer: [1]u8 = undefined; | |
| 1824 | var stderr_reader = stderr.readerStreaming(&small_buffer); | |
| 1825 | stderr_bytes = stderr_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) { | |
| 1824 | 1826 | error.OutOfMemory => return error.OutOfMemory, |
| 1825 | error.ReadFailed => return fr.err.?, | |
| 1827 | error.ReadFailed => return stderr_reader.err.?, | |
| 1826 | 1828 | error.StreamTooLong => return error.StderrStreamTooLong, |
| 1827 | 1829 | }; |
| 1828 | 1830 | } |
lib/std/Io.zig+196-143| ... | ... | @@ -92,12 +92,7 @@ pub fn poll( |
| 92 | 92 | const enum_fields = @typeInfo(StreamEnum).@"enum".fields; |
| 93 | 93 | var result: Poller(StreamEnum) = .{ |
| 94 | 94 | .gpa = gpa, |
| 95 | .readers = @splat(.{ | |
| 96 | .unbuffered_reader = .failing, | |
| 97 | .buffer = &.{}, | |
| 98 | .end = 0, | |
| 99 | .seek = 0, | |
| 100 | }), | |
| 95 | .readers = @splat(.failing), | |
| 101 | 96 | .poll_fds = undefined, |
| 102 | 97 | .windows = if (is_windows) .{ |
| 103 | 98 | .first_read_done = false, |
| ... | ... | @@ -186,21 +181,40 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 186 | 181 | } |
| 187 | 182 | } |
| 188 | 183 | |
| 189 | pub inline fn reader(self: *Self, comptime which: StreamEnum) *Reader { | |
| 184 | pub fn reader(self: *Self, which: StreamEnum) *Reader { | |
| 190 | 185 | return &self.readers[@intFromEnum(which)]; |
| 191 | 186 | } |
| 192 | 187 | |
| 188 | pub fn toOwnedSlice(self: *Self, which: StreamEnum) error{OutOfMemory}![]u8 { | |
| 189 | const gpa = self.gpa; | |
| 190 | const r = reader(self, which); | |
| 191 | if (r.seek == 0) { | |
| 192 | const new = try gpa.realloc(r.buffer, r.end); | |
| 193 | r.buffer = &.{}; | |
| 194 | r.end = 0; | |
| 195 | return new; | |
| 196 | } | |
| 197 | const new = try gpa.dupe(u8, r.buffered()); | |
| 198 | gpa.free(r.buffer); | |
| 199 | r.buffer = &.{}; | |
| 200 | r.seek = 0; | |
| 201 | r.end = 0; | |
| 202 | return new; | |
| 203 | } | |
| 204 | ||
| 193 | 205 | fn pollWindows(self: *Self, nanoseconds: ?u64) !bool { |
| 194 | 206 | const bump_amt = 512; |
| 207 | const gpa = self.gpa; | |
| 195 | 208 | |
| 196 | 209 | if (!self.windows.first_read_done) { |
| 197 | 210 | var already_read_data = false; |
| 198 | 211 | for (0..enum_fields.len) |i| { |
| 199 | 212 | const handle = self.windows.active.handles_buf[i]; |
| 200 | 213 | switch (try windowsAsyncReadToFifoAndQueueSmallRead( |
| 214 | gpa, | |
| 201 | 215 | handle, |
| 202 | 216 | &self.windows.overlapped[i], |
| 203 | &self.fifos[i], | |
| 217 | &self.readers[i], | |
| 204 | 218 | &self.windows.small_bufs[i], |
| 205 | 219 | bump_amt, |
| 206 | 220 | )) { |
| ... | ... | @@ -247,7 +261,7 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 247 | 261 | const handle = self.windows.active.handles_buf[active_idx]; |
| 248 | 262 | |
| 249 | 263 | const overlapped = &self.windows.overlapped[stream_idx]; |
| 250 | const stream_fifo = &self.fifos[stream_idx]; | |
| 264 | const stream_reader = &self.readers[stream_idx]; | |
| 251 | 265 | const small_buf = &self.windows.small_bufs[stream_idx]; |
| 252 | 266 | |
| 253 | 267 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { |
| ... | ... | @@ -258,12 +272,16 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 258 | 272 | }, |
| 259 | 273 | .aborted => unreachable, |
| 260 | 274 | }; |
| 261 | try stream_fifo.write(small_buf[0..num_bytes_read]); | |
| 275 | const buf = small_buf[0..num_bytes_read]; | |
| 276 | const dest = try writableSliceGreedyAlloc(stream_reader, gpa, buf.len); | |
| 277 | @memcpy(dest[0..buf.len], buf); | |
| 278 | advanceBufferEnd(stream_reader, buf.len); | |
| 262 | 279 | |
| 263 | 280 | switch (try windowsAsyncReadToFifoAndQueueSmallRead( |
| 281 | gpa, | |
| 264 | 282 | handle, |
| 265 | 283 | overlapped, |
| 266 | stream_fifo, | |
| 284 | stream_reader, | |
| 267 | 285 | small_buf, |
| 268 | 286 | bump_amt, |
| 269 | 287 | )) { |
| ... | ... | @@ -298,18 +316,18 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 298 | 316 | } |
| 299 | 317 | |
| 300 | 318 | var keep_polling = false; |
| 301 | inline for (&self.poll_fds, &self.readers) |*poll_fd, *r| { | |
| 319 | for (&self.poll_fds, &self.readers) |*poll_fd, *r| { | |
| 302 | 320 | // Try reading whatever is available before checking the error |
| 303 | 321 | // conditions. |
| 304 | 322 | // It's still possible to read after a POLL.HUP is received, |
| 305 | 323 | // always check if there's some data waiting to be read first. |
| 306 | 324 | if (poll_fd.revents & posix.POLL.IN != 0) { |
| 307 | const buf = try r.writableSliceGreedyAlloc(gpa, bump_amt); | |
| 325 | const buf = try writableSliceGreedyAlloc(r, gpa, bump_amt); | |
| 308 | 326 | const amt = posix.read(poll_fd.fd, buf) catch |err| switch (err) { |
| 309 | 327 | error.BrokenPipe => 0, // Handle the same as EOF. |
| 310 | 328 | else => |e| return e, |
| 311 | 329 | }; |
| 312 | r.advanceBufferEnd(amt); | |
| 330 | advanceBufferEnd(r, amt); | |
| 313 | 331 | if (amt == 0) { |
| 314 | 332 | // Remove the fd when the EOF condition is met. |
| 315 | 333 | poll_fd.fd = -1; |
| ... | ... | @@ -325,146 +343,181 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 325 | 343 | } |
| 326 | 344 | return keep_polling; |
| 327 | 345 | } |
| 328 | }; | |
| 329 | } | |
| 330 | 346 | |
| 331 | /// The `ReadFile` docuementation states that `lpNumberOfBytesRead` does not have a meaningful | |
| 332 | /// result when using overlapped I/O, but also that it cannot be `null` on Windows 7. For | |
| 333 | /// compatibility, we point it to this dummy variables, which we never otherwise access. | |
| 334 | /// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile | |
| 335 | var win_dummy_bytes_read: u32 = undefined; | |
| 336 | ||
| 337 | /// Read as much data as possible from `handle` with `overlapped`, and write it to the FIFO. Before | |
| 338 | /// returning, queue a read into `small_buf` so that `WaitForMultipleObjects` returns when more data | |
| 339 | /// is available. `handle` must have no pending asynchronous operation. | |
| 340 | fn windowsAsyncReadToFifoAndQueueSmallRead( | |
| 341 | handle: windows.HANDLE, | |
| 342 | overlapped: *windows.OVERLAPPED, | |
| 343 | r: *Reader, | |
| 344 | small_buf: *[128]u8, | |
| 345 | bump_amt: usize, | |
| 346 | ) !enum { empty, populated, closed_populated, closed } { | |
| 347 | var read_any_data = false; | |
| 348 | while (true) { | |
| 349 | const fifo_read_pending = while (true) { | |
| 350 | const buf = try r.writableWithSize(bump_amt); | |
| 351 | const buf_len = math.cast(u32, buf.len) orelse math.maxInt(u32); | |
| 352 | ||
| 353 | if (0 == windows.kernel32.ReadFile( | |
| 354 | handle, | |
| 355 | buf.ptr, | |
| 356 | buf_len, | |
| 357 | &win_dummy_bytes_read, | |
| 358 | overlapped, | |
| 359 | )) switch (windows.GetLastError()) { | |
| 360 | .IO_PENDING => break true, | |
| 361 | .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed, | |
| 362 | else => |err| return windows.unexpectedError(err), | |
| 363 | }; | |
| 347 | /// Returns a slice into the unused capacity of `buffer` with at least | |
| 348 | /// `min_len` bytes, extending `buffer` by resizing it with `gpa` as necessary. | |
| 349 | /// | |
| 350 | /// After calling this function, typically the caller will follow up with a | |
| 351 | /// call to `advanceBufferEnd` to report the actual number of bytes buffered. | |
| 352 | fn writableSliceGreedyAlloc(r: *Reader, allocator: Allocator, min_len: usize) Allocator.Error![]u8 { | |
| 353 | { | |
| 354 | const unused = r.buffer[r.end..]; | |
| 355 | if (unused.len >= min_len) return unused; | |
| 356 | } | |
| 357 | if (r.seek > 0) r.rebase(); | |
| 358 | { | |
| 359 | var list: std.ArrayListUnmanaged(u8) = .{ | |
| 360 | .items = r.buffer[0..r.end], | |
| 361 | .capacity = r.buffer.len, | |
| 362 | }; | |
| 363 | defer r.buffer = list.allocatedSlice(); | |
| 364 | try list.ensureUnusedCapacity(allocator, min_len); | |
| 365 | } | |
| 366 | const unused = r.buffer[r.end..]; | |
| 367 | assert(unused.len >= min_len); | |
| 368 | return unused; | |
| 369 | } | |
| 364 | 370 | |
| 365 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { | |
| 366 | .success => |n| n, | |
| 367 | .closed => return if (read_any_data) .closed_populated else .closed, | |
| 368 | .aborted => unreachable, | |
| 369 | }; | |
| 371 | /// After writing directly into the unused capacity of `buffer`, this function | |
| 372 | /// updates `end` so that users of `Reader` can receive the data. | |
| 373 | fn advanceBufferEnd(r: *Reader, n: usize) void { | |
| 374 | assert(n <= r.buffer.len - r.end); | |
| 375 | r.end += n; | |
| 376 | } | |
| 370 | 377 | |
| 371 | read_any_data = true; | |
| 372 | r.update(num_bytes_read); | |
| 378 | /// The `ReadFile` docuementation states that `lpNumberOfBytesRead` does not have a meaningful | |
| 379 | /// result when using overlapped I/O, but also that it cannot be `null` on Windows 7. For | |
| 380 | /// compatibility, we point it to this dummy variables, which we never otherwise access. | |
| 381 | /// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile | |
| 382 | var win_dummy_bytes_read: u32 = undefined; | |
| 383 | ||
| 384 | /// Read as much data as possible from `handle` with `overlapped`, and write it to the FIFO. Before | |
| 385 | /// returning, queue a read into `small_buf` so that `WaitForMultipleObjects` returns when more data | |
| 386 | /// is available. `handle` must have no pending asynchronous operation. | |
| 387 | fn windowsAsyncReadToFifoAndQueueSmallRead( | |
| 388 | gpa: Allocator, | |
| 389 | handle: windows.HANDLE, | |
| 390 | overlapped: *windows.OVERLAPPED, | |
| 391 | r: *Reader, | |
| 392 | small_buf: *[128]u8, | |
| 393 | bump_amt: usize, | |
| 394 | ) !enum { empty, populated, closed_populated, closed } { | |
| 395 | var read_any_data = false; | |
| 396 | while (true) { | |
| 397 | const fifo_read_pending = while (true) { | |
| 398 | const buf = try writableSliceGreedyAlloc(r, gpa, bump_amt); | |
| 399 | const buf_len = math.cast(u32, buf.len) orelse math.maxInt(u32); | |
| 373 | 400 | |
| 374 | if (num_bytes_read == buf_len) { | |
| 375 | // We filled the buffer, so there's probably more data available. | |
| 376 | continue; | |
| 377 | } else { | |
| 378 | // We didn't fill the buffer, so assume we're out of data. | |
| 379 | // There is no pending read. | |
| 380 | break false; | |
| 381 | } | |
| 382 | }; | |
| 401 | if (0 == windows.kernel32.ReadFile( | |
| 402 | handle, | |
| 403 | buf.ptr, | |
| 404 | buf_len, | |
| 405 | &win_dummy_bytes_read, | |
| 406 | overlapped, | |
| 407 | )) switch (windows.GetLastError()) { | |
| 408 | .IO_PENDING => break true, | |
| 409 | .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed, | |
| 410 | else => |err| return windows.unexpectedError(err), | |
| 411 | }; | |
| 383 | 412 | |
| 384 | if (fifo_read_pending) cancel_read: { | |
| 385 | // Cancel the pending read into the FIFO. | |
| 386 | _ = windows.kernel32.CancelIo(handle); | |
| 413 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { | |
| 414 | .success => |n| n, | |
| 415 | .closed => return if (read_any_data) .closed_populated else .closed, | |
| 416 | .aborted => unreachable, | |
| 417 | }; | |
| 387 | 418 | |
| 388 | // We have to wait for the handle to be signalled, i.e. for the cancellation to complete. | |
| 389 | switch (windows.kernel32.WaitForSingleObject(handle, windows.INFINITE)) { | |
| 390 | windows.WAIT_OBJECT_0 => {}, | |
| 391 | windows.WAIT_FAILED => return windows.unexpectedError(windows.GetLastError()), | |
| 392 | else => unreachable, | |
| 393 | } | |
| 419 | read_any_data = true; | |
| 420 | advanceBufferEnd(r, num_bytes_read); | |
| 394 | 421 | |
| 395 | // If it completed before we canceled, make sure to tell the FIFO! | |
| 396 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, true)) { | |
| 397 | .success => |n| n, | |
| 398 | .closed => return if (read_any_data) .closed_populated else .closed, | |
| 399 | .aborted => break :cancel_read, | |
| 400 | }; | |
| 401 | read_any_data = true; | |
| 402 | r.update(num_bytes_read); | |
| 403 | } | |
| 422 | if (num_bytes_read == buf_len) { | |
| 423 | // We filled the buffer, so there's probably more data available. | |
| 424 | continue; | |
| 425 | } else { | |
| 426 | // We didn't fill the buffer, so assume we're out of data. | |
| 427 | // There is no pending read. | |
| 428 | break false; | |
| 429 | } | |
| 430 | }; | |
| 404 | 431 | |
| 405 | // Try to queue the 1-byte read. | |
| 406 | if (0 == windows.kernel32.ReadFile( | |
| 407 | handle, | |
| 408 | small_buf, | |
| 409 | small_buf.len, | |
| 410 | &win_dummy_bytes_read, | |
| 411 | overlapped, | |
| 412 | )) switch (windows.GetLastError()) { | |
| 413 | .IO_PENDING => { | |
| 414 | // 1-byte read pending as intended | |
| 415 | return if (read_any_data) .populated else .empty; | |
| 416 | }, | |
| 417 | .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed, | |
| 418 | else => |err| return windows.unexpectedError(err), | |
| 419 | }; | |
| 432 | if (fifo_read_pending) cancel_read: { | |
| 433 | // Cancel the pending read into the FIFO. | |
| 434 | _ = windows.kernel32.CancelIo(handle); | |
| 420 | 435 | |
| 421 | // We got data back this time. Write it to the FIFO and run the main loop again. | |
| 422 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { | |
| 423 | .success => |n| n, | |
| 424 | .closed => return if (read_any_data) .closed_populated else .closed, | |
| 425 | .aborted => unreachable, | |
| 426 | }; | |
| 427 | try r.write(small_buf[0..num_bytes_read]); | |
| 428 | read_any_data = true; | |
| 429 | } | |
| 430 | } | |
| 436 | // We have to wait for the handle to be signalled, i.e. for the cancellation to complete. | |
| 437 | switch (windows.kernel32.WaitForSingleObject(handle, windows.INFINITE)) { | |
| 438 | windows.WAIT_OBJECT_0 => {}, | |
| 439 | windows.WAIT_FAILED => return windows.unexpectedError(windows.GetLastError()), | |
| 440 | else => unreachable, | |
| 441 | } | |
| 431 | 442 | |
| 432 | /// Simple wrapper around `GetOverlappedResult` to determine the result of a `ReadFile` operation. | |
| 433 | /// If `!allow_aborted`, then `aborted` is never returned (`OPERATION_ABORTED` is considered unexpected). | |
| 434 | /// | |
| 435 | /// The `ReadFile` documentation states that the number of bytes read by an overlapped `ReadFile` must be determined using `GetOverlappedResult`, even if the | |
| 436 | /// operation immediately returns data: | |
| 437 | /// "Use NULL for [lpNumberOfBytesRead] if this is an asynchronous operation to avoid potentially | |
| 438 | /// erroneous results." | |
| 439 | /// "If `hFile` was opened with `FILE_FLAG_OVERLAPPED`, the following conditions are in effect: [...] | |
| 440 | /// The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult function to | |
| 441 | /// get the actual number of bytes read." | |
| 442 | /// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile | |
| 443 | fn windowsGetReadResult( | |
| 444 | handle: windows.HANDLE, | |
| 445 | overlapped: *windows.OVERLAPPED, | |
| 446 | allow_aborted: bool, | |
| 447 | ) !union(enum) { | |
| 448 | success: u32, | |
| 449 | closed, | |
| 450 | aborted, | |
| 451 | } { | |
| 452 | var num_bytes_read: u32 = undefined; | |
| 453 | if (0 == windows.kernel32.GetOverlappedResult( | |
| 454 | handle, | |
| 455 | overlapped, | |
| 456 | &num_bytes_read, | |
| 457 | 0, | |
| 458 | )) switch (windows.GetLastError()) { | |
| 459 | .BROKEN_PIPE => return .closed, | |
| 460 | .OPERATION_ABORTED => |err| if (allow_aborted) { | |
| 461 | return .aborted; | |
| 462 | } else { | |
| 463 | return windows.unexpectedError(err); | |
| 464 | }, | |
| 465 | else => |err| return windows.unexpectedError(err), | |
| 443 | // If it completed before we canceled, make sure to tell the FIFO! | |
| 444 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, true)) { | |
| 445 | .success => |n| n, | |
| 446 | .closed => return if (read_any_data) .closed_populated else .closed, | |
| 447 | .aborted => break :cancel_read, | |
| 448 | }; | |
| 449 | read_any_data = true; | |
| 450 | advanceBufferEnd(r, num_bytes_read); | |
| 451 | } | |
| 452 | ||
| 453 | // Try to queue the 1-byte read. | |
| 454 | if (0 == windows.kernel32.ReadFile( | |
| 455 | handle, | |
| 456 | small_buf, | |
| 457 | small_buf.len, | |
| 458 | &win_dummy_bytes_read, | |
| 459 | overlapped, | |
| 460 | )) switch (windows.GetLastError()) { | |
| 461 | .IO_PENDING => { | |
| 462 | // 1-byte read pending as intended | |
| 463 | return if (read_any_data) .populated else .empty; | |
| 464 | }, | |
| 465 | .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed, | |
| 466 | else => |err| return windows.unexpectedError(err), | |
| 467 | }; | |
| 468 | ||
| 469 | // We got data back this time. Write it to the FIFO and run the main loop again. | |
| 470 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { | |
| 471 | .success => |n| n, | |
| 472 | .closed => return if (read_any_data) .closed_populated else .closed, | |
| 473 | .aborted => unreachable, | |
| 474 | }; | |
| 475 | const buf = small_buf[0..num_bytes_read]; | |
| 476 | const dest = try writableSliceGreedyAlloc(r, gpa, buf.len); | |
| 477 | @memcpy(dest[0..buf.len], buf); | |
| 478 | advanceBufferEnd(r, buf.len); | |
| 479 | read_any_data = true; | |
| 480 | } | |
| 481 | } | |
| 482 | ||
| 483 | /// Simple wrapper around `GetOverlappedResult` to determine the result of a `ReadFile` operation. | |
| 484 | /// If `!allow_aborted`, then `aborted` is never returned (`OPERATION_ABORTED` is considered unexpected). | |
| 485 | /// | |
| 486 | /// The `ReadFile` documentation states that the number of bytes read by an overlapped `ReadFile` must be determined using `GetOverlappedResult`, even if the | |
| 487 | /// operation immediately returns data: | |
| 488 | /// "Use NULL for [lpNumberOfBytesRead] if this is an asynchronous operation to avoid potentially | |
| 489 | /// erroneous results." | |
| 490 | /// "If `hFile` was opened with `FILE_FLAG_OVERLAPPED`, the following conditions are in effect: [...] | |
| 491 | /// The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult function to | |
| 492 | /// get the actual number of bytes read." | |
| 493 | /// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile | |
| 494 | fn windowsGetReadResult( | |
| 495 | handle: windows.HANDLE, | |
| 496 | overlapped: *windows.OVERLAPPED, | |
| 497 | allow_aborted: bool, | |
| 498 | ) !union(enum) { | |
| 499 | success: u32, | |
| 500 | closed, | |
| 501 | aborted, | |
| 502 | } { | |
| 503 | var num_bytes_read: u32 = undefined; | |
| 504 | if (0 == windows.kernel32.GetOverlappedResult( | |
| 505 | handle, | |
| 506 | overlapped, | |
| 507 | &num_bytes_read, | |
| 508 | 0, | |
| 509 | )) switch (windows.GetLastError()) { | |
| 510 | .BROKEN_PIPE => return .closed, | |
| 511 | .OPERATION_ABORTED => |err| if (allow_aborted) { | |
| 512 | return .aborted; | |
| 513 | } else { | |
| 514 | return windows.unexpectedError(err); | |
| 515 | }, | |
| 516 | else => |err| return windows.unexpectedError(err), | |
| 517 | }; | |
| 518 | return .{ .success = num_bytes_read }; | |
| 519 | } | |
| 466 | 520 | }; |
| 467 | return .{ .success = num_bytes_read }; | |
| 468 | 521 | } |
| 469 | 522 | |
| 470 | 523 | /// Given an enum, returns a struct with fields of that enum, each field |
lib/std/Io/Reader.zig-31| ... | ... | @@ -1241,37 +1241,6 @@ pub fn fillAlloc(r: *Reader, allocator: Allocator, n: usize) FillAllocError!void |
| 1241 | 1241 | return fill(r, n); |
| 1242 | 1242 | } |
| 1243 | 1243 | |
| 1244 | /// Returns a slice into the unused capacity of `buffer` with at least | |
| 1245 | /// `min_len` bytes, extending `buffer` by resizing it with `gpa` as necessary. | |
| 1246 | /// | |
| 1247 | /// After calling this function, typically the caller will follow up with a | |
| 1248 | /// call to `advanceBufferEnd` to report the actual number of bytes buffered. | |
| 1249 | pub fn writableSliceGreedyAlloc(r: *Reader, allocator: Allocator, min_len: usize) Allocator.Error![]u8 { | |
| 1250 | { | |
| 1251 | const unused = r.buffer[r.end..]; | |
| 1252 | if (unused.len >= min_len) return unused; | |
| 1253 | } | |
| 1254 | if (r.seek > 0) rebase(r); | |
| 1255 | { | |
| 1256 | var list: ArrayList(u8) = .{ | |
| 1257 | .items = r.buffer[0..r.end], | |
| 1258 | .capacity = r.buffer.len, | |
| 1259 | }; | |
| 1260 | defer r.buffer = list.allocatedSlice(); | |
| 1261 | try list.ensureUnusedCapacity(allocator, min_len); | |
| 1262 | } | |
| 1263 | const unused = r.buffer[r.end..]; | |
| 1264 | assert(unused.len >= min_len); | |
| 1265 | return unused; | |
| 1266 | } | |
| 1267 | ||
| 1268 | /// After writing directly into the unused capacity of `buffer`, this function | |
| 1269 | /// updates `end` so that users of `Reader` can receive the data. | |
| 1270 | pub fn advanceBufferEnd(r: *Reader, n: usize) void { | |
| 1271 | assert(n <= r.buffer.len - r.end); | |
| 1272 | r.end += n; | |
| 1273 | } | |
| 1274 | ||
| 1275 | 1244 | fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Result { |
| 1276 | 1245 | const result_info = @typeInfo(Result).int; |
| 1277 | 1246 | comptime assert(result_info.bits % 7 == 0); |
lib/std/c.zig+1-1| ... | ... | @@ -7147,7 +7147,7 @@ pub const dirent = switch (native_os) { |
| 7147 | 7147 | off: off_t, |
| 7148 | 7148 | reclen: c_ushort, |
| 7149 | 7149 | type: u8, |
| 7150 | name: [256:0]u8, | |
| 7150 | name: [255:0]u8, | |
| 7151 | 7151 | }, |
| 7152 | 7152 | else => void, |
| 7153 | 7153 | }; |
lib/std/posix.zig+21-4| ... | ... | @@ -192,10 +192,27 @@ pub const iovec_const = extern struct { |
| 192 | 192 | len: usize, |
| 193 | 193 | }; |
| 194 | 194 | |
| 195 | pub const ACCMODE = enum(u2) { | |
| 196 | RDONLY = 0, | |
| 197 | WRONLY = 1, | |
| 198 | RDWR = 2, | |
| 195 | pub const ACCMODE = switch (native_os) { | |
| 196 | // POSIX has a note about the access mode values: | |
| 197 | // | |
| 198 | // In historical implementations the value of O_RDONLY is zero. Because of | |
| 199 | // that, it is not possible to detect the presence of O_RDONLY and another | |
| 200 | // option. Future implementations should encode O_RDONLY and O_WRONLY as | |
| 201 | // bit flags so that: O_RDONLY | O_WRONLY == O_RDWR | |
| 202 | // | |
| 203 | // In practice SerenityOS is the only system supported by Zig that | |
| 204 | // implements this suggestion. | |
| 205 | // https://github.com/SerenityOS/serenity/blob/4adc51fdf6af7d50679c48b39362e062f5a3b2cb/Kernel/API/POSIX/fcntl.h#L28-L30 | |
| 206 | .serenity => enum(u2) { | |
| 207 | RDONLY = 1, | |
| 208 | WRONLY = 2, | |
| 209 | RDWR = 3, | |
| 210 | }, | |
| 211 | else => enum(u2) { | |
| 212 | RDONLY = 0, | |
| 213 | WRONLY = 1, | |
| 214 | RDWR = 2, | |
| 215 | }, | |
| 199 | 216 | }; |
| 200 | 217 | |
| 201 | 218 | pub const TCSA = enum(c_uint) { |
lib/std/process/Child.zig+38-29| ... | ... | @@ -14,6 +14,7 @@ const assert = std.debug.assert; |
| 14 | 14 | const native_os = builtin.os.tag; |
| 15 | 15 | const Allocator = std.mem.Allocator; |
| 16 | 16 | const ChildProcess = @This(); |
| 17 | const ArrayList = std.ArrayListUnmanaged; | |
| 17 | 18 | |
| 18 | 19 | pub const Id = switch (native_os) { |
| 19 | 20 | .windows => windows.HANDLE, |
| ... | ... | @@ -348,18 +349,6 @@ pub const RunResult = struct { |
| 348 | 349 | stderr: []u8, |
| 349 | 350 | }; |
| 350 | 351 | |
| 351 | fn writeBufferedReaderToArrayList(allocator: Allocator, list: *std.ArrayListUnmanaged(u8), r: *std.Io.Reader) !void { | |
| 352 | assert(r.seek == 0); | |
| 353 | if (list.capacity == 0) { | |
| 354 | list.* = .{ | |
| 355 | .items = r.buffered(), | |
| 356 | .capacity = r.buffer.len, | |
| 357 | }; | |
| 358 | } else { | |
| 359 | try list.appendSlice(allocator, r.buffered()); | |
| 360 | } | |
| 361 | } | |
| 362 | ||
| 363 | 352 | /// Collect the output from the process's stdout and stderr. Will return once all output |
| 364 | 353 | /// has been collected. This does not mean that the process has ended. `wait` should still |
| 365 | 354 | /// be called to wait for and clean up the process. |
| ... | ... | @@ -369,8 +358,8 @@ pub fn collectOutput( |
| 369 | 358 | child: ChildProcess, |
| 370 | 359 | /// Used for `stdout` and `stderr`. |
| 371 | 360 | allocator: Allocator, |
| 372 | stdout: *std.ArrayListUnmanaged(u8), | |
| 373 | stderr: *std.ArrayListUnmanaged(u8), | |
| 361 | stdout: *ArrayList(u8), | |
| 362 | stderr: *ArrayList(u8), | |
| 374 | 363 | max_output_bytes: usize, |
| 375 | 364 | ) !void { |
| 376 | 365 | assert(child.stdout_behavior == .Pipe); |
| ... | ... | @@ -382,15 +371,35 @@ pub fn collectOutput( |
| 382 | 371 | }); |
| 383 | 372 | defer poller.deinit(); |
| 384 | 373 | |
| 374 | const stdout_r = poller.reader(.stdout); | |
| 375 | stdout_r.buffer = stdout.allocatedSlice(); | |
| 376 | stdout_r.seek = 0; | |
| 377 | stdout_r.end = stdout.items.len; | |
| 378 | ||
| 379 | const stderr_r = poller.reader(.stderr); | |
| 380 | stderr_r.buffer = stderr.allocatedSlice(); | |
| 381 | stderr_r.seek = 0; | |
| 382 | stderr_r.end = stderr.items.len; | |
| 383 | ||
| 384 | defer { | |
| 385 | stdout.* = .{ | |
| 386 | .items = stdout_r.buffer[0..stdout_r.end], | |
| 387 | .capacity = stdout_r.buffer.len, | |
| 388 | }; | |
| 389 | stderr.* = .{ | |
| 390 | .items = stderr_r.buffer[0..stderr_r.end], | |
| 391 | .capacity = stderr_r.buffer.len, | |
| 392 | }; | |
| 393 | stdout_r.buffer = &.{}; | |
| 394 | stderr_r.buffer = &.{}; | |
| 395 | } | |
| 396 | ||
| 385 | 397 | while (try poller.poll()) { |
| 386 | if (poller.reader(.stdout).bufferedLen() > max_output_bytes) | |
| 398 | if (stdout_r.bufferedLen() > max_output_bytes) | |
| 387 | 399 | return error.StdoutStreamTooLong; |
| 388 | if (poller.reader(.stderr).bufferedLen() > max_output_bytes) | |
| 400 | if (stderr_r.bufferedLen() > max_output_bytes) | |
| 389 | 401 | return error.StderrStreamTooLong; |
| 390 | 402 | } |
| 391 | ||
| 392 | try writeBufferedReaderToArrayList(allocator, stdout, poller.reader(.stdout)); | |
| 393 | try writeBufferedReaderToArrayList(allocator, stderr, poller.reader(.stderr)); | |
| 394 | 403 | } |
| 395 | 404 | |
| 396 | 405 | pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix.PollError || error{ |
| ... | ... | @@ -420,10 +429,10 @@ pub fn run(args: struct { |
| 420 | 429 | child.expand_arg0 = args.expand_arg0; |
| 421 | 430 | child.progress_node = args.progress_node; |
| 422 | 431 | |
| 423 | var stdout: std.ArrayListUnmanaged(u8) = .empty; | |
| 424 | errdefer stdout.deinit(args.allocator); | |
| 425 | var stderr: std.ArrayListUnmanaged(u8) = .empty; | |
| 426 | errdefer stderr.deinit(args.allocator); | |
| 432 | var stdout: ArrayList(u8) = .empty; | |
| 433 | defer stdout.deinit(args.allocator); | |
| 434 | var stderr: ArrayList(u8) = .empty; | |
| 435 | defer stderr.deinit(args.allocator); | |
| 427 | 436 | |
| 428 | 437 | try child.spawn(); |
| 429 | 438 | errdefer { |
| ... | ... | @@ -431,7 +440,7 @@ pub fn run(args: struct { |
| 431 | 440 | } |
| 432 | 441 | try child.collectOutput(args.allocator, &stdout, &stderr, args.max_output_bytes); |
| 433 | 442 | |
| 434 | return RunResult{ | |
| 443 | return .{ | |
| 435 | 444 | .stdout = try stdout.toOwnedSlice(args.allocator), |
| 436 | 445 | .stderr = try stderr.toOwnedSlice(args.allocator), |
| 437 | 446 | .term = try child.wait(), |
| ... | ... | @@ -877,12 +886,12 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void { |
| 877 | 886 | var cmd_line_cache = WindowsCommandLineCache.init(self.allocator, self.argv); |
| 878 | 887 | defer cmd_line_cache.deinit(); |
| 879 | 888 | |
| 880 | var app_buf: std.ArrayListUnmanaged(u16) = .empty; | |
| 889 | var app_buf: ArrayList(u16) = .empty; | |
| 881 | 890 | defer app_buf.deinit(self.allocator); |
| 882 | 891 | |
| 883 | 892 | try app_buf.appendSlice(self.allocator, app_name_w); |
| 884 | 893 | |
| 885 | var dir_buf: std.ArrayListUnmanaged(u16) = .empty; | |
| 894 | var dir_buf: ArrayList(u16) = .empty; | |
| 886 | 895 | defer dir_buf.deinit(self.allocator); |
| 887 | 896 | |
| 888 | 897 | if (cwd_path_w.len > 0) { |
| ... | ... | @@ -1022,8 +1031,8 @@ const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); |
| 1022 | 1031 | /// Note: If the dir is the cwd, dir_buf should be empty (len = 0). |
| 1023 | 1032 | fn windowsCreateProcessPathExt( |
| 1024 | 1033 | allocator: mem.Allocator, |
| 1025 | dir_buf: *std.ArrayListUnmanaged(u16), | |
| 1026 | app_buf: *std.ArrayListUnmanaged(u16), | |
| 1034 | dir_buf: *ArrayList(u16), | |
| 1035 | app_buf: *ArrayList(u16), | |
| 1027 | 1036 | pathext: [:0]const u16, |
| 1028 | 1037 | cmd_line_cache: *WindowsCommandLineCache, |
| 1029 | 1038 | envp_ptr: ?[*]u16, |
| ... | ... | @@ -1506,7 +1515,7 @@ const WindowsCommandLineCache = struct { |
| 1506 | 1515 | /// Returns the absolute path of `cmd.exe` within the Windows system directory. |
| 1507 | 1516 | /// The caller owns the returned slice. |
| 1508 | 1517 | fn windowsCmdExePath(allocator: mem.Allocator) error{ OutOfMemory, Unexpected }![:0]u16 { |
| 1509 | var buf = try std.ArrayListUnmanaged(u16).initCapacity(allocator, 128); | |
| 1518 | var buf = try ArrayList(u16).initCapacity(allocator, 128); | |
| 1510 | 1519 | errdefer buf.deinit(allocator); |
| 1511 | 1520 | while (true) { |
| 1512 | 1521 | const unused_slice = buf.unusedCapacitySlice(); |
src/Compilation.zig+11-13| ... | ... | @@ -6215,19 +6215,20 @@ fn spawnZigRc( |
| 6215 | 6215 | return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) }); |
| 6216 | 6216 | }; |
| 6217 | 6217 | |
| 6218 | var poller = std.io.poll(comp.gpa, enum { stdout }, .{ | |
| 6218 | var poller = std.Io.poll(comp.gpa, enum { stdout, stderr }, .{ | |
| 6219 | 6219 | .stdout = child.stdout.?, |
| 6220 | .stderr = child.stderr.?, | |
| 6220 | 6221 | }); |
| 6221 | 6222 | defer poller.deinit(); |
| 6222 | 6223 | |
| 6223 | const stdout = poller.fifo(.stdout); | |
| 6224 | const stdout = poller.reader(.stdout); | |
| 6224 | 6225 | |
| 6225 | 6226 | poll: while (true) { |
| 6226 | while (stdout.readableLength() < @sizeOf(std.zig.Server.Message.Header)) if (!try poller.poll()) break :poll; | |
| 6227 | var header: std.zig.Server.Message.Header = undefined; | |
| 6228 | assert(stdout.read(std.mem.asBytes(&header)) == @sizeOf(std.zig.Server.Message.Header)); | |
| 6229 | while (stdout.readableLength() < header.bytes_len) if (!try poller.poll()) break :poll; | |
| 6230 | const body = stdout.readableSliceOfLen(header.bytes_len); | |
| 6227 | const MessageHeader = std.zig.Server.Message.Header; | |
| 6228 | while (stdout.buffered().len < @sizeOf(MessageHeader)) if (!try poller.poll()) break :poll; | |
| 6229 | const header = stdout.takeStruct(MessageHeader, .little) catch unreachable; | |
| 6230 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; | |
| 6231 | const body = stdout.take(header.bytes_len) catch unreachable; | |
| 6231 | 6232 | |
| 6232 | 6233 | switch (header.tag) { |
| 6233 | 6234 | // We expect exactly one ErrorBundle, and if any error_bundle header is |
| ... | ... | @@ -6250,13 +6251,10 @@ fn spawnZigRc( |
| 6250 | 6251 | }, |
| 6251 | 6252 | else => {}, // ignore other messages |
| 6252 | 6253 | } |
| 6253 | ||
| 6254 | stdout.discard(body.len); | |
| 6255 | 6254 | } |
| 6256 | 6255 | |
| 6257 | 6256 | // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace) |
| 6258 | const stderr_reader = child.stderr.?.deprecatedReader(); | |
| 6259 | const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); | |
| 6257 | const stderr = poller.reader(.stderr); | |
| 6260 | 6258 | |
| 6261 | 6259 | const term = child.wait() catch |err| { |
| 6262 | 6260 | return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv[0], @errorName(err) }); |
| ... | ... | @@ -6265,12 +6263,12 @@ fn spawnZigRc( |
| 6265 | 6263 | switch (term) { |
| 6266 | 6264 | .Exited => |code| { |
| 6267 | 6265 | if (code != 0) { |
| 6268 | log.err("zig rc failed with stderr:\n{s}", .{stderr}); | |
| 6266 | log.err("zig rc failed with stderr:\n{s}", .{stderr.buffered()}); | |
| 6269 | 6267 | return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code}); |
| 6270 | 6268 | } |
| 6271 | 6269 | }, |
| 6272 | 6270 | else => { |
| 6273 | log.err("zig rc terminated with stderr:\n{s}", .{stderr}); | |
| 6271 | log.err("zig rc terminated with stderr:\n{s}", .{stderr.buffered()}); | |
| 6274 | 6272 | return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); |
| 6275 | 6273 | }, |
| 6276 | 6274 | } |
src/arch/wasm/CodeGen.zig+9-12| ... | ... | @@ -1887,8 +1887,10 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1887 | 1887 | .call_never_tail => cg.airCall(inst, .never_tail), |
| 1888 | 1888 | .call_never_inline => cg.airCall(inst, .never_inline), |
| 1889 | 1889 | |
| 1890 | .is_err => cg.airIsErr(inst, .i32_ne), | |
| 1891 | .is_non_err => cg.airIsErr(inst, .i32_eq), | |
| 1890 | .is_err => cg.airIsErr(inst, .i32_ne, .value), | |
| 1891 | .is_non_err => cg.airIsErr(inst, .i32_eq, .value), | |
| 1892 | .is_err_ptr => cg.airIsErr(inst, .i32_ne, .ptr), | |
| 1893 | .is_non_err_ptr => cg.airIsErr(inst, .i32_eq, .ptr), | |
| 1892 | 1894 | |
| 1893 | 1895 | .is_null => cg.airIsNull(inst, .i32_eq, .value), |
| 1894 | 1896 | .is_non_null => cg.airIsNull(inst, .i32_ne, .value), |
| ... | ... | @@ -1971,8 +1973,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1971 | 1973 | .runtime_nav_ptr => cg.airRuntimeNavPtr(inst), |
| 1972 | 1974 | |
| 1973 | 1975 | .assembly, |
| 1974 | .is_err_ptr, | |
| 1975 | .is_non_err_ptr, | |
| 1976 | 1976 | |
| 1977 | 1977 | .err_return_trace, |
| 1978 | 1978 | .set_err_return_trace, |
| ... | ... | @@ -4106,7 +4106,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4106 | 4106 | return cg.finishAir(inst, .none, &.{br.operand}); |
| 4107 | 4107 | } |
| 4108 | 4108 | |
| 4109 | fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerError!void { | |
| 4109 | fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void { | |
| 4110 | 4110 | const zcu = cg.pt.zcu; |
| 4111 | 4111 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4112 | 4112 | const operand = try cg.resolveInst(un_op); |
| ... | ... | @@ -4123,7 +4123,7 @@ fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerEr |
| 4123 | 4123 | } |
| 4124 | 4124 | |
| 4125 | 4125 | try cg.emitWValue(operand); |
| 4126 | if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | |
| 4126 | if (op_kind == .ptr or pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | |
| 4127 | 4127 | try cg.addMemArg(.i32_load16_u, .{ |
| 4128 | 4128 | .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))), |
| 4129 | 4129 | .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?), |
| ... | ... | @@ -6463,9 +6463,6 @@ fn lowerTry( |
| 6463 | 6463 | operand_is_ptr: bool, |
| 6464 | 6464 | ) InnerError!WValue { |
| 6465 | 6465 | const zcu = cg.pt.zcu; |
| 6466 | if (operand_is_ptr) { | |
| 6467 | return cg.fail("TODO: lowerTry for pointers", .{}); | |
| 6468 | } | |
| 6469 | 6466 | |
| 6470 | 6467 | const pl_ty = err_union_ty.errorUnionPayload(zcu); |
| 6471 | 6468 | const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(zcu); |
| ... | ... | @@ -6476,7 +6473,7 @@ fn lowerTry( |
| 6476 | 6473 | |
| 6477 | 6474 | // check if the error tag is set for the error union. |
| 6478 | 6475 | try cg.emitWValue(err_union); |
| 6479 | if (pl_has_bits) { | |
| 6476 | if (pl_has_bits or operand_is_ptr) { | |
| 6480 | 6477 | const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu)); |
| 6481 | 6478 | try cg.addMemArg(.i32_load16_u, .{ |
| 6482 | 6479 | .offset = err_union.offset() + err_offset, |
| ... | ... | @@ -6498,12 +6495,12 @@ fn lowerTry( |
| 6498 | 6495 | } |
| 6499 | 6496 | |
| 6500 | 6497 | // if we reach here it means error was not set, and we want the payload |
| 6501 | if (!pl_has_bits) { | |
| 6498 | if (!pl_has_bits and !operand_is_ptr) { | |
| 6502 | 6499 | return .none; |
| 6503 | 6500 | } |
| 6504 | 6501 | |
| 6505 | 6502 | const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu)); |
| 6506 | if (isByRef(pl_ty, zcu, cg.target)) { | |
| 6503 | if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) { | |
| 6507 | 6504 | return buildPointerOffset(cg, err_union, pl_offset, .new); |
| 6508 | 6505 | } |
| 6509 | 6506 | const payload = try cg.load(err_union, pl_ty, pl_offset); |
src/target.zig+2| ... | ... | @@ -414,6 +414,8 @@ pub fn libcFullLinkFlags(target: *const std.Target) []const []const u8 { |
| 414 | 414 | .android, .androideabi, .ohos, .ohoseabi => &.{ "-lm", "-lc", "-ldl" }, |
| 415 | 415 | else => &.{ "-lm", "-lpthread", "-lc", "-ldl", "-lrt", "-lutil" }, |
| 416 | 416 | }, |
| 417 | // On SerenityOS libc includes libm, libpthread, libdl, and libssp. | |
| 418 | .serenity => &.{"-lc"}, | |
| 417 | 419 | else => &.{}, |
| 418 | 420 | }; |
| 419 | 421 | return result; |
test/behavior/try.zig+79| ... | ... | @@ -121,3 +121,82 @@ test "'return try' through conditional" { |
| 121 | 121 | comptime std.debug.assert(result == 123); |
| 122 | 122 | } |
| 123 | 123 | } |
| 124 | ||
| 125 | test "try ptr propagation const" { | |
| 126 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 127 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 128 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 129 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; | |
| 130 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 131 | ||
| 132 | const S = struct { | |
| 133 | fn foo0() !u32 { | |
| 134 | return 0; | |
| 135 | } | |
| 136 | ||
| 137 | fn foo1() error{Bad}!u32 { | |
| 138 | return 1; | |
| 139 | } | |
| 140 | ||
| 141 | fn foo2() anyerror!u32 { | |
| 142 | return 2; | |
| 143 | } | |
| 144 | ||
| 145 | fn doTheTest() !void { | |
| 146 | const res0: *const u32 = &(try foo0()); | |
| 147 | const res1: *const u32 = &(try foo1()); | |
| 148 | const res2: *const u32 = &(try foo2()); | |
| 149 | try expect(res0.* == 0); | |
| 150 | try expect(res1.* == 1); | |
| 151 | try expect(res2.* == 2); | |
| 152 | } | |
| 153 | }; | |
| 154 | try S.doTheTest(); | |
| 155 | try comptime S.doTheTest(); | |
| 156 | } | |
| 157 | ||
| 158 | test "try ptr propagation mutate" { | |
| 159 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 160 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 161 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 162 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; | |
| 163 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 164 | ||
| 165 | const S = struct { | |
| 166 | fn foo0() !u32 { | |
| 167 | return 0; | |
| 168 | } | |
| 169 | ||
| 170 | fn foo1() error{Bad}!u32 { | |
| 171 | return 1; | |
| 172 | } | |
| 173 | ||
| 174 | fn foo2() anyerror!u32 { | |
| 175 | return 2; | |
| 176 | } | |
| 177 | ||
| 178 | fn doTheTest() !void { | |
| 179 | var f0 = foo0(); | |
| 180 | var f1 = foo1(); | |
| 181 | var f2 = foo2(); | |
| 182 | ||
| 183 | const res0: *u32 = &(try f0); | |
| 184 | const res1: *u32 = &(try f1); | |
| 185 | const res2: *u32 = &(try f2); | |
| 186 | ||
| 187 | res0.* += 1; | |
| 188 | res1.* += 1; | |
| 189 | res2.* += 1; | |
| 190 | ||
| 191 | try expect(f0 catch unreachable == 1); | |
| 192 | try expect(f1 catch unreachable == 2); | |
| 193 | try expect(f2 catch unreachable == 3); | |
| 194 | ||
| 195 | try expect(res0.* == 1); | |
| 196 | try expect(res1.* == 2); | |
| 197 | try expect(res2.* == 3); | |
| 198 | } | |
| 199 | }; | |
| 200 | try S.doTheTest(); | |
| 201 | try comptime S.doTheTest(); | |
| 202 | } |
test/src/Cases.zig+2| ... | ... | @@ -800,6 +800,8 @@ const TestManifestConfigDefaults = struct { |
| 800 | 800 | } |
| 801 | 801 | // Windows |
| 802 | 802 | defaults = defaults ++ "x86_64-windows" ++ ","; |
| 803 | // Wasm | |
| 804 | defaults = defaults ++ "wasm32-wasi"; | |
| 803 | 805 | break :blk defaults; |
| 804 | 806 | }; |
| 805 | 807 | } else if (std.mem.eql(u8, key, "output_mode")) { |
test/tests.zig+9-10| ... | ... | @@ -1369,16 +1369,15 @@ const test_targets = blk: { |
| 1369 | 1369 | |
| 1370 | 1370 | // WASI Targets |
| 1371 | 1371 | |
| 1372 | // TODO: lowerTry for pointers | |
| 1373 | //.{ | |
| 1374 | // .target = .{ | |
| 1375 | // .cpu_arch = .wasm32, | |
| 1376 | // .os_tag = .wasi, | |
| 1377 | // .abi = .none, | |
| 1378 | // }, | |
| 1379 | // .use_llvm = false, | |
| 1380 | // .use_lld = false, | |
| 1381 | //}, | |
| 1372 | .{ | |
| 1373 | .target = .{ | |
| 1374 | .cpu_arch = .wasm32, | |
| 1375 | .os_tag = .wasi, | |
| 1376 | .abi = .none, | |
| 1377 | }, | |
| 1378 | .use_llvm = false, | |
| 1379 | .use_lld = false, | |
| 1380 | }, | |
| 1382 | 1381 | .{ |
| 1383 | 1382 | .target = .{ |
| 1384 | 1383 | .cpu_arch = .wasm32, |
tools/docgen.zig-1| ... | ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); |
| 3 | 3 | const io = std.io; |
| 4 | 4 | const fs = std.fs; |
| 5 | 5 | const process = std.process; |
| 6 | const ChildProcess = std.process.Child; | |
| 7 | 6 | const Progress = std.Progress; |
| 8 | 7 | const print = std.debug.print; |
| 9 | 8 | const mem = std.mem; |
tools/incr-check.zig+24-39| ... | ... | @@ -186,7 +186,7 @@ pub fn main() !void { |
| 186 | 186 | |
| 187 | 187 | try child.spawn(); |
| 188 | 188 | |
| 189 | var poller = std.io.poll(arena, Eval.StreamEnum, .{ | |
| 189 | var poller = std.Io.poll(arena, Eval.StreamEnum, .{ | |
| 190 | 190 | .stdout = child.stdout.?, |
| 191 | 191 | .stderr = child.stderr.?, |
| 192 | 192 | }); |
| ... | ... | @@ -247,19 +247,15 @@ const Eval = struct { |
| 247 | 247 | |
| 248 | 248 | fn check(eval: *Eval, poller: *Poller, update: Case.Update, prog_node: std.Progress.Node) !void { |
| 249 | 249 | const arena = eval.arena; |
| 250 | const Header = std.zig.Server.Message.Header; | |
| 251 | const stdout = poller.fifo(.stdout); | |
| 252 | const stderr = poller.fifo(.stderr); | |
| 250 | const stdout = poller.reader(.stdout); | |
| 251 | const stderr = poller.reader(.stderr); | |
| 253 | 252 | |
| 254 | 253 | poll: while (true) { |
| 255 | while (stdout.readableLength() < @sizeOf(Header)) { | |
| 256 | if (!(try poller.poll())) break :poll; | |
| 257 | } | |
| 258 | const header = stdout.reader().readStruct(Header) catch unreachable; | |
| 259 | while (stdout.readableLength() < header.bytes_len) { | |
| 260 | if (!(try poller.poll())) break :poll; | |
| 261 | } | |
| 262 | const body = stdout.readableSliceOfLen(header.bytes_len); | |
| 254 | const Header = std.zig.Server.Message.Header; | |
| 255 | while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll; | |
| 256 | const header = stdout.takeStruct(Header, .little) catch unreachable; | |
| 257 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; | |
| 258 | const body = stdout.take(header.bytes_len) catch unreachable; | |
| 263 | 259 | |
| 264 | 260 | switch (header.tag) { |
| 265 | 261 | .error_bundle => { |
| ... | ... | @@ -277,8 +273,8 @@ const Eval = struct { |
| 277 | 273 | .string_bytes = try arena.dupe(u8, string_bytes), |
| 278 | 274 | .extra = extra_array, |
| 279 | 275 | }; |
| 280 | if (stderr.readableLength() > 0) { | |
| 281 | const stderr_data = try stderr.toOwnedSlice(); | |
| 276 | if (stderr.bufferedLen() > 0) { | |
| 277 | const stderr_data = try poller.toOwnedSlice(.stderr); | |
| 282 | 278 | if (eval.allow_stderr) { |
| 283 | 279 | std.log.info("error_bundle included stderr:\n{s}", .{stderr_data}); |
| 284 | 280 | } else { |
| ... | ... | @@ -289,15 +285,14 @@ const Eval = struct { |
| 289 | 285 | try eval.checkErrorOutcome(update, result_error_bundle); |
| 290 | 286 | } |
| 291 | 287 | // This message indicates the end of the update. |
| 292 | stdout.discard(body.len); | |
| 293 | 288 | return; |
| 294 | 289 | }, |
| 295 | 290 | .emit_digest => { |
| 296 | 291 | const EbpHdr = std.zig.Server.Message.EmitDigest; |
| 297 | 292 | const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); |
| 298 | 293 | _ = ebp_hdr; |
| 299 | if (stderr.readableLength() > 0) { | |
| 300 | const stderr_data = try stderr.toOwnedSlice(); | |
| 294 | if (stderr.bufferedLen() > 0) { | |
| 295 | const stderr_data = try poller.toOwnedSlice(.stderr); | |
| 301 | 296 | if (eval.allow_stderr) { |
| 302 | 297 | std.log.info("emit_digest included stderr:\n{s}", .{stderr_data}); |
| 303 | 298 | } else { |
| ... | ... | @@ -308,7 +303,6 @@ const Eval = struct { |
| 308 | 303 | if (eval.target.backend == .sema) { |
| 309 | 304 | try eval.checkSuccessOutcome(update, null, prog_node); |
| 310 | 305 | // This message indicates the end of the update. |
| 311 | stdout.discard(body.len); | |
| 312 | 306 | } |
| 313 | 307 | |
| 314 | 308 | const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; |
| ... | ... | @@ -323,21 +317,18 @@ const Eval = struct { |
| 323 | 317 | |
| 324 | 318 | try eval.checkSuccessOutcome(update, bin_path, prog_node); |
| 325 | 319 | // This message indicates the end of the update. |
| 326 | stdout.discard(body.len); | |
| 327 | 320 | }, |
| 328 | 321 | else => { |
| 329 | 322 | // Ignore other messages. |
| 330 | stdout.discard(body.len); | |
| 331 | 323 | }, |
| 332 | 324 | } |
| 333 | 325 | } |
| 334 | 326 | |
| 335 | if (stderr.readableLength() > 0) { | |
| 336 | const stderr_data = try stderr.toOwnedSlice(); | |
| 327 | if (stderr.bufferedLen() > 0) { | |
| 337 | 328 | if (eval.allow_stderr) { |
| 338 | std.log.info("update '{s}' included stderr:\n{s}", .{ update.name, stderr_data }); | |
| 329 | std.log.info("update '{s}' included stderr:\n{s}", .{ update.name, stderr.buffered() }); | |
| 339 | 330 | } else { |
| 340 | eval.fatal("update '{s}' failed:\n{s}", .{ update.name, stderr_data }); | |
| 331 | eval.fatal("update '{s}' failed:\n{s}", .{ update.name, stderr.buffered() }); | |
| 341 | 332 | } |
| 342 | 333 | } |
| 343 | 334 | |
| ... | ... | @@ -537,25 +528,19 @@ const Eval = struct { |
| 537 | 528 | fn end(eval: *Eval, poller: *Poller) !void { |
| 538 | 529 | requestExit(eval.child, eval); |
| 539 | 530 | |
| 540 | const Header = std.zig.Server.Message.Header; | |
| 541 | const stdout = poller.fifo(.stdout); | |
| 542 | const stderr = poller.fifo(.stderr); | |
| 531 | const stdout = poller.reader(.stdout); | |
| 532 | const stderr = poller.reader(.stderr); | |
| 543 | 533 | |
| 544 | 534 | poll: while (true) { |
| 545 | while (stdout.readableLength() < @sizeOf(Header)) { | |
| 546 | if (!(try poller.poll())) break :poll; | |
| 547 | } | |
| 548 | const header = stdout.reader().readStruct(Header) catch unreachable; | |
| 549 | while (stdout.readableLength() < header.bytes_len) { | |
| 550 | if (!(try poller.poll())) break :poll; | |
| 551 | } | |
| 552 | const body = stdout.readableSliceOfLen(header.bytes_len); | |
| 553 | stdout.discard(body.len); | |
| 535 | const Header = std.zig.Server.Message.Header; | |
| 536 | while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll; | |
| 537 | const header = stdout.takeStruct(Header, .little) catch unreachable; | |
| 538 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; | |
| 539 | stdout.toss(header.bytes_len); | |
| 554 | 540 | } |
| 555 | 541 | |
| 556 | if (stderr.readableLength() > 0) { | |
| 557 | const stderr_data = try stderr.toOwnedSlice(); | |
| 558 | eval.fatal("unexpected stderr:\n{s}", .{stderr_data}); | |
| 542 | if (stderr.bufferedLen() > 0) { | |
| 543 | eval.fatal("unexpected stderr:\n{s}", .{stderr.buffered()}); | |
| 559 | 544 | } |
| 560 | 545 | } |
| 561 | 546 |