authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-05 16:34:16-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-08 19:20:06-07:00
log8b69341271db18a03a4aa7400a4c6679c5fbf5c6
tree56a65d8d8da2f43acac12a77a3095429470739a4
parentbe0f188990d8aedc3f5bea5c9ab436a7f778d59d

std.Io.Threaded: optimize batchAwaitConcurrent for net_receive

Eagerly receive messages with MSG_DONTWAIT before polling. This makes the DNS resolution use case end up doing: recvmsg (EAGAIN) poll recvmsg (success) recvmsg (success) rather than: poll recvmsg (success) poll recvmsg (success)

1 files changed, 32 insertions(+), 7 deletions(-)

lib/std/Io/Threaded.zig+32-7
...@@ -2636,8 +2636,9 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper...@@ -2636,8 +2636,9 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper
2636 .net_receive => |*o| return .{ .net_receive = o: {2636 .net_receive => |*o| return .{ .net_receive = o: {
2637 if (!have_networking) break :o .{ error.NetworkDown, 0 };2637 if (!have_networking) break :o .{ error.NetworkDown, 0 };
2638 if (is_windows) break :o netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags);2638 if (is_windows) break :o netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags);
2639 netReceivePosix(o.socket_handle, &o.message_buffer[0], o.data_buffer, o.flags) catch |err| switch (err) {2639 netReceivePosix(o.socket_handle, &o.message_buffer[0], o.data_buffer, o.flags, false) catch |err| switch (err) {
2640 error.Canceled => |e| return e,2640 error.Canceled => |e| return e,
2641 error.WouldBlock => unreachable,
2641 else => |e| break :o .{ e, 0 },2642 else => |e| break :o .{ e, 0 },
2642 };2643 };
2643 break :o .{ null, 1 };2644 break :o .{ null, 1 };
...@@ -2846,19 +2847,41 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout...@@ -2846,19 +2847,41 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout
2846 {2847 {
2847 var index = b.submitted.head;2848 var index = b.submitted.head;
2848 while (index != .none) {2849 while (index != .none) {
2849 const submission = &b.storage[index.toIndex()].submission;2850 const storage = &b.storage[index.toIndex()];
2851 const submission = storage.submission;
2850 switch (submission.operation) {2852 switch (submission.operation) {
2851 .file_read_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.ERR),2853 .file_read_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.ERR),
2852 .file_write_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.OUT | posix.POLL.ERR),2854 .file_write_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.OUT | posix.POLL.ERR),
2853 .device_io_control => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR),2855 .device_io_control => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR),
2854 .net_receive => |o| try poll_storage.add(o.socket_handle, posix.POLL.IN | posix.POLL.ERR),2856 .net_receive => |*o| nb: {
2857 var data_i: usize = 0;
2858 const result: Io.Operation.Result = .{ .net_receive = for (o.message_buffer, 0..) |*msg, msg_i| {
2859 const remaining_data_buffer = o.data_buffer[data_i..];
2860 netReceivePosix(o.socket_handle, msg, remaining_data_buffer, o.flags, true) catch |err| switch (err) {
2861 error.Canceled => |e| return e,
2862 error.WouldBlock => {
2863 if (msg_i != 0) break .{ null, msg_i };
2864 try poll_storage.add(o.socket_handle, posix.POLL.IN | posix.POLL.ERR);
2865 break :nb;
2866 },
2867 else => |e| break .{ e, 0 },
2868 };
2869 data_i += msg.data.len;
2870 } else .{ null, o.message_buffer.len } };
2871 switch (b.completed.tail) {
2872 .none => b.completed.head = index,
2873 else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index,
2874 }
2875 storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
2876 b.completed.tail = index;
2877 },
2855 }2878 }
2856 index = submission.node.next;2879 index = submission.node.next;
2857 }2880 }
2858 }2881 }
2859 switch (poll_storage.len) {2882 switch (poll_storage.len) {
2860 0 => return,2883 0 => return,
2861 1 => if (timeout == .none) {2884 1 => if (timeout == .none and b.completed.head == .none) {
2862 const index = b.submitted.head;2885 const index = b.submitted.head;
2863 const storage = &b.storage[index.toIndex()];2886 const storage = &b.storage[index.toIndex()];
2864 const result = try operate(t, storage.submission.operation);2887 const result = try operate(t, storage.submission.operation);
...@@ -13221,7 +13244,8 @@ fn netReceivePosix(...@@ -13221,7 +13244,8 @@ fn netReceivePosix(
13221 message: *net.IncomingMessage,13244 message: *net.IncomingMessage,
13222 data_buffer: []u8,13245 data_buffer: []u8,
13223 flags: net.ReceiveFlags,13246 flags: net.ReceiveFlags,
13224) net.Socket.ReceiveError!void {13247 nonblocking: bool,
13248) (net.Socket.ReceiveError || error{WouldBlock})!void {
13225 // recvmmsg is useless, here's why:13249 // recvmmsg is useless, here's why:
13226 // * [timeout bug](https://bugzilla.kernel.org/show_bug.cgi?id=75371)13250 // * [timeout bug](https://bugzilla.kernel.org/show_bug.cgi?id=75371)
13227 // * it wants iovecs for each message but we have a better API: one data13251 // * it wants iovecs for each message but we have a better API: one data
...@@ -13232,7 +13256,8 @@ fn netReceivePosix(...@@ -13232,7 +13256,8 @@ fn netReceivePosix(
13232 @as(u32, if (flags.oob) posix.MSG.OOB else 0) |13256 @as(u32, if (flags.oob) posix.MSG.OOB else 0) |
13233 @as(u32, if (flags.peek) posix.MSG.PEEK else 0) |13257 @as(u32, if (flags.peek) posix.MSG.PEEK else 0) |
13234 @as(u32, if (flags.trunc) posix.MSG.TRUNC else 0) |13258 @as(u32, if (flags.trunc) posix.MSG.TRUNC else 0) |
13235 posix.MSG.NOSIGNAL;13259 posix.MSG.NOSIGNAL |
13260 @as(u32, if (nonblocking) posix.MSG.DONTWAIT else 0);
1323613261
13237 var storage: PosixAddress = undefined;13262 var storage: PosixAddress = undefined;
13238 var iov: posix.iovec = .{ .base = data_buffer.ptr, .len = data_buffer.len };13263 var iov: posix.iovec = .{ .base = data_buffer.ptr, .len = data_buffer.len };
...@@ -13280,7 +13305,7 @@ fn netReceivePosix(...@@ -13280,7 +13305,7 @@ fn netReceivePosix(
13280 .PIPE => return syscall.fail(error.SocketUnconnected),13305 .PIPE => return syscall.fail(error.SocketUnconnected),
13281 .CONNRESET => return syscall.fail(error.ConnectionResetByPeer),13306 .CONNRESET => return syscall.fail(error.ConnectionResetByPeer),
13282 .NETDOWN => return syscall.fail(error.NetworkDown),13307 .NETDOWN => return syscall.fail(error.NetworkDown),
13283 .AGAIN => |err| return syscall.errnoBug(err),13308 .AGAIN => return syscall.fail(error.WouldBlock),
13284 .BADF => |err| return syscall.errnoBug(err),13309 .BADF => |err| return syscall.errnoBug(err),
13285 .FAULT => |err| return syscall.errnoBug(err),13310 .FAULT => |err| return syscall.errnoBug(err),
13286 .INVAL => |err| return syscall.errnoBug(err),13311 .INVAL => |err| return syscall.errnoBug(err),