| ... | @@ -1340,8 +1340,6 @@ const AlertableSyscall = struct { | ... | @@ -1340,8 +1340,6 @@ const AlertableSyscall = struct { |
| 1340 | } | 1340 | } |
| 1341 | }; | 1341 | }; |
| 1342 | | 1342 | |
| 1343 | fn noopApc(_: ?*anyopaque, _: *windows.IO_STATUS_BLOCK, _: windows.ULONG) callconv(.winapi) void {} | | |
| 1344 | | | |
| 1345 | fn waitForApcOrAlert() void { | 1343 | fn waitForApcOrAlert() void { |
| 1346 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); | 1344 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); |
| 1347 | _ = windows.ntdll.NtDelayExecution(windows.TRUE, &infinite_timeout); | 1345 | _ = windows.ntdll.NtDelayExecution(windows.TRUE, &infinite_timeout); |
| ... | @@ -2500,7 +2498,10 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper | ... | @@ -2500,7 +2498,10 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper |
| 2500 | fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Batch.AwaitAsyncError!void { | 2498 | fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Batch.AwaitAsyncError!void { |
| 2501 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 2499 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 2502 | if (is_windows) { | 2500 | if (is_windows) { |
| 2503 | try batchAwaitWindows(b); | 2501 | batchAwaitWindows(b, false) catch |err| switch (err) { |
| | 2502 | error.ConcurrencyUnavailable => unreachable, // passed concurrency=false |
| | 2503 | else => |e| return e, |
| | 2504 | }; |
| 2504 | const alertable_syscall = try AlertableSyscall.start(); | 2505 | const alertable_syscall = try AlertableSyscall.start(); |
| 2505 | while (b.pending.head != .none and b.completions.head == .none) waitForApcOrAlert(); | 2506 | while (b.pending.head != .none and b.completions.head == .none) waitForApcOrAlert(); |
| 2506 | alertable_syscall.finish(); | 2507 | alertable_syscall.finish(); |
| ... | @@ -2616,7 +2617,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout | ... | @@ -2616,7 +2617,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout |
| 2616 | }, | 2617 | }, |
| 2617 | error.UnsupportedClock => |e| return e, | 2618 | error.UnsupportedClock => |e| return e, |
| 2618 | }; | 2619 | }; |
| 2619 | try batchAwaitWindows(b); | 2620 | try batchAwaitWindows(b, true); |
| 2620 | while (b.pending.head != .none and b.completions.head == .none) { | 2621 | while (b.pending.head != .none and b.completions.head == .none) { |
| 2621 | var delay_interval: windows.LARGE_INTEGER = interval: { | 2622 | var delay_interval: windows.LARGE_INTEGER = interval: { |
| 2622 | const d = deadline orelse break :interval std.math.minInt(windows.LARGE_INTEGER); | 2623 | const d = deadline orelse break :interval std.math.minInt(windows.LARGE_INTEGER); |
| ... | @@ -2810,7 +2811,8 @@ fn batchCancel(userdata: ?*anyopaque, b: *Io.Batch) void { | ... | @@ -2810,7 +2811,8 @@ fn batchCancel(userdata: ?*anyopaque, b: *Io.Batch) void { |
| 2810 | while (index != .none) { | 2811 | while (index != .none) { |
| 2811 | const pending = &b.storage[index.toIndex()].pending; | 2812 | const pending = &b.storage[index.toIndex()].pending; |
| 2812 | const context: *WindowsBatchPendingOperationContext = .fromErased(&pending.context); | 2813 | const context: *WindowsBatchPendingOperationContext = .fromErased(&pending.context); |
| 2813 | _ = windows.ntdll.NtCancelIoFile(context.file, &context.iosb); | 2814 | var cancel_iosb: windows.IO_STATUS_BLOCK = undefined; |
| | 2815 | _ = windows.ntdll.NtCancelIoFileEx(context.file, &context.iosb, &cancel_iosb); |
| 2814 | index = pending.node.next; | 2816 | index = pending.node.next; |
| 2815 | } | 2817 | } |
| 2816 | while (b.pending.head != .none) waitForApcOrAlert(); | 2818 | while (b.pending.head != .none) waitForApcOrAlert(); |
| ... | @@ -2860,30 +2862,94 @@ fn batchApc(apc_context: ?*anyopaque, iosb: *windows.IO_STATUS_BLOCK, _: windows | ... | @@ -2860,30 +2862,94 @@ fn batchApc(apc_context: ?*anyopaque, iosb: *windows.IO_STATUS_BLOCK, _: windows |
| 2860 | } | 2862 | } |
| 2861 | } | 2863 | } |
| 2862 | | 2864 | |
| 2863 | fn batchAwaitWindows(b: *Io.Batch) Io.Cancelable!void { | 2865 | /// If `concurrency` is false, `error.ConcurrencyUnavailable` is unreachable. |
| | 2866 | fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, ConcurrencyUnavailable }!void { |
| 2864 | var index = b.submissions.head; | 2867 | var index = b.submissions.head; |
| 2865 | errdefer b.submissions.head = index; | 2868 | errdefer b.submissions.head = index; |
| 2866 | while (index != .none) { | 2869 | while (index != .none) { |
| 2867 | const storage = &b.storage[index.toIndex()]; | 2870 | const storage = &b.storage[index.toIndex()]; |
| 2868 | const submission = storage.submission; | 2871 | const submission = storage.submission; |
| 2869 | errdefer storage.* = .{ .submission = submission }; | | |
| 2870 | storage.* = .{ .pending = .{ | 2872 | storage.* = .{ .pending = .{ |
| 2871 | .node = .{ .prev = b.pending.tail, .next = .none }, | 2873 | .node = .{ .prev = b.pending.tail, .next = .none }, |
| 2872 | .tag = submission.operation, | 2874 | .tag = submission.operation, |
| 2873 | .context = undefined, | 2875 | .context = undefined, |
| 2874 | } }; | 2876 | } }; |
| 2875 | const context: *WindowsBatchPendingOperationContext = .fromErased(&storage.pending.context); | | |
| 2876 | switch (submission.operation) { | | |
| 2877 | .file_read_streaming => |o| { | | |
| 2878 | context.file = o.file.handle; | | |
| 2879 | try ntReadFile(o.file.handle, o.data, &batchApc, b, &context.iosb); | | |
| 2880 | }, | | |
| 2881 | } | | |
| 2882 | switch (b.pending.tail) { | 2877 | switch (b.pending.tail) { |
| 2883 | .none => b.pending.head = index, | 2878 | .none => b.pending.head = index, |
| 2884 | else => |tail_index| b.storage[tail_index.toIndex()].pending.node.next = index, | 2879 | else => |tail_index| b.storage[tail_index.toIndex()].pending.node.next = index, |
| 2885 | } | 2880 | } |
| 2886 | b.pending.tail = index; | 2881 | b.pending.tail = index; |
| | 2882 | const context: *WindowsBatchPendingOperationContext = .fromErased(&storage.pending.context); |
| | 2883 | errdefer { |
| | 2884 | context.iosb.u.Status = .CANCELLED; |
| | 2885 | batchApc(b, &context.iosb, 0); |
| | 2886 | } |
| | 2887 | switch (submission.operation) { |
| | 2888 | .file_read_streaming => |o| o: { |
| | 2889 | var data_index: usize = 0; |
| | 2890 | while (o.data.len - data_index != 0 and o.data[data_index].len == 0) data_index += 1; |
| | 2891 | if (o.data.len - data_index == 0) { |
| | 2892 | context.iosb = .{ |
| | 2893 | .u = .{ .Status = .SUCCESS }, |
| | 2894 | .Information = 0, |
| | 2895 | }; |
| | 2896 | batchApc(b, &context.iosb, 0); |
| | 2897 | break :o; |
| | 2898 | } |
| | 2899 | const buffer = o.data[data_index]; |
| | 2900 | const short_buffer_len = @min(std.math.maxInt(u32), buffer.len); |
| | 2901 | |
| | 2902 | if (o.file.flags.nonblocking) { |
| | 2903 | context.file = o.file.handle; |
| | 2904 | switch (windows.ntdll.NtReadFile( |
| | 2905 | o.file.handle, |
| | 2906 | null, // event |
| | 2907 | &batchApc, |
| | 2908 | b, |
| | 2909 | &context.iosb, |
| | 2910 | buffer.ptr, |
| | 2911 | short_buffer_len, |
| | 2912 | null, // byte offset |
| | 2913 | null, // key |
| | 2914 | )) { |
| | 2915 | .PENDING, .SUCCESS => {}, |
| | 2916 | .CANCELLED => unreachable, |
| | 2917 | else => |status| { |
| | 2918 | context.iosb.u.Status = status; |
| | 2919 | batchApc(b, &context.iosb, 0); |
| | 2920 | }, |
| | 2921 | } |
| | 2922 | } else { |
| | 2923 | if (concurrency) return error.ConcurrencyUnavailable; |
| | 2924 | |
| | 2925 | const syscall: Syscall = try .start(); |
| | 2926 | while (true) switch (windows.ntdll.NtReadFile( |
| | 2927 | o.file.handle, |
| | 2928 | null, // event |
| | 2929 | null, // APC routine |
| | 2930 | null, // APC context |
| | 2931 | &context.iosb, |
| | 2932 | buffer.ptr, |
| | 2933 | short_buffer_len, |
| | 2934 | null, // byte offset |
| | 2935 | null, // key |
| | 2936 | )) { |
| | 2937 | .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag |
| | 2938 | .CANCELLED => { |
| | 2939 | try syscall.checkCancel(); |
| | 2940 | continue; |
| | 2941 | }, |
| | 2942 | else => |status| { |
| | 2943 | syscall.finish(); |
| | 2944 | |
| | 2945 | context.iosb.u.Status = status; |
| | 2946 | batchApc(b, &context.iosb, 0); |
| | 2947 | break; |
| | 2948 | }, |
| | 2949 | }; |
| | 2950 | } |
| | 2951 | }, |
| | 2952 | } |
| 2887 | index = submission.node.next; | 2953 | index = submission.node.next; |
| 2888 | } | 2954 | } |
| 2889 | b.submissions = .{ .head = .none, .tail = .none }; | 2955 | b.submissions = .{ .head = .none, .tail = .none }; |
| ... | @@ -8846,28 +8912,76 @@ fn fileReadStreamingPosix(file: File, data: []const []u8) File.ReadStreamingErro | ... | @@ -8846,28 +8912,76 @@ fn fileReadStreamingPosix(file: File, data: []const []u8) File.ReadStreamingErro |
| 8846 | } | 8912 | } |
| 8847 | | 8913 | |
| 8848 | fn fileReadStreamingWindows(file: File, data: []const []u8) File.ReadStreamingError!usize { | 8914 | fn fileReadStreamingWindows(file: File, data: []const []u8) File.ReadStreamingError!usize { |
| 8849 | var io_status_block: windows.IO_STATUS_BLOCK = .{ | 8915 | var index: usize = 0; |
| 8850 | .u = .{ .Status = .PENDING }, | 8916 | while (data.len - index != 0 and data[index].len == 0) index += 1; |
| 8851 | .Information = undefined, | 8917 | if (data.len - index == 0) return 0; |
| 8852 | }; | 8918 | const buffer = data[index]; |
| 8853 | try ntReadFile(file.handle, data, &noopApc, null, &io_status_block); | 8919 | const short_buffer_len = @min(std.math.maxInt(u32), buffer.len); |
| 8854 | | 8920 | |
| 8855 | while (@atomicLoad(windows.NTSTATUS, &io_status_block.u.Status, .acquire) == .PENDING) { | 8921 | var iosb: windows.IO_STATUS_BLOCK = undefined; |
| 8856 | // Once we get here we must not return from the function until the | 8922 | |
| 8857 | // operation completes, thereby releasing reference to io_status_block. | 8923 | if (!file.flags.nonblocking) { |
| 8858 | const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) { | 8924 | const syscall: Syscall = try .start(); |
| 8859 | error.Canceled => |e| { | 8925 | while (true) switch (windows.ntdll.NtReadFile( |
| 8860 | _ = windows.ntdll.NtCancelIoFile(file.handle, &io_status_block); | 8926 | file.handle, |
| 8861 | while (@atomicLoad(windows.NTSTATUS, &io_status_block.u.Status, .acquire) == .PENDING) { | 8927 | null, // event |
| 8862 | waitForApcOrAlert(); | 8928 | null, // APC routine |
| 8863 | } | 8929 | null, // APC context |
| 8864 | return e; | 8930 | &iosb, |
| | 8931 | buffer.ptr, |
| | 8932 | short_buffer_len, |
| | 8933 | null, // byte offset |
| | 8934 | null, // key |
| | 8935 | )) { |
| | 8936 | .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag |
| | 8937 | .CANCELLED => { |
| | 8938 | try syscall.checkCancel(); |
| | 8939 | continue; |
| | 8940 | }, |
| | 8941 | else => |status| { |
| | 8942 | syscall.finish(); |
| | 8943 | iosb.u.Status = status; |
| | 8944 | return ntReadFileResult(&iosb); |
| 8865 | }, | 8945 | }, |
| 8866 | }; | 8946 | }; |
| 8867 | waitForApcOrAlert(); | | |
| 8868 | alertable_syscall.finish(); | | |
| 8869 | } | 8947 | } |
| 8870 | return ntReadFileResult(&io_status_block); | 8948 | |
| | 8949 | var done: bool = false; |
| | 8950 | |
| | 8951 | switch (windows.ntdll.NtReadFile( |
| | 8952 | file.handle, |
| | 8953 | null, // event |
| | 8954 | flagApc, |
| | 8955 | &done, // APC context |
| | 8956 | &iosb, |
| | 8957 | buffer.ptr, |
| | 8958 | short_buffer_len, |
| | 8959 | null, // byte offset |
| | 8960 | null, // key |
| | 8961 | )) { |
| | 8962 | // We must wait for the APC routine. |
| | 8963 | .PENDING, .SUCCESS => while (!done) { |
| | 8964 | // Once we get here we must not return from the function until the |
| | 8965 | // operation completes, thereby releasing reference to io_status_block. |
| | 8966 | const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) { |
| | 8967 | error.Canceled => |e| { |
| | 8968 | var cancel_iosb: windows.IO_STATUS_BLOCK = undefined; |
| | 8969 | _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb); |
| | 8970 | while (!done) waitForApcOrAlert(); |
| | 8971 | return e; |
| | 8972 | }, |
| | 8973 | }; |
| | 8974 | waitForApcOrAlert(); |
| | 8975 | alertable_syscall.finish(); |
| | 8976 | }, |
| | 8977 | else => |status| iosb.u.Status = status, |
| | 8978 | } |
| | 8979 | return ntReadFileResult(&iosb); |
| | 8980 | } |
| | 8981 | |
| | 8982 | fn flagApc(userdata: ?*anyopaque, _: *windows.IO_STATUS_BLOCK, _: windows.ULONG) callconv(.winapi) void { |
| | 8983 | const flag: *bool = @ptrCast(userdata); |
| | 8984 | flag.* = true; |
| 8871 | } | 8985 | } |
| 8872 | | 8986 | |
| 8873 | fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { | 8987 | fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { |
| ... | @@ -8883,52 +8997,6 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { | ... | @@ -8883,52 +8997,6 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { |
| 8883 | } | 8997 | } |
| 8884 | } | 8998 | } |
| 8885 | | 8999 | |
| 8886 | fn ntReadFile( | | |
| 8887 | handle: windows.HANDLE, | | |
| 8888 | data: []const []u8, | | |
| 8889 | apcRoutine: ?*const windows.IO_APC_ROUTINE, | | |
| 8890 | apc_context: ?*anyopaque, | | |
| 8891 | iosb: *windows.IO_STATUS_BLOCK, | | |
| 8892 | ) Io.Cancelable!void { | | |
| 8893 | var index: usize = 0; | | |
| 8894 | while (index < data.len and data[index].len == 0) index += 1; | | |
| 8895 | if (index == data.len) { | | |
| 8896 | iosb.* = .{ .u = .{ .Status = .SUCCESS }, .Information = 0 }; | | |
| 8897 | if (apcRoutine) |routine| if (routine != &noopApc) { | | |
| 8898 | _ = windows.ntdll.NtQueueApcThread(windows.current_process, routine, apc_context, iosb, null); | | |
| 8899 | }; | | |
| 8900 | return; | | |
| 8901 | } | | |
| 8902 | const buffer = data[index]; | | |
| 8903 | | | |
| 8904 | const syscall: Syscall = try .start(); | | |
| 8905 | while (true) switch (windows.ntdll.NtReadFile( | | |
| 8906 | handle, | | |
| 8907 | null, // event | | |
| 8908 | apcRoutine, | | |
| 8909 | apc_context, | | |
| 8910 | iosb, | | |
| 8911 | buffer.ptr, | | |
| 8912 | @min(std.math.maxInt(u32), buffer.len), | | |
| 8913 | null, // byte offset | | |
| 8914 | null, // key | | |
| 8915 | )) { | | |
| 8916 | .PENDING => { | | |
| 8917 | syscall.finish(); | | |
| 8918 | return; | | |
| 8919 | }, | | |
| 8920 | .CANCELLED => { | | |
| 8921 | try syscall.checkCancel(); | | |
| 8922 | continue; | | |
| 8923 | }, | | |
| 8924 | else => |status| { | | |
| 8925 | syscall.finish(); | | |
| 8926 | iosb.u.Status = status; | | |
| 8927 | return; | | |
| 8928 | }, | | |
| 8929 | }; | | |
| 8930 | } | | |
| 8931 | | | |
| 8932 | fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize { | 9000 | fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize { |
| 8933 | if (!have_preadv) @compileError("TODO implement fileReadPositionalPosix for cursed operating systems that don't support preadv (it's only Haiku)"); | 9001 | if (!have_preadv) @compileError("TODO implement fileReadPositionalPosix for cursed operating systems that don't support preadv (it's only Haiku)"); |
| 8934 | | 9002 | |