| ... | @@ -2474,6 +2474,7 @@ fn operate(userdata: ?*anyopaque, op: *Io.Operation) Io.Cancelable!void { | ... | @@ -2474,6 +2474,7 @@ fn operate(userdata: ?*anyopaque, op: *Io.Operation) Io.Cancelable!void { |
| 2474 | | 2474 | |
| 2475 | fn batchWait(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.WaitError!void { | 2475 | fn batchWait(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.WaitError!void { |
| 2476 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 2476 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| | 2477 | if (is_windows) return batchWaitWindows(t, b, timeout); |
| 2477 | const operations = b.operations; | 2478 | const operations = b.operations; |
| 2478 | const len: u31 = @intCast(operations.len); | 2479 | const len: u31 = @intCast(operations.len); |
| 2479 | const ring = b.ring[0..len]; | 2480 | const ring = b.ring[0..len]; |
| ... | @@ -2492,13 +2493,12 @@ fn batchWait(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch. | ... | @@ -2492,13 +2493,12 @@ fn batchWait(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch. |
| 2492 | b.impl.complete_tail = complete_tail; | 2493 | b.impl.complete_tail = complete_tail; |
| 2493 | b.user.complete_tail = complete_tail; | 2494 | b.user.complete_tail = complete_tail; |
| 2494 | } | 2495 | } |
| 2495 | if (is_windows) @panic("TODO"); | | |
| 2496 | var poll_buffer: [poll_buffer_len]posix.pollfd = undefined; | 2496 | var poll_buffer: [poll_buffer_len]posix.pollfd = undefined; |
| 2497 | while (submit_head != submit_tail) : (submit_head = submit_head.next(len)) { | 2497 | while (submit_head != submit_tail) : (submit_head = submit_head.next(len)) { |
| 2498 | const op = ring[submit_head.index(len)]; | 2498 | const op = ring[submit_head.index(len)]; |
| 2499 | const operation = &operations[op]; | 2499 | const operation = &operations[op]; |
| 2500 | switch (operation.*) { | 2500 | switch (operation.*) { |
| 2501 | else => { | 2501 | .noop => { |
| 2502 | try operate(t, operation); | 2502 | try operate(t, operation); |
| 2503 | ring[complete_tail.index(len)] = op; | 2503 | ring[complete_tail.index(len)] = op; |
| 2504 | complete_tail = complete_tail.next(len); | 2504 | complete_tail = complete_tail.next(len); |
| ... | @@ -2597,147 +2597,98 @@ fn batchCancel(userdata: ?*anyopaque, b: *Io.Batch) void { | ... | @@ -2597,147 +2597,98 @@ fn batchCancel(userdata: ?*anyopaque, b: *Io.Batch) void { |
| 2597 | b.user.complete_tail = complete_tail; | 2597 | b.user.complete_tail = complete_tail; |
| 2598 | } | 2598 | } |
| 2599 | | 2599 | |
| 2600 | fn batch(userdata: ?*anyopaque, operations: []Io.Operation) Io.ConcurrentError!void { | 2600 | fn batchWaitWindows(t: *Threaded, b: *Io.Batch, timeout: Io.Timeout) Io.ConcurrentError!void { |
| 2601 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 2601 | const operations = b.operations; |
| 2602 | | 2602 | const len: u31 = @intCast(operations.len); |
| 2603 | if (operations.len == 1) { | 2603 | const ring = b.ring[0..len]; |
| 2604 | @branchHint(.likely); | 2604 | var submit_head = b.impl.submit_head; |
| 2605 | return operate(&operations[0]); | 2605 | const submit_tail = b.user.submit_tail; |
| 2606 | } | 2606 | b.impl.submit_tail = submit_tail; |
| 2607 | | 2607 | var complete_tail = b.impl.complete_tail; |
| 2608 | if (is_windows) return batchWindows(t, operations); | | |
| 2609 | | | |
| 2610 | var poll_buffer: [poll_buffer_len]posix.pollfd = undefined; | | |
| 2611 | var map_buffer: [poll_buffer_len]u8 = undefined; // poll_buffer index to operations index | | |
| 2612 | var poll_i: usize = 0; | | |
| 2613 | | 2608 | |
| 2614 | for (operations, 0..) |*op, operation_index| switch (op.*) { | 2609 | var overlapped_buffer: [poll_buffer_len]windows.OVERLAPPED = undefined; |
| 2615 | .noop => continue, | 2610 | var handles_buffer: [poll_buffer_len]windows.HANDLE = undefined; |
| 2616 | .file_read_streaming => |*o| { | 2611 | var map_buffer: [poll_buffer_len]u32 = undefined; // handles_buffer index to operations index |
| 2617 | if (poll_buffer.len - poll_i == 0) return error.ConcurrencyUnavailable; | 2612 | var buffer_i: usize = 0; |
| 2618 | poll_buffer[poll_i] = .{ | | |
| 2619 | .fd = o.file.handle, | | |
| 2620 | .events = posix.POLL.IN, | | |
| 2621 | .revents = 0, | | |
| 2622 | }; | | |
| 2623 | map_buffer[poll_i] = @intCast(operation_index); | | |
| 2624 | poll_i += 1; | | |
| 2625 | }, | | |
| 2626 | }; | | |
| 2627 | | 2613 | |
| 2628 | const polls = poll_buffer[0..poll_i]; | 2614 | defer { |
| 2629 | const map = map_buffer[0..poll_i]; | 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; |
| | 2620 | b.impl.complete_tail = complete_tail; |
| | 2621 | b.user.complete_tail = complete_tail; |
| | 2622 | } |
| 2630 | | 2623 | |
| 2631 | var pending = poll_i; | 2624 | while (submit_head != submit_tail) : (submit_head = submit_head.next(len)) { |
| 2632 | while (pending > 0) { | 2625 | const op = ring[submit_head.index(len)]; |
| 2633 | const syscall = Syscall.start() catch |err| switch (err) { | 2626 | const operation = &operations[op]; |
| 2634 | error.Canceled => { | 2627 | switch (operation.*) { |
| 2635 | if (!setOperationsError(operations, polls, map, error.Canceled)) | 2628 | .noop => { |
| 2636 | recancelInner(); | 2629 | try operate(t, operation); |
| 2637 | return; | 2630 | ring[complete_tail.index(len)] = op; |
| 2638 | }, | 2631 | complete_tail = complete_tail.next(len); |
| 2639 | }; | | |
| 2640 | const rc = posix.system.poll(polls.ptr, polls.len, -1); | | |
| 2641 | syscall.finish(); | | |
| 2642 | switch (posix.errno(rc)) { | | |
| 2643 | .SUCCESS => for (polls, map) |*poll_fd, i| { | | |
| 2644 | if (poll_fd.revents == 0) continue; | | |
| 2645 | poll_fd.fd = -1; | | |
| 2646 | pending -= 1; | | |
| 2647 | operate(&operations[i]); | | |
| 2648 | }, | | |
| 2649 | .INTR => continue, | | |
| 2650 | .NOMEM => { | | |
| 2651 | assert(setOperationsError(operations, polls, map, error.SystemResources)); | | |
| 2652 | return; | | |
| 2653 | }, | 2632 | }, |
| 2654 | else => { | 2633 | .file_read_streaming => |*o| { |
| 2655 | assert(setOperationsError(operations, polls, map, error.Unexpected)); | 2634 | _ = o.status.unstarted; |
| 2656 | return; | 2635 | if (handles_buffer.len - buffer_i == 0) return error.ConcurrencyUnavailable; |
| | 2636 | const overlapped = &overlapped_buffer[buffer_i]; |
| | 2637 | overlapped.* = .{ |
| | 2638 | .Internal = 0, |
| | 2639 | .InternalHigh = 0, |
| | 2640 | .DUMMYUNIONNAME = .{ |
| | 2641 | .DUMMYSTRUCTNAME = .{ |
| | 2642 | .Offset = 0, |
| | 2643 | .OffsetHigh = 0, |
| | 2644 | }, |
| | 2645 | .Pointer = null, |
| | 2646 | }, |
| | 2647 | .hEvent = null, |
| | 2648 | }; |
| | 2649 | var n: windows.DWORD = undefined; |
| | 2650 | const buf = o.data[0]; |
| | 2651 | if (windows.kernel32.ReadFile(o.file.handle, buf.ptr, buf.len, &n, overlapped) == 0) { |
| | 2652 | @panic("TODO"); |
| | 2653 | } |
| | 2654 | handles_buffer[buffer_i] = o.file.handle; |
| | 2655 | map_buffer[buffer_i] = op; |
| | 2656 | buffer_i += 1; |
| 2657 | }, | 2657 | }, |
| 2658 | } | 2658 | } |
| 2659 | } | 2659 | } |
| 2660 | } | | |
| 2661 | | | |
| 2662 | fn batchWindows(t: *Threaded, operations: []Io.Operation) Io.ConcurrentError!void { | | |
| 2663 | _ = t; | | |
| 2664 | var overlapped_buffer: [poll_buffer_len]windows.OVERLAPPED = undefined; | | |
| 2665 | var handles_buffer: [poll_buffer_len]windows.HANDLE = undefined; | | |
| 2666 | var map_buffer: [poll_buffer_len]u8 = undefined; // handles_buffer index to operations index | | |
| 2667 | var buffer_i: usize = 0; | | |
| 2668 | | 2660 | |
| 2669 | for (operations, 0..) |*op, operation_index| switch (op.*) { | 2661 | switch (buffer_i) { |
| 2670 | .noop => continue, | 2662 | 0 => return, |
| 2671 | .file_read_streaming => |*o| { | 2663 | 1 => if (timeout == .none) { |
| 2672 | if (handles_buffer.len - buffer_i == 0) return error.ConcurrencyUnavailable; | 2664 | const op = map_buffer[0]; |
| 2673 | | 2665 | try operate(t, &operations[op]); |
| 2674 | const overlapped = &overlapped_buffer[buffer_i]; | 2666 | ring[complete_tail.index(len)] = op; |
| 2675 | overlapped.* = .{ | 2667 | complete_tail = complete_tail.next(len); |
| 2676 | .Internal = 0, | 2668 | return; |
| 2677 | .InternalHigh = 0, | | |
| 2678 | .DUMMYUNIONNAME = .{ | | |
| 2679 | .DUMMYSTRUCTNAME = .{ | | |
| 2680 | .Offset = 0, | | |
| 2681 | .OffsetHigh = 0, | | |
| 2682 | }, | | |
| 2683 | .Pointer = null, | | |
| 2684 | }, | | |
| 2685 | .hEvent = null, | | |
| 2686 | }; | | |
| 2687 | var n: windows.DWORD = undefined; | | |
| 2688 | const buf = o.data[0]; | | |
| 2689 | if (windows.kernel32.ReadFile(o.file.handle, buf.ptr, buf.len, &n, overlapped) == 0) { | | |
| 2690 | @panic("TODO"); | | |
| 2691 | } | | |
| 2692 | handles_buffer[buffer_i] = o.file.handle; | | |
| 2693 | map_buffer[buffer_i] = @intCast(operation_index); | | |
| 2694 | buffer_i += 1; | | |
| 2695 | }, | 2669 | }, |
| 2696 | }; | 2670 | else => {}, |
| | 2671 | } |
| 2697 | | 2672 | |
| 2698 | const handles = handles_buffer[0..buffer_i]; | 2673 | const handles = handles_buffer[0..buffer_i]; |
| 2699 | const map = map_buffer[0..buffer_i]; | 2674 | const map = map_buffer[0..buffer_i]; |
| 2700 | var pending = buffer_i; | | |
| 2701 | | 2675 | |
| 2702 | while (pending > 0) { | 2676 | const syscall: Syscall = try .start(); |
| 2703 | const syscall: Syscall = try .start(); | 2677 | const index = windows.WaitForMultipleObjectsEx(handles, false, windows.INFINITE, true); |
| 2704 | const index = windows.WaitForMultipleObjectsEx(handles, false, windows.INFINITE, true); | 2678 | syscall.finish(); |
| 2705 | syscall.finish(); | 2679 | var n: windows.DWORD = undefined; |
| 2706 | var n: windows.DWORD = undefined; | 2680 | if (0 == windows.kernel32.GetOverlappedResult(handles[index], overlapped_buffer[index], &n, 0)) { |
| 2707 | if (0 == windows.kernel32.GetOverlappedResult(handles[index], overlapped_buffer[index], &n, 0)) { | 2681 | switch (windows.GetLastError()) { |
| 2708 | switch (windows.GetLastError()) { | 2682 | .BROKEN_PIPE => @panic("TODO"), |
| 2709 | .BROKEN_PIPE => @panic("TODO"), | 2683 | .OPERATION_ABORTED => @panic("TODO"), |
| 2710 | .OPERATION_ABORTED => @panic("TODO"), | 2684 | else => @panic("TODO"), |
| 2711 | else => @panic("TODO"), | | |
| 2712 | } | | |
| 2713 | } else switch (operations[map[index]]) { | | |
| 2714 | .noop => unreachable, | | |
| 2715 | .file_read_streaming => |*o| { | | |
| 2716 | o.status = .{ .result = n }; | | |
| 2717 | pending -= 1; | | |
| 2718 | }, | | |
| 2719 | } | | |
| 2720 | } | | |
| 2721 | } | | |
| 2722 | | | |
| 2723 | fn setOperationsError( | | |
| 2724 | operations: []Io.Operation, | | |
| 2725 | polls: []const posix.pollfd, | | |
| 2726 | map: []const u8, | | |
| 2727 | err: error{ Canceled, SystemResources, Unexpected }, | | |
| 2728 | ) bool { | | |
| 2729 | var marked = false; | | |
| 2730 | for (polls, map) |*poll_fd, i| { | | |
| 2731 | if (poll_fd.fd == -1) continue; | | |
| 2732 | switch (operations[i]) { | | |
| 2733 | .noop => unreachable, | | |
| 2734 | inline else => |*o| { | | |
| 2735 | o.status = .{ .result = err }; | | |
| 2736 | marked = true; | | |
| 2737 | }, | | |
| 2738 | } | 2685 | } |
| | 2686 | } else switch (operations[map[index]]) { |
| | 2687 | .noop => unreachable, |
| | 2688 | .file_read_streaming => |*o| { |
| | 2689 | o.status = .{ .result = n }; |
| | 2690 | }, |
| 2739 | } | 2691 | } |
| 2740 | return marked; | | |
| 2741 | } | 2692 | } |
| 2742 | | 2693 | |
| 2743 | const dirCreateDir = switch (native_os) { | 2694 | const dirCreateDir = switch (native_os) { |