| ... | @@ -1255,6 +1255,32 @@ const AlertableSyscall = struct { | ... | @@ -1255,6 +1255,32 @@ const AlertableSyscall = struct { |
| 1255 | assert(is_windows); | 1255 | assert(is_windows); |
| 1256 | } | 1256 | } |
| 1257 | | 1257 | |
| | 1258 | fn start() Io.Cancelable!AlertableSyscall { |
| | 1259 | const thread = Thread.current orelse return .{ .thread = null }; |
| | 1260 | switch (thread.cancel_protection) { |
| | 1261 | .blocked => return .{ .thread = null }, |
| | 1262 | .unblocked => {}, |
| | 1263 | } |
| | 1264 | const old_status = thread.status.fetchOr(.{ |
| | 1265 | .cancelation = @enumFromInt(0b010), |
| | 1266 | .awaitable = .null, |
| | 1267 | }, .monotonic); |
| | 1268 | switch (old_status.cancelation) { |
| | 1269 | .parked => unreachable, |
| | 1270 | .blocked => unreachable, |
| | 1271 | .blocked_alertable => unreachable, |
| | 1272 | .blocked_canceling => unreachable, |
| | 1273 | .blocked_alertable_canceling => unreachable, |
| | 1274 | .none => return .{ .thread = thread }, // new status is `.blocked_alertable` |
| | 1275 | .canceling => { |
| | 1276 | // Status is unchanged (still `.canceling`)---change to `.canceled` before return. |
| | 1277 | thread.status.store(.{ .cancelation = .canceled, .awaitable = old_status.awaitable }, .monotonic); |
| | 1278 | return error.Canceled; |
| | 1279 | }, |
| | 1280 | .canceled => return .{ .thread = null }, // new status is `.canceled` (unchanged) |
| | 1281 | } |
| | 1282 | } |
| | 1283 | |
| 1258 | fn checkCancel(s: AlertableSyscall) Io.Cancelable!void { | 1284 | fn checkCancel(s: AlertableSyscall) Io.Cancelable!void { |
| 1259 | comptime assert(is_windows); | 1285 | comptime assert(is_windows); |
| 1260 | const thread = s.thread orelse return; | 1286 | const thread = s.thread orelse return; |
| ... | @@ -2501,10 +2527,10 @@ fn batchWait(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch. | ... | @@ -2501,10 +2527,10 @@ fn batchWait(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch. |
| 2501 | const op = ring[submit_head.index(len)]; | 2527 | const op = ring[submit_head.index(len)]; |
| 2502 | const operation = &operations[op]; | 2528 | const operation = &operations[op]; |
| 2503 | switch (operation.*) { | 2529 | switch (operation.*) { |
| 2504 | .noop => { | 2530 | .noop => |*o| { |
| 2505 | try operate(t, operation); | 2531 | _ = o.status.unstarted; |
| 2506 | ring[complete_tail.index(len)] = op; | 2532 | o.status = .{ .result = {} }; |
| 2507 | complete_tail = complete_tail.next(len); | 2533 | submitComplete(ring, &complete_tail, op); |
| 2508 | }, | 2534 | }, |
| 2509 | .file_read_streaming => |*o| { | 2535 | .file_read_streaming => |*o| { |
| 2510 | _ = o.status.unstarted; | 2536 | _ = o.status.unstarted; |
| ... | @@ -2524,8 +2550,7 @@ fn batchWait(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch. | ... | @@ -2524,8 +2550,7 @@ fn batchWait(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch. |
| 2524 | 1 => if (timeout == .none) { | 2550 | 1 => if (timeout == .none) { |
| 2525 | const op = map_buffer[0]; | 2551 | const op = map_buffer[0]; |
| 2526 | try operate(t, &operations[op]); | 2552 | try operate(t, &operations[op]); |
| 2527 | ring[complete_tail.index(len)] = op; | 2553 | submitComplete(ring, &complete_tail, op); |
| 2528 | complete_tail = complete_tail.next(len); | | |
| 2529 | poll_i = 0; | 2554 | poll_i = 0; |
| 2530 | return; | 2555 | return; |
| 2531 | }, | 2556 | }, |
| ... | @@ -2560,8 +2585,7 @@ fn batchWait(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch. | ... | @@ -2560,8 +2585,7 @@ fn batchWait(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch. |
| 2560 | ring[submit_head.index(len)] = op; | 2585 | ring[submit_head.index(len)] = op; |
| 2561 | } else { | 2586 | } else { |
| 2562 | try operate(t, &operations[op]); | 2587 | try operate(t, &operations[op]); |
| 2563 | ring[complete_tail.index(len)] = op; | 2588 | submitComplete(ring, &complete_tail, op); |
| 2564 | complete_tail = complete_tail.next(len); | | |
| 2565 | } | 2589 | } |
| 2566 | } | 2590 | } |
| 2567 | return; | 2591 | return; |
| ... | @@ -2584,19 +2608,49 @@ fn batchCancel(userdata: ?*anyopaque, b: *Io.Batch) void { | ... | @@ -2584,19 +2608,49 @@ fn batchCancel(userdata: ?*anyopaque, b: *Io.Batch) void { |
| 2584 | while (submit_head != submit_tail) : (submit_head = submit_head.next(len)) { | 2608 | while (submit_head != submit_tail) : (submit_head = submit_head.next(len)) { |
| 2585 | const op = ring[submit_head.index(len)]; | 2609 | const op = ring[submit_head.index(len)]; |
| 2586 | switch (operations[op]) { | 2610 | switch (operations[op]) { |
| 2587 | .noop => { | 2611 | .noop => |*o| { |
| 2588 | operate(t, &operations[op]) catch unreachable; | 2612 | _ = o.status.unstarted; |
| 2589 | ring[complete_tail.index(len)] = op; | 2613 | o.status = .{ .result = {} }; |
| 2590 | complete_tail = complete_tail.next(len); | 2614 | submitComplete(ring, &complete_tail, op); |
| 2591 | }, | 2615 | }, |
| 2592 | .file_read_streaming => |*o| _ = o.status.unstarted, | 2616 | .file_read_streaming => |*o| _ = o.status.unstarted, |
| 2593 | } | 2617 | } |
| 2594 | } | 2618 | } |
| | 2619 | if (is_windows) { |
| | 2620 | // Iterate over pending and issue cancelations, then free the allocation for IO_STATUS_BLOCK |
| | 2621 | if (b.impl.reserved) |reserved| { |
| | 2622 | const gpa = t.allocator; |
| | 2623 | const metadatas_ptr: [*]WinOpMetadata = @ptrCast(@alignCast(reserved)); |
| | 2624 | const metadatas = metadatas_ptr[0..b.operations.len]; |
| | 2625 | for (metadatas, 0..) |*metadata, op| { |
| | 2626 | const done = @atomicLoad(windows.NTSTATUS, &metadata.iosb.u.Status, .acquire) != .PENDING; |
| | 2627 | if (done) continue; |
| | 2628 | switch (operations[op]) { |
| | 2629 | .noop => unreachable, |
| | 2630 | .file_read_streaming => |*o| { |
| | 2631 | _ = windows.ntdll.NtCancelIoFile(o.file.handle, &metadata.iosb); |
| | 2632 | }, |
| | 2633 | } |
| | 2634 | } |
| | 2635 | for (metadatas) |*metadata| { |
| | 2636 | while (@atomicLoad(windows.NTSTATUS, &metadata.iosb.u.Status, .acquire) == .PENDING) { |
| | 2637 | waitForApcOrAlert(); |
| | 2638 | } |
| | 2639 | } |
| | 2640 | gpa.free(metadatas); |
| | 2641 | b.impl.reserved = null; |
| | 2642 | } |
| | 2643 | } |
| 2595 | b.impl.submit_head = submit_tail; | 2644 | b.impl.submit_head = submit_tail; |
| 2596 | b.impl.complete_tail = complete_tail; | 2645 | b.impl.complete_tail = complete_tail; |
| 2597 | b.user.complete_tail = complete_tail; | 2646 | b.user.complete_tail = complete_tail; |
| 2598 | } | 2647 | } |
| 2599 | | 2648 | |
| | 2649 | const WinOpMetadata = struct { |
| | 2650 | iosb: windows.IO_STATUS_BLOCK, |
| | 2651 | pending: bool, |
| | 2652 | }; |
| | 2653 | |
| 2600 | fn batchWaitWindows(t: *Threaded, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.WaitError!void { | 2654 | fn batchWaitWindows(t: *Threaded, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.WaitError!void { |
| 2601 | const operations = b.operations; | 2655 | const operations = b.operations; |
| 2602 | const len: u31 = @intCast(operations.len); | 2656 | const len: u31 = @intCast(operations.len); |
| ... | @@ -2606,16 +2660,16 @@ fn batchWaitWindows(t: *Threaded, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.Wa | ... | @@ -2606,16 +2660,16 @@ fn batchWaitWindows(t: *Threaded, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.Wa |
| 2606 | b.impl.submit_tail = submit_tail; | 2660 | b.impl.submit_tail = submit_tail; |
| 2607 | var complete_tail = b.impl.complete_tail; | 2661 | var complete_tail = b.impl.complete_tail; |
| 2608 | | 2662 | |
| 2609 | var overlapped_buffer: [poll_buffer_len]windows.OVERLAPPED = undefined; | 2663 | const metadatas_ptr: [*]WinOpMetadata = if (b.impl.reserved) |reserved| @ptrCast(@alignCast(reserved)) else a: { |
| 2610 | var handles_buffer: [poll_buffer_len]windows.HANDLE = undefined; | 2664 | const gpa = t.allocator; |
| 2611 | var map_buffer: [poll_buffer_len]u32 = undefined; // handles_buffer index to operations index | 2665 | const metadatas = gpa.alloc(WinOpMetadata, operations.len) catch return error.ConcurrencyUnavailable; |
| 2612 | var buffer_i: usize = 0; | 2666 | b.impl.reserved = metadatas.ptr; |
| | 2667 | @memset(metadatas, .{ .iosb = undefined, .pending = false }); |
| | 2668 | break :a metadatas.ptr; |
| | 2669 | }; |
| | 2670 | const metadatas = metadatas_ptr[0..operations.len]; |
| 2613 | | 2671 | |
| 2614 | defer { | 2672 | defer { |
| 2615 | for (map_buffer[0..buffer_i]) |op| { | | |
| 2616 | submit_head = submit_head.prev(len); | | |
| 2617 | ring[submit_head.index(len)] = op; | | |
| 2618 | } | | |
| 2619 | b.impl.submit_head = submit_head; | 2673 | b.impl.submit_head = submit_head; |
| 2620 | b.impl.complete_tail = complete_tail; | 2674 | b.impl.complete_tail = complete_tail; |
| 2621 | b.user.complete_tail = complete_tail; | 2675 | b.user.complete_tail = complete_tail; |
| ... | @@ -2624,74 +2678,76 @@ fn batchWaitWindows(t: *Threaded, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.Wa | ... | @@ -2624,74 +2678,76 @@ fn batchWaitWindows(t: *Threaded, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.Wa |
| 2624 | while (submit_head != submit_tail) : (submit_head = submit_head.next(len)) { | 2678 | while (submit_head != submit_tail) : (submit_head = submit_head.next(len)) { |
| 2625 | const op = ring[submit_head.index(len)]; | 2679 | const op = ring[submit_head.index(len)]; |
| 2626 | const operation = &operations[op]; | 2680 | const operation = &operations[op]; |
| | 2681 | const metadata = &metadatas[op]; |
| | 2682 | metadata.* = .{ .iosb = undefined, .pending = false }; |
| 2627 | switch (operation.*) { | 2683 | switch (operation.*) { |
| 2628 | .noop => { | 2684 | .noop => |*o| { |
| 2629 | try operate(t, operation); | 2685 | _ = o.status.unstarted; |
| 2630 | ring[complete_tail.index(len)] = op; | 2686 | o.status = .{ .result = {} }; |
| 2631 | complete_tail = complete_tail.next(len); | 2687 | submitComplete(ring, &complete_tail, op); |
| 2632 | }, | 2688 | }, |
| 2633 | .file_read_streaming => |*o| { | 2689 | .file_read_streaming => |*o| { |
| 2634 | _ = o.status.unstarted; | 2690 | _ = o.status.unstarted; |
| 2635 | if (handles_buffer.len - buffer_i == 0) return error.ConcurrencyUnavailable; | 2691 | switch (try ntReadFile(o.file.handle, o.data, &metadata.iosb)) { |
| 2636 | const overlapped = &overlapped_buffer[buffer_i]; | 2692 | .status => { |
| 2637 | overlapped.* = .{ | 2693 | o.status = .{ .result = ntReadFileResult(&metadata.iosb) }; |
| 2638 | .Internal = 0, | 2694 | submitComplete(ring, &complete_tail, op); |
| 2639 | .InternalHigh = 0, | 2695 | }, |
| 2640 | .DUMMYUNIONNAME = .{ .Pointer = null }, | 2696 | .pending => { |
| 2641 | .hEvent = null, | 2697 | o.status = .{ .pending = b }; |
| 2642 | }; | 2698 | metadata.pending = true; |
| 2643 | var n: windows.DWORD = undefined; | 2699 | }, |
| 2644 | const buf = o.data[0]; | | |
| 2645 | const buf_len = std.math.lossyCast(windows.DWORD, buf.len); | | |
| 2646 | if (windows.kernel32.ReadFile(o.file.handle, buf.ptr, buf_len, &n, overlapped) == 0) { | | |
| 2647 | @panic("TODO"); | | |
| 2648 | } | 2700 | } |
| 2649 | handles_buffer[buffer_i] = o.file.handle; | | |
| 2650 | map_buffer[buffer_i] = op; | | |
| 2651 | buffer_i += 1; | | |
| 2652 | }, | 2701 | }, |
| 2653 | } | 2702 | } |
| 2654 | } | 2703 | } |
| 2655 | | 2704 | |
| 2656 | switch (buffer_i) { | 2705 | var delay_interval: windows.LARGE_INTEGER = timeoutToWindowsInterval(timeout); |
| 2657 | 0 => return, | | |
| 2658 | 1 => if (timeout == .none) { | | |
| 2659 | const op = map_buffer[0]; | | |
| 2660 | try operate(t, &operations[op]); | | |
| 2661 | ring[complete_tail.index(len)] = op; | | |
| 2662 | complete_tail = complete_tail.next(len); | | |
| 2663 | buffer_i = 0; | | |
| 2664 | return; | | |
| 2665 | }, | | |
| 2666 | else => {}, | | |
| 2667 | } | | |
| 2668 | | | |
| 2669 | const handles = handles_buffer[0..buffer_i]; | | |
| 2670 | const map = map_buffer[0..buffer_i]; | | |
| 2671 | | 2706 | |
| 2672 | const syscall: Syscall = try .start(); | 2707 | while (true) { |
| 2673 | const index_result = windows.WaitForMultipleObjectsEx(handles, false, windows.INFINITE, true); | 2708 | const alertable_syscall = try AlertableSyscall.start(); |
| 2674 | syscall.finish(); | 2709 | const delay_rc = windows.ntdll.NtDelayExecution(windows.TRUE, &delay_interval); |
| 2675 | const index = index_result catch |err| switch (err) { | 2710 | alertable_syscall.finish(); |
| 2676 | error.Unexpected => @panic("TODO"), | 2711 | switch (delay_rc) { |
| 2677 | error.WaitAbandoned => @panic("TODO"), | 2712 | .SUCCESS => { |
| 2678 | error.WaitTimeOut => @panic("TODO"), | 2713 | // The thread woke due to the timeout. Although spurious |
| 2679 | }; | 2714 | // timeouts are OK, when no deadline is passed we must not |
| 2680 | var n: windows.DWORD = undefined; | 2715 | // return `error.Timeout`. |
| 2681 | if (0 == windows.kernel32.GetOverlappedResult(handles[index], &overlapped_buffer[index], &n, 0)) { | 2716 | if (timeout != .none) return error.Timeout; |
| 2682 | switch (windows.GetLastError()) { | 2717 | }, |
| 2683 | .BROKEN_PIPE => @panic("TODO"), | 2718 | else => {}, |
| 2684 | .OPERATION_ABORTED => @panic("TODO"), | | |
| 2685 | else => @panic("TODO"), | | |
| 2686 | } | 2719 | } |
| 2687 | } else switch (operations[map[index]]) { | 2720 | var any_done = false; |
| 2688 | .noop => unreachable, | 2721 | var any_pending = false; |
| 2689 | .file_read_streaming => |*o| { | 2722 | for (metadatas, 0..) |*metadata, op_usize| { |
| 2690 | o.status = .{ .result = n }; | 2723 | if (!metadata.pending) continue; |
| 2691 | }, | 2724 | any_pending = true; |
| | 2725 | const op: u31 = @intCast(op_usize); |
| | 2726 | const done = @atomicLoad(windows.NTSTATUS, &metadata.iosb.u.Status, .acquire) != .PENDING; |
| | 2727 | switch (operations[op]) { |
| | 2728 | .noop => unreachable, |
| | 2729 | .file_read_streaming => |*o| { |
| | 2730 | assert(o.status.pending == b); |
| | 2731 | if (!done) continue; |
| | 2732 | o.status = .{ .result = ntReadFileResult(&metadata.iosb) }; |
| | 2733 | }, |
| | 2734 | } |
| | 2735 | any_done = true; |
| | 2736 | metadata.pending = false; |
| | 2737 | submitComplete(ring, &complete_tail, op); |
| | 2738 | } |
| | 2739 | if (any_done) return; |
| | 2740 | if (!any_pending) return; |
| 2692 | } | 2741 | } |
| 2693 | } | 2742 | } |
| 2694 | | 2743 | |
| | 2744 | fn submitComplete(ring: []u32, complete_tail: *Io.Batch.RingIndex, op: u32) void { |
| | 2745 | const ct = complete_tail.*; |
| | 2746 | const len: u31 = @intCast(ring.len); |
| | 2747 | ring[ct.index(len)] = op; |
| | 2748 | complete_tail.* = ct.next(len); |
| | 2749 | } |
| | 2750 | |
| 2695 | const dirCreateDir = switch (native_os) { | 2751 | const dirCreateDir = switch (native_os) { |
| 2696 | .windows => dirCreateDirWindows, | 2752 | .windows => dirCreateDirWindows, |
| 2697 | .wasi => dirCreateDirWasi, | 2753 | .wasi => dirCreateDirWasi, |
| ... | @@ -5529,7 +5585,7 @@ fn dirRealPathFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, | ... | @@ -5529,7 +5585,7 @@ fn dirRealPathFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, |
| 5529 | | 5585 | |
| 5530 | fn realPathWindows(h_file: windows.HANDLE, out_buffer: []u8) File.RealPathError!usize { | 5586 | fn realPathWindows(h_file: windows.HANDLE, out_buffer: []u8) File.RealPathError!usize { |
| 5531 | var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined; | 5587 | var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined; |
| 5532 | // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks | 5588 | // TODO move GetFinalPathNameByHandle logic into Io.Threaded and add cancel checks |
| 5533 | try Thread.checkCancel(); | 5589 | try Thread.checkCancel(); |
| 5534 | const wide_slice = try windows.GetFinalPathNameByHandle(h_file, .{}, &wide_buf); | 5590 | const wide_slice = try windows.GetFinalPathNameByHandle(h_file, .{}, &wide_buf); |
| 5535 | | 5591 | |
| ... | @@ -8617,75 +8673,88 @@ fn fileReadStreamingPosix(file: File, data: []const []u8) File.Reader.Error!usiz | ... | @@ -8617,75 +8673,88 @@ fn fileReadStreamingPosix(file: File, data: []const []u8) File.Reader.Error!usiz |
| 8617 | } | 8673 | } |
| 8618 | | 8674 | |
| 8619 | fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!usize { | 8675 | fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!usize { |
| | 8676 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| | 8677 | if (ntReadFile(file.handle, data, &io_status_block)) |result| switch (result) { |
| | 8678 | .status => return ntReadFileResult(&io_status_block), |
| | 8679 | .pending => { |
| | 8680 | // Once we get here we received PENDING so we must not return from the |
| | 8681 | // function until the operation completes. |
| | 8682 | defer while (@atomicLoad(windows.NTSTATUS, &io_status_block.u.Status, .acquire) == .PENDING) { |
| | 8683 | waitForApcOrAlert(); |
| | 8684 | }; |
| | 8685 | |
| | 8686 | const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) { |
| | 8687 | error.Canceled => |e| { |
| | 8688 | _ = windows.ntdll.NtCancelIoFile(file.handle, &io_status_block); |
| | 8689 | return e; |
| | 8690 | }, |
| | 8691 | }; |
| | 8692 | defer alertable_syscall.finish(); |
| | 8693 | waitForApcOrAlert(); |
| | 8694 | while (@atomicLoad(windows.NTSTATUS, &io_status_block.u.Status, .acquire) == .PENDING) { |
| | 8695 | alertable_syscall.checkCancel() catch |err| switch (err) { |
| | 8696 | error.Canceled => |e| { |
| | 8697 | _ = windows.ntdll.NtCancelIoFile(file.handle, &io_status_block); |
| | 8698 | return e; |
| | 8699 | }, |
| | 8700 | }; |
| | 8701 | waitForApcOrAlert(); |
| | 8702 | } |
| | 8703 | }, |
| | 8704 | } else |err| return err; |
| | 8705 | return ntReadFileResult(&io_status_block); |
| | 8706 | } |
| | 8707 | |
| | 8708 | fn ntReadFileResult(io_status_block: *windows.IO_STATUS_BLOCK) !usize { |
| | 8709 | switch (io_status_block.u.Status) { |
| | 8710 | .SUCCESS, .END_OF_FILE, .PIPE_BROKEN => return io_status_block.Information, |
| | 8711 | .PENDING => unreachable, |
| | 8712 | .INVALID_DEVICE_REQUEST => return error.IsDir, |
| | 8713 | .LOCK_NOT_GRANTED => return error.LockViolation, |
| | 8714 | .ACCESS_DENIED => return error.AccessDenied, |
| | 8715 | else => |status| return windows.unexpectedStatus(status), |
| | 8716 | } |
| | 8717 | } |
| | 8718 | |
| | 8719 | fn ntReadFile(handle: windows.HANDLE, data: []const []u8, iosb: *windows.IO_STATUS_BLOCK) Io.Cancelable!enum { status, pending } { |
| 8620 | var index: usize = 0; | 8720 | var index: usize = 0; |
| 8621 | while (index < data.len and data[index].len == 0) index += 1; | 8721 | while (index < data.len and data[index].len == 0) index += 1; |
| 8622 | if (index == data.len) return 0; | 8722 | if (index == data.len) { |
| | 8723 | iosb.u.Status = .SUCCESS; |
| | 8724 | iosb.Information = 0; |
| | 8725 | return .status; |
| | 8726 | } |
| 8623 | const buffer = data[index]; | 8727 | const buffer = data[index]; |
| 8624 | | 8728 | |
| 8625 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | | |
| 8626 | const syscall: Syscall = try .start(); | 8729 | const syscall: Syscall = try .start(); |
| 8627 | while (true) { | 8730 | while (true) { |
| 8628 | io_status_block.u.Status = .PENDING; | 8731 | iosb.u.Status = .PENDING; |
| 8629 | switch (windows.ntdll.NtReadFile( | 8732 | switch (windows.ntdll.NtReadFile( |
| 8630 | file.handle, | 8733 | handle, |
| 8631 | null, // event | 8734 | null, // event |
| 8632 | noopApc, // apc callback | 8735 | noopApc, // apc callback |
| 8633 | null, // apc context | 8736 | null, // apc context |
| 8634 | &io_status_block, | 8737 | iosb, |
| 8635 | buffer.ptr, | 8738 | buffer.ptr, |
| 8636 | @min(std.math.maxInt(u32), buffer.len), | 8739 | @min(std.math.maxInt(u32), buffer.len), |
| 8637 | null, // byte offset | 8740 | null, // byte offset |
| 8638 | null, // key | 8741 | null, // key |
| 8639 | )) { | 8742 | )) { |
| 8640 | .SUCCESS, .END_OF_FILE, .PIPE_BROKEN => { | 8743 | .PENDING => { |
| 8641 | syscall.finish(); | 8744 | syscall.finish(); |
| 8642 | return io_status_block.Information; | 8745 | return .pending; |
| 8643 | }, | 8746 | }, |
| 8644 | .PENDING => break, | | |
| 8645 | .CANCELLED => { | 8747 | .CANCELLED => { |
| 8646 | try syscall.checkCancel(); | 8748 | try syscall.checkCancel(); |
| 8647 | continue; | 8749 | continue; |
| 8648 | }, | 8750 | }, |
| 8649 | .INVALID_DEVICE_REQUEST => return syscall.fail(error.IsDir), | 8751 | else => |status| { |
| 8650 | .LOCK_NOT_GRANTED => return syscall.fail(error.LockViolation), | 8752 | syscall.finish(); |
| 8651 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), | 8753 | iosb.u.Status = status; |
| 8652 | .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err), // streaming read of async mode file | 8754 | return .status; |
| 8653 | else => |status| return syscall.unexpectedNtstatus(status), | | |
| 8654 | } | | |
| 8655 | } | | |
| 8656 | { | | |
| 8657 | // Once we get here we received PENDING so we must not return from the | | |
| 8658 | // function until the operation completes. | | |
| 8659 | defer while (@atomicLoad(windows.NTSTATUS, &io_status_block.u.Status, .acquire) == .PENDING) { | | |
| 8660 | waitForApcOrAlert(); | | |
| 8661 | }; | | |
| 8662 | | | |
| 8663 | const alertable_syscall = syscall.toAlertable() catch |err| switch (err) { | | |
| 8664 | error.Canceled => |e| { | | |
| 8665 | _ = windows.ntdll.NtCancelIoFile(file.handle, &io_status_block); | | |
| 8666 | return e; | | |
| 8667 | }, | 8755 | }, |
| 8668 | }; | | |
| 8669 | defer alertable_syscall.finish(); | | |
| 8670 | waitForApcOrAlert(); | | |
| 8671 | while (@atomicLoad(windows.NTSTATUS, &io_status_block.u.Status, .acquire) == .PENDING) { | | |
| 8672 | alertable_syscall.checkCancel() catch |err| switch (err) { | | |
| 8673 | error.Canceled => |e| { | | |
| 8674 | _ = windows.ntdll.NtCancelIoFile(file.handle, &io_status_block); | | |
| 8675 | return e; | | |
| 8676 | }, | | |
| 8677 | }; | | |
| 8678 | waitForApcOrAlert(); | | |
| 8679 | } | 8756 | } |
| 8680 | } | 8757 | } |
| 8681 | switch (io_status_block.u.Status) { | | |
| 8682 | .SUCCESS, .END_OF_FILE, .PIPE_BROKEN => return io_status_block.Information, | | |
| 8683 | .PENDING => unreachable, // cannot return until the operation completes | | |
| 8684 | .INVALID_DEVICE_REQUEST => return error.IsDir, | | |
| 8685 | .LOCK_NOT_GRANTED => return error.LockViolation, | | |
| 8686 | .ACCESS_DENIED => return error.AccessDenied, | | |
| 8687 | else => |status| return windows.unexpectedStatus(status), | | |
| 8688 | } | | |
| 8689 | } | 8758 | } |
| 8690 | | 8759 | |
| 8691 | fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize { | 8760 | fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize { |
| ... | @@ -9318,7 +9387,7 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut | ... | @@ -9318,7 +9387,7 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut |
| 9318 | }; | 9387 | }; |
| 9319 | defer w.CloseHandle(h_file); | 9388 | defer w.CloseHandle(h_file); |
| 9320 | | 9389 | |
| 9321 | // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks | 9390 | // TODO move GetFinalPathNameByHandle logic into Io.Threaded and add cancel checks |
| 9322 | try Thread.checkCancel(); | 9391 | try Thread.checkCancel(); |
| 9323 | const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data); | 9392 | const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data); |
| 9324 | | 9393 | |
| ... | @@ -12989,7 +13058,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirEr | ... | @@ -12989,7 +13058,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirEr |
| 12989 | | 13058 | |
| 12990 | if (is_windows) { | 13059 | if (is_windows) { |
| 12991 | var dir_path_buffer: [windows.PATH_MAX_WIDE]u16 = undefined; | 13060 | var dir_path_buffer: [windows.PATH_MAX_WIDE]u16 = undefined; |
| 12992 | // TODO move GetFinalPathNameByHandle logic into std.Io.Threaded and add cancel checks | 13061 | // TODO move GetFinalPathNameByHandle logic into Io.Threaded and add cancel checks |
| 12993 | try Thread.checkCancel(); | 13062 | try Thread.checkCancel(); |
| 12994 | const dir_path = try windows.GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buffer); | 13063 | const dir_path = try windows.GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buffer); |
| 12995 | const path_len_bytes = std.math.cast(u16, dir_path.len * 2) orelse return error.NameTooLong; | 13064 | const path_len_bytes = std.math.cast(u16, dir_path.len * 2) orelse return error.NameTooLong; |
| ... | @@ -16657,7 +16726,7 @@ const parking_sleep = struct { | ... | @@ -16657,7 +16726,7 @@ const parking_sleep = struct { |
| 16657 | /// Spurious wakeups are possible. | 16726 | /// Spurious wakeups are possible. |
| 16658 | /// | 16727 | /// |
| 16659 | /// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation. | 16728 | /// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation. |
| 16660 | fn park(opt_deadline: ?std.Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{Timeout}!void { | 16729 | fn park(opt_deadline: ?Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{Timeout}!void { |
| 16661 | comptime assert(use_parking_futex or use_parking_sleep); | 16730 | comptime assert(use_parking_futex or use_parking_sleep); |
| 16662 | switch (native_os) { | 16731 | switch (native_os) { |
| 16663 | .windows => { | 16732 | .windows => { |
| ... | @@ -16713,6 +16782,23 @@ fn park(opt_deadline: ?std.Io.Clock.Timestamp, addr_hint: ?*const anyopaque) err | ... | @@ -16713,6 +16782,23 @@ fn park(opt_deadline: ?std.Io.Clock.Timestamp, addr_hint: ?*const anyopaque) err |
| 16713 | } | 16782 | } |
| 16714 | } | 16783 | } |
| 16715 | | 16784 | |
| | 16785 | fn timeoutToWindowsInterval(timeout: Io.Timeout) windows.LARGE_INTEGER { |
| | 16786 | switch (timeout) { |
| | 16787 | .none => { |
| | 16788 | return std.math.minInt(windows.LARGE_INTEGER); // infinite timeout |
| | 16789 | }, |
| | 16790 | .deadline => |deadline| { |
| | 16791 | const nanoseconds = deadline.raw.nanoseconds; |
| | 16792 | return @intCast(@divTrunc(nanoseconds, 100)); |
| | 16793 | }, |
| | 16794 | .duration => |duration| { |
| | 16795 | const now_timestamp = nowWindows(duration.clock) catch unreachable; |
| | 16796 | const deadline_ns = now_timestamp.nanoseconds + duration.raw.nanoseconds; |
| | 16797 | return @intCast(@divTrunc(deadline_ns, 100)); |
| | 16798 | }, |
| | 16799 | } |
| | 16800 | } |
| | 16801 | |
| 16716 | const UnparkTid = switch (native_os) { | 16802 | const UnparkTid = switch (native_os) { |
| 16717 | // `NtAlertMultipleThreadByThreadId` is weird and wants 64-bit thread handles? | 16803 | // `NtAlertMultipleThreadByThreadId` is weird and wants 64-bit thread handles? |
| 16718 | .windows => usize, | 16804 | .windows => usize, |