| ... | ... | @@ -1938,6 +1938,10 @@ const have_mmap = switch (native_os) { |
| 1938 | 1938 | .wasi, .windows => false, |
| 1939 | 1939 | else => true, |
| 1940 | 1940 | }; |
| 1941 | const have_poll = switch (native_os) { |
| 1942 | .wasi, .windows => false, |
| 1943 | else => true, |
| 1944 | }; |
| 1941 | 1945 | |
| 1942 | 1946 | const open_sym = if (posix.lfs64_abi) posix.system.open64 else posix.system.open; |
| 1943 | 1947 | const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; |
| ... | ... | @@ -2507,104 +2511,104 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void { |
| 2507 | 2511 | alertable_syscall.finish(); |
| 2508 | 2512 | return; |
| 2509 | 2513 | } |
| 2510 | | if (native_os == .wasi and !builtin.link_libc) @panic("TODO"); |
| 2511 | | var poll_buffer: [poll_buffer_len]posix.pollfd = undefined; |
| 2512 | | var poll_len: u32 = 0; |
| 2513 | | { |
| 2514 | | var index = b.submissions.head; |
| 2515 | | while (index != .none and poll_len < poll_buffer_len) { |
| 2516 | | const submission = &b.storage[index.toIndex()].submission; |
| 2517 | | switch (submission.operation) { |
| 2518 | | .file_read_streaming => |o| { |
| 2519 | | poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.IN, .revents = 0 }; |
| 2520 | | poll_len += 1; |
| 2521 | | }, |
| 2514 | if (have_poll) { |
| 2515 | var poll_buffer: [poll_buffer_len]posix.pollfd = undefined; |
| 2516 | var poll_len: u32 = 0; |
| 2517 | { |
| 2518 | var index = b.submissions.head; |
| 2519 | while (index != .none and poll_len < poll_buffer_len) { |
| 2520 | const submission = &b.storage[index.toIndex()].submission; |
| 2521 | switch (submission.operation) { |
| 2522 | .file_read_streaming => |o| { |
| 2523 | poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.IN, .revents = 0 }; |
| 2524 | poll_len += 1; |
| 2525 | }, |
| 2526 | } |
| 2527 | index = submission.node.next; |
| 2522 | 2528 | } |
| 2523 | | index = submission.node.next; |
| 2524 | 2529 | } |
| 2525 | | } |
| 2526 | | switch (poll_len) { |
| 2527 | | 0 => return, |
| 2528 | | 1 => {}, |
| 2529 | | else => while (true) { |
| 2530 | | const timeout_ms: i32 = t: { |
| 2531 | | if (b.completions.head != .none) { |
| 2532 | | // It is legal to call batchWait with already completed |
| 2533 | | // operations in the ring. In such case, we need to avoid |
| 2534 | | // blocking in the poll syscall, but we can still take this |
| 2535 | | // opportunity to find additional ready operations. |
| 2536 | | break :t 0; |
| 2537 | | } |
| 2538 | | const max_poll_ms = std.math.maxInt(i32); |
| 2539 | | break :t max_poll_ms; |
| 2540 | | }; |
| 2541 | | const syscall = try Syscall.start(); |
| 2542 | | const rc = posix.system.poll(&poll_buffer, poll_len, timeout_ms); |
| 2543 | | syscall.finish(); |
| 2544 | | switch (posix.errno(rc)) { |
| 2545 | | .SUCCESS => { |
| 2546 | | if (rc == 0) { |
| 2547 | | if (b.completions.head != .none) { |
| 2548 | | // Since there are already completions available in the |
| 2549 | | // queue, this is neither a timeout nor a case for |
| 2550 | | // retrying. |
| 2551 | | return; |
| 2552 | | } |
| 2553 | | continue; |
| 2530 | switch (poll_len) { |
| 2531 | 0 => return, |
| 2532 | 1 => {}, |
| 2533 | else => while (true) { |
| 2534 | const timeout_ms: i32 = t: { |
| 2535 | if (b.completions.head != .none) { |
| 2536 | // It is legal to call batchWait with already completed |
| 2537 | // operations in the ring. In such case, we need to avoid |
| 2538 | // blocking in the poll syscall, but we can still take this |
| 2539 | // opportunity to find additional ready operations. |
| 2540 | break :t 0; |
| 2554 | 2541 | } |
| 2555 | | var prev_index: Io.Operation.OptionalIndex = .none; |
| 2556 | | var index = b.submissions.head; |
| 2557 | | for (poll_buffer[0..poll_len]) |poll_entry| { |
| 2558 | | const storage = &b.storage[index.toIndex()]; |
| 2559 | | const submission = &storage.submission; |
| 2560 | | const next_index = submission.node.next; |
| 2561 | | if (poll_entry.revents != 0) { |
| 2562 | | const result = try operate(t, submission.operation); |
| 2563 | | |
| 2564 | | switch (prev_index) { |
| 2565 | | .none => b.submissions.head = next_index, |
| 2566 | | else => b.storage[prev_index.toIndex()].submission.node.next = next_index, |
| 2567 | | } |
| 2568 | | if (next_index == .none) b.submissions.tail = prev_index; |
| 2569 | | |
| 2570 | | switch (b.completions.tail) { |
| 2571 | | .none => b.completions.head = index, |
| 2572 | | else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index, |
| 2542 | const max_poll_ms = std.math.maxInt(i32); |
| 2543 | break :t max_poll_ms; |
| 2544 | }; |
| 2545 | const syscall = try Syscall.start(); |
| 2546 | const rc = posix.system.poll(&poll_buffer, poll_len, timeout_ms); |
| 2547 | syscall.finish(); |
| 2548 | switch (posix.errno(rc)) { |
| 2549 | .SUCCESS => { |
| 2550 | if (rc == 0) { |
| 2551 | if (b.completions.head != .none) { |
| 2552 | // Since there are already completions available in the |
| 2553 | // queue, this is neither a timeout nor a case for |
| 2554 | // retrying. |
| 2555 | return; |
| 2573 | 2556 | } |
| 2574 | | storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } }; |
| 2575 | | b.completions.tail = index; |
| 2576 | | } else prev_index = index; |
| 2577 | | index = next_index; |
| 2578 | | } |
| 2579 | | assert(index == .none); |
| 2580 | | return; |
| 2581 | | }, |
| 2582 | | .INTR => continue, |
| 2583 | | else => break, |
| 2584 | | } |
| 2585 | | }, |
| 2557 | continue; |
| 2558 | } |
| 2559 | var prev_index: Io.Operation.OptionalIndex = .none; |
| 2560 | var index = b.submissions.head; |
| 2561 | for (poll_buffer[0..poll_len]) |poll_entry| { |
| 2562 | const storage = &b.storage[index.toIndex()]; |
| 2563 | const submission = &storage.submission; |
| 2564 | const next_index = submission.node.next; |
| 2565 | if (poll_entry.revents != 0) { |
| 2566 | const result = try operate(t, submission.operation); |
| 2567 | |
| 2568 | switch (prev_index) { |
| 2569 | .none => b.submissions.head = next_index, |
| 2570 | else => b.storage[prev_index.toIndex()].submission.node.next = next_index, |
| 2571 | } |
| 2572 | if (next_index == .none) b.submissions.tail = prev_index; |
| 2573 | |
| 2574 | switch (b.completions.tail) { |
| 2575 | .none => b.completions.head = index, |
| 2576 | else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index, |
| 2577 | } |
| 2578 | storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } }; |
| 2579 | b.completions.tail = index; |
| 2580 | } else prev_index = index; |
| 2581 | index = next_index; |
| 2582 | } |
| 2583 | assert(index == .none); |
| 2584 | return; |
| 2585 | }, |
| 2586 | .INTR => continue, |
| 2587 | else => break, |
| 2588 | } |
| 2589 | }, |
| 2590 | } |
| 2586 | 2591 | } |
| 2587 | | { |
| 2588 | | var tail_index = b.completions.tail; |
| 2589 | | defer b.completions.tail = tail_index; |
| 2590 | | var index = b.submissions.head; |
| 2591 | | errdefer b.submissions.head = index; |
| 2592 | | while (index != .none) { |
| 2593 | | const storage = &b.storage[index.toIndex()]; |
| 2594 | | const submission = &storage.submission; |
| 2595 | | const next_index = submission.node.next; |
| 2596 | | const result = try operate(t, submission.operation); |
| 2597 | 2592 | |
| 2598 | | switch (tail_index) { |
| 2599 | | .none => b.completions.head = index, |
| 2600 | | else => b.storage[tail_index.toIndex()].completion.node.next = index, |
| 2601 | | } |
| 2602 | | storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } }; |
| 2603 | | tail_index = index; |
| 2604 | | index = next_index; |
| 2593 | var tail_index = b.completions.tail; |
| 2594 | defer b.completions.tail = tail_index; |
| 2595 | var index = b.submissions.head; |
| 2596 | errdefer b.submissions.head = index; |
| 2597 | while (index != .none) { |
| 2598 | const storage = &b.storage[index.toIndex()]; |
| 2599 | const submission = &storage.submission; |
| 2600 | const next_index = submission.node.next; |
| 2601 | const result = try operate(t, submission.operation); |
| 2602 | |
| 2603 | switch (tail_index) { |
| 2604 | .none => b.completions.head = index, |
| 2605 | else => b.storage[tail_index.toIndex()].completion.node.next = index, |
| 2605 | 2606 | } |
| 2606 | | b.submissions = .{ .head = .none, .tail = .none }; |
| 2607 | storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } }; |
| 2608 | tail_index = index; |
| 2609 | index = next_index; |
| 2607 | 2610 | } |
| 2611 | b.submissions = .{ .head = .none, .tail = .none }; |
| 2608 | 2612 | } |
| 2609 | 2613 | |
| 2610 | 2614 | fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.AwaitConcurrentError!void { |
| ... | ... | @@ -2644,7 +2648,11 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout |
| 2644 | 2648 | } |
| 2645 | 2649 | return; |
| 2646 | 2650 | } |
| 2647 | | if (native_os == .wasi and !builtin.link_libc) @panic("TODO"); |
| 2651 | if (native_os == .wasi) { |
| 2652 | // TODO call poll_oneoff |
| 2653 | return error.ConcurrencyUnavailable; |
| 2654 | } |
| 2655 | if (!have_poll) return error.ConcurrencyUnavailable; |
| 2648 | 2656 | var poll_buffer: [poll_buffer_len]posix.pollfd = undefined; |
| 2649 | 2657 | var poll_storage: struct { |
| 2650 | 2658 | gpa: std.mem.Allocator, |