authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-10-01 10:07:01+02:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-10-01 11:21:28+02:00
log2217d43cc91145c4f569108594f6d246d4733752
tree652fcdabd7a9a1e5f476e8061c1990806ebd5c32
parentc53cb65568e80a1d9c20a07373db86377ee9421b
signature Commit is signed but in an unrecognized format.

Revert "std.Io.net: improve reception of control data"

It is not clear to me that this is the right path to take, using a shared buffer for control data makes managing the alignment trickier, and would require the Io interface itself to deal with alignment. For an example of what I consider clean design, see net_read/net_write. There is no need for the Io interface/operations to be aware of control buffer alignment and it is sufficient for the net.Stream.Reader abstraction to use an aligned buffer. Also this has no test coverage and is (to my knowledege) not currently blocking any real world Zig project. This reverts commit 7c0347fce0b6f510b43ca503ad9c1cd2f8ac9a6b.

5 files changed, 23 insertions(+), 106 deletions(-)

lib/std/Io.zig-1
......@@ -346,7 +346,6 @@ pub const Operation = union(enum) {
346346 socket_handle: net.Socket.Handle,
347347 message_buffer: []net.IncomingMessage,
348348 data_buffer: []u8,
349 control_buffer: []u8 = &.{},
350349 flags: net.ReceiveFlags,
351350
352351 pub const Error = error{
lib/std/Io/Kqueue.zig-2
......@@ -1183,7 +1183,6 @@ fn netReceive(
11831183 handle: net.Socket.Handle,
11841184 message_buffer: []net.IncomingMessage,
11851185 data_buffer: []u8,
1186 control_buffer: []u8,
11871186 flags: net.ReceiveFlags,
11881187 timeout: Io.Timeout,
11891188) struct { ?net.Socket.ReceiveTimeoutError, usize } {
......@@ -1192,7 +1191,6 @@ fn netReceive(
11921191 _ = handle;
11931192 _ = message_buffer;
11941193 _ = data_buffer;
1195 _ = control_buffer;
11961194 _ = flags;
11971195 _ = timeout;
11981196 @panic("TODO");
lib/std/Io/Threaded.zig+8-18
......@@ -2555,8 +2555,8 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper
25552555 .device_io_control => |*o| return .{ .device_io_control = try deviceIoControl(o) },
25562556 .net_receive => |*o| return .{ .net_receive = o: {
25572557 if (!have_networking) break :o .{ error.NetworkDown, 0 };
2558 if (is_windows) break :o netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.control_buffer, o.flags);
2559 netReceiveOnePosix(o.socket_handle, &o.message_buffer[0], o.data_buffer, o.control_buffer, o.flags, false) catch |err| switch (err) {
2558 if (is_windows) break :o netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags);
2559 netReceivePosix(o.socket_handle, &o.message_buffer[0], o.data_buffer, o.flags, false) catch |err| switch (err) {
25602560 error.Canceled => |e| return e,
25612561 error.WouldBlock => unreachable,
25622562 else => |e| break :o .{ e, 0 },
......@@ -2825,11 +2825,9 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout
28252825 .device_io_control => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR),
28262826 .net_receive => |*o| nb: {
28272827 var data_i: usize = 0;
2828 var control_i: usize = 0;
28292828 const result: Io.Operation.Result = .{ .net_receive = for (o.message_buffer, 0..) |*msg, msg_i| {
28302829 const remaining_data_buffer = o.data_buffer[data_i..];
2831 const remaining_control_buffer = o.control_buffer[control_i..];
2832 netReceiveOnePosix(o.socket_handle, msg, remaining_data_buffer, remaining_control_buffer, o.flags, true) catch |err| switch (err) {
2830 netReceivePosix(o.socket_handle, msg, remaining_data_buffer, o.flags, true) catch |err| switch (err) {
28332831 error.Canceled => |e| return e,
28342832 error.WouldBlock => {
28352833 if (msg_i != 0) break .{ null, msg_i };
......@@ -2839,7 +2837,6 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout
28392837 else => |e| break .{ e, 0 },
28402838 };
28412839 data_i += msg.data.len;
2842 control_i += msg.control.len;
28432840 } else .{ null, o.message_buffer.len } };
28442841 switch (b.completed.tail) {
28452842 .none => b.completed.head = index,
......@@ -3282,7 +3279,7 @@ fn batchDrainSubmittedWindows(t: *Threaded, b: *Io.Batch, concurrency: bool) (Io
32823279 // TODO integrate with overlapped I/O or equivalent to avoid this error
32833280 if (concurrency) return error.ConcurrencyUnavailable;
32843281 batchCompleteBlockingWindows(b, operation_userdata, .{
3285 .net_receive = netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.control_buffer, o.flags),
3282 .net_receive = netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags),
32863283 });
32873284 },
32883285 .net_send => |*o| {
......@@ -13233,11 +13230,10 @@ fn netSendManyPosix(
1323313230 }
1323413231}
1323513232
13236fn netReceiveOnePosix(
13233fn netReceivePosix(
1323713234 socket_handle: net.Socket.Handle,
1323813235 message: *net.IncomingMessage,
1323913236 data_buffer: []u8,
13240 control_buffer: []u8,
1324113237 flags: net.ReceiveFlags,
1324213238 nonblocking: bool,
1324313239) (net.Socket.ReceiveError || error{WouldBlock})!void {
......@@ -13260,24 +13256,22 @@ fn netReceiveOnePosix(
1326013256 .namelen = @sizeOf(PosixAddress),
1326113257 .iov = (&iov)[0..1],
1326213258 .iovlen = 1,
13263 .control = control_buffer.ptr,
13264 .controllen = @intCast(control_buffer.len),
13259 .control = message.control.ptr,
13260 .controllen = @intCast(message.control.len),
1326513261 .flags = 0,
1326613262 };
1326713263
1326813264 const syscall = try Syscall.start();
1326913265 while (true) {
1327013266 const rc = posix.system.recvmsg(socket_handle, &msg, posix_flags);
13271 const rc_control = msg.controllen;
1327213267 switch (posix.errno(rc)) {
1327313268 .SUCCESS => {
1327413269 syscall.finish();
1327513270 const data = data_buffer[0..@intCast(rc)];
13276 const control = control_buffer[0..@intCast(rc_control)];
1327713271 message.* = .{
1327813272 .from = addressFromPosix(&storage),
1327913273 .data = data,
13280 .control = control,
13274 .control = if (msg.control) |ptr| @as([*]u8, @ptrCast(ptr))[0..msg.controllen] else message.control,
1328113275 .flags = .{
1328213276 .eor = (msg.flags & posix.MSG.EOR) != 0,
1328313277 .trunc = (msg.flags & posix.MSG.TRUNC) != 0,
......@@ -13318,12 +13312,8 @@ fn netReceiveWindows(
1331813312 socket_handle: net.Socket.Handle,
1331913313 message_buffer: []net.IncomingMessage,
1332013314 data_buffer: []u8,
13321 control_buffer: []u8,
1332213315 flags: net.ReceiveFlags,
1332313316) struct { ?net.Socket.ReceiveError, usize } {
13324 // Windows doesn't have the concept of control/ancillary data.
13325 _ = control_buffer;
13326
1332713317 t.netReceiveOneWindows(socket_handle, &message_buffer[0], data_buffer, flags) catch |err|
1332813318 return .{ err, 0 };
1332913319 return .{ null, 1 };
lib/std/Io/Uring.zig+4-9
......@@ -2095,7 +2095,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper
20952095 },
20962096 .net_receive => |o| .{
20972097 .net_receive = r: {
2098 const opt_err, const n = ev.netReceive(&maybe_sync.cancel_region, o.socket_handle, o.message_buffer, o.data_buffer, o.control_buffer, o.flags);
2098 const opt_err, const n = ev.netReceive(&maybe_sync.cancel_region, o.socket_handle, o.message_buffer, o.data_buffer, o.flags);
20992099 break :r .{
21002100 if (opt_err) |err| switch (err) {
21012101 error.Canceled => |e| return e,
......@@ -5075,18 +5075,15 @@ fn netReceive(
50755075 handle: net.Socket.Handle,
50765076 message_buffer: []net.IncomingMessage,
50775077 data_buffer: []u8,
5078 control_buffer: []u8,
50795078 flags: net.ReceiveFlags,
50805079) struct { ?net.Socket.ReceiveError, usize } {
50815080 var message_i: usize = 0;
50825081 var data_i: usize = 0;
5083 var control_i: usize = 0;
50845082
50855083 while (true) {
50865084 if (message_buffer.len - message_i == 0) return .{ null, message_i };
50875085 const message = &message_buffer[message_i];
50885086 const remaining_data_buffer = data_buffer[data_i..];
5089 const remaining_control_buffer = control_buffer[control_i..];
50905087 var storage: PosixAddress = undefined;
50915088 var iov: iovec = .{ .base = remaining_data_buffer.ptr, .len = remaining_data_buffer.len };
50925089 var msg: linux.msghdr = .{
......@@ -5094,8 +5091,8 @@ fn netReceive(
50945091 .namelen = @sizeOf(PosixAddress),
50955092 .iov = (&iov)[0..1],
50965093 .iovlen = 1,
5097 .control = remaining_control_buffer.ptr,
5098 .controllen = @intCast(remaining_control_buffer.len),
5094 .control = message.control.ptr,
5095 .controllen = @intCast(message.control.len),
50995096 .flags = undefined,
51005097 };
51015098
......@@ -5124,13 +5121,11 @@ fn netReceive(
51245121 switch (completion.errno()) {
51255122 .SUCCESS => {
51265123 const data = remaining_data_buffer[0..@intCast(completion.result)];
5127 const control = remaining_control_buffer[0..@intCast(msg.controllen)];
51285124 data_i += data.len;
5129 control_i += @intCast(msg.controllen);
51305125 message.* = .{
51315126 .from = addressFromPosix(&storage),
51325127 .data = data,
5133 .control = control,
5128 .control = if (msg.control) |ptr| @as([*]u8, @ptrCast(ptr))[0..msg.controllen] else message.control,
51345129 .flags = .{
51355130 .eor = msg.flags & linux.MSG.EOR != 0,
51365131 .trunc = msg.flags & linux.MSG.TRUNC != 0,
lib/std/Io/net.zig+11-76
......@@ -924,20 +924,22 @@ pub const ReceiveFlags = packed struct(u8) {
924924 _: u5 = 0,
925925};
926926
927/// Fields are populated by receive functions.
928927pub const IncomingMessage = struct {
928 /// Populated by receive functions.
929929 from: IpAddress,
930 /// Points into caller-supplied buffer.
930 /// Populated by receive functions, points into the caller-supplied buffer.
931931 data: []u8,
932 /// Points into caller-supplied buffer.
932 /// Supplied by caller before calling receive functions; mutated by receive
933 /// functions.
933934 control: []u8,
935 /// Populated by receive functions.
934936 flags: Flags,
935937
936 /// Deprecated. Initialize with `undefined`.
938 /// Useful for initializing before calling `receiveManyTimeout`.
937939 pub const init: IncomingMessage = .{
938940 .from = undefined,
939941 .data = undefined,
940 .control = undefined,
942 .control = &.{},
941943 .flags = undefined,
942944 };
943945
......@@ -1160,30 +1162,13 @@ pub const Socket = struct {
11601162 /// Waits for data. Connectionless.
11611163 ///
11621164 /// See also:
1163 /// * `receiveWithControl`
11641165 /// * `receiveTimeout`
1165 /// * `receiveWithControlTimeout`
1166 /// * `receiveManyTimeout`
1167 /// * `receiveManyWithControlTimeout`
11681166 pub fn receive(s: *const Socket, io: Io, buffer: []u8) ReceiveError!IncomingMessage {
1169 return s.receiveWithControl(io, buffer, &.{});
1170 }
1171
1172 /// Waits for data, allowing the reception of control data. Connectionless.
1173 ///
1174 /// See also:
1175 /// * `receive`
1176 /// * `receiveTimeout`
1177 /// * `receiveWithControlTimeout`
1178 /// * `receiveManyTimeout`
1179 /// * `receiveManyWithControlTimeout`
1180 pub fn receiveWithControl(s: *const Socket, io: Io, buffer: []u8, control: []u8) ReceiveError!IncomingMessage {
1181 var message: IncomingMessage = undefined;
1167 var message: IncomingMessage = .init;
11821168 const maybe_err, const count = (try io.operate(.{ .net_receive = .{
11831169 .socket_handle = s.handle,
11841170 .message_buffer = (&message)[0..1],
11851171 .data_buffer = buffer,
1186 .control_buffer = control,
11871172 .flags = .{},
11881173 } })).net_receive;
11891174 if (maybe_err) |err| return err;
......@@ -1199,42 +1184,18 @@ pub const Socket = struct {
11991184 ///
12001185 /// See also:
12011186 /// * `receive`
1202 /// * `receiveWithControl`
1203 /// * `receiveWithControlTimeout`
12041187 /// * `receiveManyTimeout`
1205 /// * `receiveManyWithControlTimeout`
12061188 pub fn receiveTimeout(
12071189 s: *const Socket,
12081190 io: Io,
12091191 buffer: []u8,
12101192 timeout: Io.Timeout,
12111193 ) ReceiveTimeoutError!IncomingMessage {
1212 return s.receiveWithControlTimeout(io, buffer, &.{}, timeout);
1213 }
1214
1215 /// Waits for data, allowing the reception of control data. Connectionless.
1216 ///
1217 /// Returns `error.Timeout` if no message arrives early enough.
1218 ///
1219 /// See also:
1220 /// * `receive`
1221 /// * `receiveWithControl`
1222 /// * `receiveTimeout`
1223 /// * `receiveManyTimeout`
1224 /// * `receiveManyWithControlTimeout`
1225 pub fn receiveWithControlTimeout(
1226 s: *const Socket,
1227 io: Io,
1228 buffer: []u8,
1229 control: []u8,
1230 timeout: Io.Timeout,
1231 ) ReceiveTimeoutError!IncomingMessage {
1232 var message: IncomingMessage = undefined;
1194 var message: IncomingMessage = .init;
12331195 const maybe_err, const count = (try io.operateTimeout(.{ .net_receive = .{
12341196 .socket_handle = s.handle,
12351197 .message_buffer = (&message)[0..1],
12361198 .data_buffer = buffer,
1237 .control_buffer = control,
12381199 .flags = .{},
12391200 } }, timeout)).net_receive;
12401201 if (maybe_err) |err| return err;
......@@ -1250,47 +1211,21 @@ pub const Socket = struct {
12501211 ///
12511212 /// See also:
12521213 /// * `receive`
1253 /// * `receiveWithControl`
12541214 /// * `receiveTimeout`
1255 /// * `receiveWithControlTimeout`
1256 /// * `receiveManyWithControlTimeout`
12571215 pub fn receiveManyTimeout(
12581216 s: *const Socket,
12591217 io: Io,
1218 /// Function assumes each element has initialized `control` field.
1219 /// Initializing with `IncomingMessage.init` may be helpful.
12601220 message_buffer: []IncomingMessage,
12611221 data_buffer: []u8,
12621222 flags: ReceiveFlags,
12631223 timeout: Io.Timeout,
1264 ) struct { ?ReceiveTimeoutError, usize } {
1265 return s.receiveManyWithControlTimeout(io, message_buffer, data_buffer, &.{}, flags, timeout);
1266 }
1267
1268 /// Waits until at least one message is delivered, possibly returning more
1269 /// than one message. Allows reception of control data. Connectionless.
1270 ///
1271 /// Returns number of messages received, or `error.Timeout` if no message
1272 /// arrives early enough.
1273 ///
1274 /// See also:
1275 /// * `receive`
1276 /// * `receiveWithControl`
1277 /// * `receiveTimeout`
1278 /// * `receiveWithControlTimeout`
1279 /// * `receiveManyTimeout`
1280 pub fn receiveManyWithControlTimeout(
1281 s: *const Socket,
1282 io: Io,
1283 message_buffer: []IncomingMessage,
1284 data_buffer: []u8,
1285 control_buffer: []u8,
1286 flags: ReceiveFlags,
1287 timeout: Io.Timeout,
12881224 ) struct { ?ReceiveTimeoutError, usize } {
12891225 const result = io.operateTimeout(.{ .net_receive = .{
12901226 .socket_handle = s.handle,
12911227 .message_buffer = message_buffer,
12921228 .data_buffer = data_buffer,
1293 .control_buffer = control_buffer,
12941229 .flags = flags,
12951230 } }, timeout) catch |err| return .{ err, 0 };
12961231 return result.net_receive;