authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-01 07:56:09+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-01 07:56:09+01:00
loge5454ff780ae4571cfa71a2edb6f4287eb8cf4de
tree49441358c6025c991572759183002e7c11abe7d8
parent3abc96a601d2349cc1743774f0cebb2eb0ea0c61
parentcc442d24ab172a6a2e5ee5210eca3e2f822629f8

Merge pull request 'std.Io: move fileWriteStreaming to Operation' (#31065) from more-poll into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31065

5 files changed, 219 insertions(+), 73 deletions(-)

lib/std/Io.zig+37-3
...@@ -184,7 +184,6 @@ pub const VTable = struct {...@@ -184,7 +184,6 @@ pub const VTable = struct {
184 fileStat: *const fn (?*anyopaque, File) File.StatError!File.Stat,184 fileStat: *const fn (?*anyopaque, File) File.StatError!File.Stat,
185 fileLength: *const fn (?*anyopaque, File) File.LengthError!u64,185 fileLength: *const fn (?*anyopaque, File) File.LengthError!u64,
186 fileClose: *const fn (?*anyopaque, []const File) void,186 fileClose: *const fn (?*anyopaque, []const File) void,
187 fileWriteStreaming: *const fn (?*anyopaque, File, header: []const u8, data: []const []const u8, splat: usize) File.Writer.Error!usize,
188 fileWritePositional: *const fn (?*anyopaque, File, header: []const u8, data: []const []const u8, splat: usize, offset: u64) File.WritePositionalError!usize,187 fileWritePositional: *const fn (?*anyopaque, File, header: []const u8, data: []const []const u8, splat: usize, offset: u64) File.WritePositionalError!usize,
189 fileWriteFileStreaming: *const fn (?*anyopaque, File, header: []const u8, *Io.File.Reader, Io.Limit) File.Writer.WriteFileError!usize,188 fileWriteFileStreaming: *const fn (?*anyopaque, File, header: []const u8, *Io.File.Reader, Io.Limit) File.Writer.WriteFileError!usize,
190 fileWriteFilePositional: *const fn (?*anyopaque, File, header: []const u8, *Io.File.Reader, Io.Limit, offset: u64) File.WriteFilePositionalError!usize,189 fileWriteFilePositional: *const fn (?*anyopaque, File, header: []const u8, *Io.File.Reader, Io.Limit, offset: u64) File.WriteFilePositionalError!usize,
...@@ -257,6 +256,7 @@ pub const VTable = struct {...@@ -257,6 +256,7 @@ pub const VTable = struct {
257256
258pub const Operation = union(enum) {257pub const Operation = union(enum) {
259 file_read_streaming: FileReadStreaming,258 file_read_streaming: FileReadStreaming,
259 file_write_streaming: FileWriteStreaming,
260260
261 pub const Tag = @typeInfo(Operation).@"union".tag_type.?;261 pub const Tag = @typeInfo(Operation).@"union".tag_type.?;
262262
...@@ -287,7 +287,41 @@ pub const Operation = union(enum) {...@@ -287,7 +287,41 @@ pub const Operation = union(enum) {
287 LockViolation,287 LockViolation,
288 } || Io.UnexpectedError;288 } || Io.UnexpectedError;
289289
290 pub const Result = usize;290 pub const Result = Error!usize;
291 };
292
293 pub const FileWriteStreaming = struct {
294 file: File,
295 header: []const u8 = &.{},
296 data: []const []const u8,
297 splat: usize = 1,
298
299 pub const Error = error{
300 DiskQuota,
301 FileTooBig,
302 InputOutput,
303 NoSpaceLeft,
304 DeviceBusy,
305 /// File descriptor does not hold the required rights to write to it.
306 AccessDenied,
307 PermissionDenied,
308 /// File is an unconnected socket, or closed its read end.
309 BrokenPipe,
310 /// Insufficient kernel memory to read from in_fd.
311 SystemResources,
312 NotOpenForWriting,
313 /// The process cannot access the file because another process has locked
314 /// a portion of the file. Windows-only.
315 LockViolation,
316 /// Non-blocking has been enabled and this operation would block.
317 WouldBlock,
318 /// This error occurs when a device gets disconnected before or mid-flush
319 /// while it's being written to - errno(6): No such device or address.
320 NoDevice,
321 FileBusy,
322 } || Io.UnexpectedError;
323
324 pub const Result = Error!usize;
291 };325 };
292326
293 pub const Result = Result: {327 pub const Result = Result: {
...@@ -296,7 +330,7 @@ pub const Operation = union(enum) {...@@ -296,7 +330,7 @@ pub const Operation = union(enum) {
296 var field_types: [operation_fields.len]type = undefined;330 var field_types: [operation_fields.len]type = undefined;
297 for (operation_fields, &field_names, &field_types) |field, *field_name, *field_type| {331 for (operation_fields, &field_names, &field_types) |field, *field_name, *field_type| {
298 field_name.* = field.name;332 field_name.* = field.name;
299 field_type.* = field.type.Error!field.type.Result;333 field_type.* = field.type.Result;
300 }334 }
301 break :Result @Union(.auto, Tag, &field_names, &field_types, &@splat(.{}));335 break :Result @Union(.auto, Tag, &field_names, &field_types, &@splat(.{}));
302 };336 };
lib/std/Io/File.zig+16-5
...@@ -572,16 +572,16 @@ pub fn setTimestampsNow(file: File, io: Io) SetTimestampsError!void {...@@ -572,16 +572,16 @@ pub fn setTimestampsNow(file: File, io: Io) SetTimestampsError!void {
572572
573pub const ReadStreamingError = error{EndOfStream} || Reader.Error;573pub const ReadStreamingError = error{EndOfStream} || Reader.Error;
574574
575/// Returns 0 on stream end or if `buffer` has no space available for data.575/// May return fewer bytes than buffer space available, including 0.
576/// End-of-stream is indicated by `error.EndOfStream`.
576///577///
577/// See also:578/// See also:
578/// * `reader`579/// * `reader`
579pub fn readStreaming(file: File, io: Io, buffer: []const []u8) ReadStreamingError!usize {580pub fn readStreaming(file: File, io: Io, buffer: []const []u8) ReadStreamingError!usize {
580 const result = try io.operate(.{ .file_read_streaming = .{581 return (try io.operate(.{ .file_read_streaming = .{
581 .file = file,582 .file = file,
582 .data = buffer,583 .data = buffer,
583 } });584 } })).file_read_streaming;
584 return result.file_read_streaming;
585}585}
586586
587pub const ReadPositionalError = error{587pub const ReadPositionalError = error{
...@@ -714,11 +714,22 @@ pub fn writerStreaming(file: File, io: Io, buffer: []u8) Writer {...@@ -714,11 +714,22 @@ pub fn writerStreaming(file: File, io: Io, buffer: []u8) Writer {
714 return .initStreaming(file, io, buffer);714 return .initStreaming(file, io, buffer);
715}715}
716716
717/// This is a low-level API that calls the `Io` interface function directly.
718/// For a higher level API, see `writerStreaming`.
719pub fn writeStreaming(file: File, io: Io, header: []const u8, data: []const []const u8, splat: usize) Writer.Error!usize {
720 return (try io.operate(.{ .file_write_streaming = .{
721 .file = file,
722 .header = header,
723 .data = data,
724 .splat = splat,
725 } })).file_write_streaming;
726}
727
717/// Equivalent to creating a streaming writer, writing `bytes`, and then flushing.728/// Equivalent to creating a streaming writer, writing `bytes`, and then flushing.
718pub fn writeStreamingAll(file: File, io: Io, bytes: []const u8) Writer.Error!void {729pub fn writeStreamingAll(file: File, io: Io, bytes: []const u8) Writer.Error!void {
719 var index: usize = 0;730 var index: usize = 0;
720 while (index < bytes.len) {731 while (index < bytes.len) {
721 index += try io.vtable.fileWriteStreaming(io.userdata, file, &.{}, &.{bytes[index..]}, 1);732 index += try writeStreaming(file, io, &.{}, &.{bytes[index..]}, 1);
722 }733 }
723}734}
724735
lib/std/Io/File/Writer.zig+2-25
...@@ -20,30 +20,7 @@ interface: Io.Writer,...@@ -20,30 +20,7 @@ interface: Io.Writer,
2020
21pub const Mode = File.Reader.Mode;21pub const Mode = File.Reader.Mode;
2222
23pub const Error = error{23pub const Error = Io.Operation.FileWriteStreaming.Error || Io.Cancelable;
24 DiskQuota,
25 FileTooBig,
26 InputOutput,
27 NoSpaceLeft,
28 DeviceBusy,
29 /// File descriptor does not hold the required rights to write to it.
30 AccessDenied,
31 PermissionDenied,
32 /// File is an unconnected socket, or closed its read end.
33 BrokenPipe,
34 /// Insufficient kernel memory to read from in_fd.
35 SystemResources,
36 NotOpenForWriting,
37 /// The process cannot access the file because another process has locked
38 /// a portion of the file. Windows-only.
39 LockViolation,
40 /// Non-blocking has been enabled and this operation would block.
41 WouldBlock,
42 /// This error occurs when a device gets disconnected before or mid-flush
43 /// while it's being written to - errno(6): No such device or address.
44 NoDevice,
45 FileBusy,
46} || Io.Cancelable || Io.UnexpectedError;
4724
48pub const WriteFileError = Error || error{25pub const WriteFileError = Error || error{
49 /// Descriptor is not valid or locked, or an mmap(2)-like operation is not available for in_fd.26 /// Descriptor is not valid or locked, or an mmap(2)-like operation is not available for in_fd.
...@@ -146,7 +123,7 @@ fn drainPositional(w: *Writer, data: []const []const u8, splat: usize) Io.Writer...@@ -146,7 +123,7 @@ fn drainPositional(w: *Writer, data: []const []const u8, splat: usize) Io.Writer
146fn drainStreaming(w: *Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize {123fn drainStreaming(w: *Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize {
147 const io = w.io;124 const io = w.io;
148 const header = w.interface.buffered();125 const header = w.interface.buffered();
149 const n = io.vtable.fileWriteStreaming(io.userdata, w.file, header, data, splat) catch |err| {126 const n = w.file.writeStreaming(io, header, data, splat) catch |err| {
150 w.err = err;127 w.err = err;
151 return error.WriteFailed;128 return error.WriteFailed;
152 };129 };
lib/std/Io/Threaded.zig+162-38
...@@ -1649,7 +1649,6 @@ pub fn io(t: *Threaded) Io {...@@ -1649,7 +1649,6 @@ pub fn io(t: *Threaded) Io {
1649 .fileStat = fileStat,1649 .fileStat = fileStat,
1650 .fileLength = fileLength,1650 .fileLength = fileLength,
1651 .fileClose = fileClose,1651 .fileClose = fileClose,
1652 .fileWriteStreaming = fileWriteStreaming,
1653 .fileWritePositional = fileWritePositional,1652 .fileWritePositional = fileWritePositional,
1654 .fileWriteFileStreaming = fileWriteFileStreaming,1653 .fileWriteFileStreaming = fileWriteFileStreaming,
1655 .fileWriteFilePositional = fileWriteFilePositional,1654 .fileWriteFilePositional = fileWriteFilePositional,
...@@ -1813,7 +1812,6 @@ pub fn ioBasic(t: *Threaded) Io {...@@ -1813,7 +1812,6 @@ pub fn ioBasic(t: *Threaded) Io {
1813 .fileStat = fileStat,1812 .fileStat = fileStat,
1814 .fileLength = fileLength,1813 .fileLength = fileLength,
1815 .fileClose = fileClose,1814 .fileClose = fileClose,
1816 .fileWriteStreaming = fileWriteStreaming,
1817 .fileWritePositional = fileWritePositional,1815 .fileWritePositional = fileWritePositional,
1818 .fileWriteFileStreaming = fileWriteFileStreaming,1816 .fileWriteFileStreaming = fileWriteFileStreaming,
1819 .fileWriteFilePositional = fileWriteFilePositional,1817 .fileWriteFilePositional = fileWriteFilePositional,
...@@ -2496,6 +2494,12 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper...@@ -2496,6 +2494,12 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper
2496 else => |e| e,2494 else => |e| e,
2497 },2495 },
2498 },2496 },
2497 .file_write_streaming => |o| return .{
2498 .file_write_streaming = fileWriteStreaming(t, o.file, o.header, o.data, o.splat) catch |err| switch (err) {
2499 error.Canceled => |e| return e,
2500 else => |e| e,
2501 },
2502 },
2499 }2503 }
2500}2504}
25012505
...@@ -2523,6 +2527,10 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void {...@@ -2523,6 +2527,10 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void {
2523 poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.IN, .revents = 0 };2527 poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.IN, .revents = 0 };
2524 poll_len += 1;2528 poll_len += 1;
2525 },2529 },
2530 .file_write_streaming => |o| {
2531 poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.OUT, .revents = 0 };
2532 poll_len += 1;
2533 },
2526 }2534 }
2527 index = submission.node.next;2535 index = submission.node.next;
2528 }2536 }
...@@ -2687,6 +2695,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout...@@ -2687,6 +2695,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout
2687 const submission = &b.storage[index.toIndex()].submission;2695 const submission = &b.storage[index.toIndex()].submission;
2688 switch (submission.operation) {2696 switch (submission.operation) {
2689 .file_read_streaming => |o| try poll_storage.add(o.file, posix.POLL.IN),2697 .file_read_streaming => |o| try poll_storage.add(o.file, posix.POLL.IN),
2698 .file_write_streaming => |o| try poll_storage.add(o.file, posix.POLL.OUT),
2690 }2699 }
2691 index = submission.node.next;2700 index = submission.node.next;
2692 }2701 }
...@@ -2864,6 +2873,7 @@ fn batchApc(apc_context: ?*anyopaque, iosb: *windows.IO_STATUS_BLOCK, _: windows...@@ -2864,6 +2873,7 @@ fn batchApc(apc_context: ?*anyopaque, iosb: *windows.IO_STATUS_BLOCK, _: windows
2864 b.completions.tail = .fromIndex(index);2873 b.completions.tail = .fromIndex(index);
2865 const result: Io.Operation.Result = switch (pending.tag) {2874 const result: Io.Operation.Result = switch (pending.tag) {
2866 .file_read_streaming => .{ .file_read_streaming = ntReadFileResult(iosb) },2875 .file_read_streaming => .{ .file_read_streaming = ntReadFileResult(iosb) },
2876 .file_write_streaming => .{ .file_write_streaming = ntWriteFileResult(iosb) },
2867 };2877 };
2868 storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };2878 storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
2869 },2879 },
...@@ -2950,6 +2960,66 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren...@@ -2950,6 +2960,66 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren
2950 else => |status| {2960 else => |status| {
2951 syscall.finish();2961 syscall.finish();
29522962
2963 context.iosb.u.Status = status;
2964 batchApc(b, &context.iosb, 0);
2965 break;
2966 },
2967 };
2968 }
2969 },
2970 .file_write_streaming => |o| o: {
2971 const buffer = windowsWriteBuffer(o.header, o.data, o.splat);
2972 if (buffer.len == 0) {
2973 context.iosb = .{
2974 .u = .{ .Status = .SUCCESS },
2975 .Information = 0,
2976 };
2977 batchApc(b, &context.iosb, 0);
2978 break :o;
2979 }
2980 if (o.file.flags.nonblocking) {
2981 context.file = o.file.handle;
2982 switch (windows.ntdll.NtWriteFile(
2983 o.file.handle,
2984 null, // event
2985 &batchApc,
2986 b,
2987 &context.iosb,
2988 buffer.ptr,
2989 @intCast(buffer.len),
2990 null, // byte offset
2991 null, // key
2992 )) {
2993 .PENDING, .SUCCESS => {},
2994 .CANCELLED => unreachable,
2995 else => |status| {
2996 context.iosb.u.Status = status;
2997 batchApc(b, &context.iosb, 0);
2998 },
2999 }
3000 } else {
3001 if (concurrency) return error.ConcurrencyUnavailable;
3002
3003 const syscall: Syscall = try .start();
3004 while (true) switch (windows.ntdll.NtWriteFile(
3005 o.file.handle,
3006 null, // event
3007 null, // APC routine
3008 null, // APC context
3009 &context.iosb,
3010 buffer.ptr,
3011 @intCast(buffer.len),
3012 null, // byte offset
3013 null, // key
3014 )) {
3015 .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
3016 .CANCELLED => {
3017 try syscall.checkCancel();
3018 continue;
3019 },
3020 else => |status| {
3021 syscall.finish();
3022
2953 context.iosb.u.Status = status;3023 context.iosb.u.Status = status;
2954 batchApc(b, &context.iosb, 0);3024 batchApc(b, &context.iosb, 0);
2955 break;3025 break;
...@@ -2963,6 +3033,21 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren...@@ -2963,6 +3033,21 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren
2963 b.submissions = .{ .head = .none, .tail = .none };3033 b.submissions = .{ .head = .none, .tail = .none };
2964}3034}
29653035
3036/// Since Windows only supports writing one contiguous buffer, returns the
3037/// first one, while also limiting it to a length representable by 32-bit
3038/// unsigned integer.
3039fn windowsWriteBuffer(header: []const u8, data: []const []const u8, splat: usize) []const u8 {
3040 const buffer = b: {
3041 if (header.len != 0) break :b header;
3042 for (data[0 .. data.len - 1]) |buffer| {
3043 if (buffer.len != 0) break :b buffer;
3044 }
3045 if (splat == 0) return &.{};
3046 break :b data[data.len - 1];
3047 };
3048 return buffer[0..@min(buffer.len, std.math.maxInt(u32))];
3049}
3050
2966fn submitComplete(ring: []u32, complete_tail: *Io.Batch.RingIndex, op: u32) void {3051fn submitComplete(ring: []u32, complete_tail: *Io.Batch.RingIndex, op: u32) void {
2967 const ct = complete_tail.*;3052 const ct = complete_tail.*;
2968 const len: u31 = @intCast(ring.len);3053 const len: u31 = @intCast(ring.len);
...@@ -9005,6 +9090,24 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {...@@ -9005,6 +9090,24 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
9005 }9090 }
9006}9091}
90079092
9093fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
9094 switch (io_status_block.u.Status) {
9095 .PENDING => unreachable,
9096 .CANCELLED => unreachable,
9097 .SUCCESS => return io_status_block.Information,
9098 .INVALID_USER_BUFFER => return error.SystemResources,
9099 .NO_MEMORY => return error.SystemResources,
9100 .QUOTA_EXCEEDED => return error.SystemResources,
9101 .PIPE_BROKEN => return error.BrokenPipe,
9102 .INVALID_HANDLE => return error.NotOpenForWriting,
9103 .LOCK_NOT_GRANTED => return error.LockViolation,
9104 .ACCESS_DENIED => return error.AccessDenied,
9105 .WORKING_SET_QUOTA => return error.SystemResources,
9106 .DISK_FULL => return error.NoSpaceLeft,
9107 else => |status| return windows.unexpectedStatus(status),
9108 }
9109}
9110
9008fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {9111fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
9009 if (!have_preadv) @compileError("TODO implement fileReadPositionalPosix for cursed operating systems that don't support preadv (it's only Haiku)");9112 if (!have_preadv) @compileError("TODO implement fileReadPositionalPosix for cursed operating systems that don't support preadv (it's only Haiku)");
90109113
...@@ -9837,16 +9940,9 @@ fn fileWriteStreaming(...@@ -9837,16 +9940,9 @@ fn fileWriteStreaming(
9837 _ = t;9940 _ = t;
98389941
9839 if (is_windows) {9942 if (is_windows) {
9840 if (header.len != 0) {9943 const buffer = windowsWriteBuffer(header, data, splat);
9841 return writeFileStreamingWindows(file.handle, header);9944 if (buffer.len == 0) return 0;
9842 }9945 return fileWriteStreamingWindows(file, buffer);
9843 for (data[0 .. data.len - 1]) |buf| {
9844 if (buf.len == 0) continue;
9845 return writeFileStreamingWindows(file.handle, buf);
9846 }
9847 const pattern = data[data.len - 1];
9848 if (pattern.len == 0 or splat == 0) return 0;
9849 return writeFileStreamingWindows(file.handle, pattern);
9850 }9946 }
98519947
9852 var iovecs: [max_iovecs_len]posix.iovec_const = undefined;9948 var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
...@@ -9953,38 +10049,66 @@ fn fileWriteStreaming(...@@ -9953,38 +10049,66 @@ fn fileWriteStreaming(
9953 }10049 }
9954}10050}
995510051
9956fn writeFileStreamingWindows(10052fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!usize {
9957 handle: windows.HANDLE,10053 assert(buffer.len != 0);
9958 bytes: []const u8,10054
9959) File.Writer.Error!usize {10055 var iosb: windows.IO_STATUS_BLOCK = undefined;
9960 assert(bytes.len != 0);10056
9961 var bytes_written: windows.DWORD = undefined;10057 if (file.flags.nonblocking) {
9962 const adjusted_len = std.math.lossyCast(u32, bytes.len);10058 var done: bool = false;
9963 const syscall: Syscall = try .start();10059 switch (windows.ntdll.NtWriteFile(
9964 while (true) {10060 file.handle,
9965 if (windows.kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, null) != 0) {10061 null, // event
9966 syscall.finish();10062 flagApc,
9967 return bytes_written;10063 &done, // APC context
10064 &iosb,
10065 buffer.ptr,
10066 @intCast(buffer.len),
10067 null, // byte offset
10068 null, // key
10069 )) {
10070 // We must wait for the APC routine.
10071 .PENDING, .SUCCESS => while (!done) {
10072 // Once we get here we must not return from the function until the
10073 // operation completes, thereby releasing reference to io_status_block.
10074 const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
10075 error.Canceled => |e| {
10076 var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
10077 _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
10078 while (!done) waitForApcOrAlert();
10079 return e;
10080 },
10081 };
10082 waitForApcOrAlert();
10083 alertable_syscall.finish();
10084 },
10085 else => |status| iosb.u.Status = status,
9968 }10086 }
9969 switch (windows.GetLastError()) {10087 return ntWriteFileResult(&iosb);
9970 .OPERATION_ABORTED => {10088 } else {
10089 const syscall: Syscall = try .start();
10090 while (true) switch (windows.ntdll.NtWriteFile(
10091 file.handle,
10092 null, // event
10093 null, // APC routine
10094 null, // APC context
10095 &iosb,
10096 buffer.ptr,
10097 @intCast(buffer.len),
10098 null, // byte offset
10099 null, // key
10100 )) {
10101 .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
10102 .CANCELLED => {
9971 try syscall.checkCancel();10103 try syscall.checkCancel();
9972 continue;10104 continue;
9973 },10105 },
9974 .INVALID_USER_BUFFER => return syscall.fail(error.SystemResources),10106 else => |status| {
9975 .NOT_ENOUGH_MEMORY => return syscall.fail(error.SystemResources),
9976 .NOT_ENOUGH_QUOTA => return syscall.fail(error.SystemResources),
9977 .NO_DATA => return syscall.fail(error.BrokenPipe),
9978 .INVALID_HANDLE => return syscall.fail(error.NotOpenForWriting),
9979 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),
9980 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
9981 .WORKING_SET_QUOTA => return syscall.fail(error.SystemResources),
9982 .DISK_FULL => return syscall.fail(error.NoSpaceLeft),
9983 else => |err| {
9984 syscall.finish();10107 syscall.finish();
9985 return windows.unexpectedError(err);10108 iosb.u.Status = status;
10109 return ntWriteFileResult(&iosb);
9986 },10110 },
9987 }10111 };
9988 }10112 }
9989}10113}
999010114
lib/std/Progress.zig+2-2
...@@ -1437,7 +1437,7 @@ fn writeIpc(io: Io, file: Io.File, serialized: Serialized) error{BrokenPipe}!voi...@@ -1437,7 +1437,7 @@ fn writeIpc(io: Io, file: Io.File, serialized: Serialized) error{BrokenPipe}!voi
1437 // We do this in a separate write call to give a better chance for the1437 // We do this in a separate write call to give a better chance for the
1438 // writev below to be in a single packet.1438 // writev below to be in a single packet.
1439 const n = @min(parents.len, remaining_write_trash_bytes);1439 const n = @min(parents.len, remaining_write_trash_bytes);
1440 if (io.vtable.fileWriteStreaming(io.userdata, file, &.{}, &.{parents[0..n]}, 1)) |written| {1440 if (file.writeStreaming(io, &.{}, &.{parents[0..n]}, 1)) |written| {
1441 remaining_write_trash_bytes -= written;1441 remaining_write_trash_bytes -= written;
1442 continue;1442 continue;
1443 } else |err| switch (err) {1443 } else |err| switch (err) {
...@@ -1478,7 +1478,7 @@ fn writevNonblock(io: Io, file: Io.File, iov: [][]const u8) Io.File.Writer.Error...@@ -1478,7 +1478,7 @@ fn writevNonblock(io: Io, file: Io.File, iov: [][]const u8) Io.File.Writer.Error
1478 return total_written) : (iov_index += 1) written -= iov[iov_index].len;1478 return total_written) : (iov_index += 1) written -= iov[iov_index].len;
1479 iov[iov_index].ptr += written;1479 iov[iov_index].ptr += written;
1480 iov[iov_index].len -= written;1480 iov[iov_index].len -= written;
1481 written = try io.vtable.fileWriteStreaming(io.userdata, file, &.{}, iov, 1);1481 written = try file.writeStreaming(io, &.{}, iov, 1);
1482 if (written == 0) return total_written;1482 if (written == 0) return total_written;
1483 total_written += written;1483 total_written += written;
1484 }1484 }