| author | |
| committer | |
| log | 20355494085a6eb59b73816038a5d0c56b5492c6 |
| tree | 07234e26640c2f1626046fb5853d199e8af45a9a |
| parent | 8909d25b365de94d4b37c613ce3276634765a86b |
| signature | Commit is signed but in an unrecognized format. |
A couple of changes to improve reception of control data when using the
datagram API.
Instead of having a separate control buffer per received message,
there's one big buffer that all messages share (like the data buffer).
This means that now IncomingMessage has no user-initialised fields, and
can be initialised with undefined (which means that .init is now
deprecated).
Also, instead of taking a pointer to mutable IncomingMessage,
netReceiveOne* functions simply return a IncomingMessage.5 files changed, 106 insertions(+), 23 deletions(-)
lib/std/Io.zig+1| ... | ... | @@ -346,6 +346,7 @@ pub const Operation = union(enum) { |
| 346 | 346 | socket_handle: net.Socket.Handle, |
| 347 | 347 | message_buffer: []net.IncomingMessage, |
| 348 | 348 | data_buffer: []u8, |
| 349 | control_buffer: []u8 = &.{}, | |
| 349 | 350 | flags: net.ReceiveFlags, |
| 350 | 351 | |
| 351 | 352 | pub const Error = error{ |
lib/std/Io/Kqueue.zig+2| ... | ... | @@ -1183,6 +1183,7 @@ fn netReceive( |
| 1183 | 1183 | handle: net.Socket.Handle, |
| 1184 | 1184 | message_buffer: []net.IncomingMessage, |
| 1185 | 1185 | data_buffer: []u8, |
| 1186 | control_buffer: []u8, | |
| 1186 | 1187 | flags: net.ReceiveFlags, |
| 1187 | 1188 | timeout: Io.Timeout, |
| 1188 | 1189 | ) struct { ?net.Socket.ReceiveTimeoutError, usize } { |
| ... | ... | @@ -1191,6 +1192,7 @@ fn netReceive( |
| 1191 | 1192 | _ = handle; |
| 1192 | 1193 | _ = message_buffer; |
| 1193 | 1194 | _ = data_buffer; |
| 1195 | _ = control_buffer; | |
| 1194 | 1196 | _ = flags; |
| 1195 | 1197 | _ = timeout; |
| 1196 | 1198 | @panic("TODO"); |
lib/std/Io/Threaded.zig+18-8| ... | ... | @@ -2555,8 +2555,8 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper |
| 2555 | 2555 | .device_io_control => |*o| return .{ .device_io_control = try deviceIoControl(o) }, |
| 2556 | 2556 | .net_receive => |*o| return .{ .net_receive = o: { |
| 2557 | 2557 | 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.flags); | |
| 2559 | netReceivePosix(o.socket_handle, &o.message_buffer[0], o.data_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.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) { | |
| 2560 | 2560 | error.Canceled => |e| return e, |
| 2561 | 2561 | error.WouldBlock => unreachable, |
| 2562 | 2562 | else => |e| break :o .{ e, 0 }, |
| ... | ... | @@ -2825,9 +2825,11 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout |
| 2825 | 2825 | .device_io_control => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR), |
| 2826 | 2826 | .net_receive => |*o| nb: { |
| 2827 | 2827 | var data_i: usize = 0; |
| 2828 | var control_i: usize = 0; | |
| 2828 | 2829 | const result: Io.Operation.Result = .{ .net_receive = for (o.message_buffer, 0..) |*msg, msg_i| { |
| 2829 | 2830 | const remaining_data_buffer = o.data_buffer[data_i..]; |
| 2830 | netReceivePosix(o.socket_handle, msg, remaining_data_buffer, o.flags, true) catch |err| switch (err) { | |
| 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) { | |
| 2831 | 2833 | error.Canceled => |e| return e, |
| 2832 | 2834 | error.WouldBlock => { |
| 2833 | 2835 | if (msg_i != 0) break .{ null, msg_i }; |
| ... | ... | @@ -2837,6 +2839,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout |
| 2837 | 2839 | else => |e| break .{ e, 0 }, |
| 2838 | 2840 | }; |
| 2839 | 2841 | data_i += msg.data.len; |
| 2842 | control_i += msg.control.len; | |
| 2840 | 2843 | } else .{ null, o.message_buffer.len } }; |
| 2841 | 2844 | switch (b.completed.tail) { |
| 2842 | 2845 | .none => b.completed.head = index, |
| ... | ... | @@ -3279,7 +3282,7 @@ fn batchDrainSubmittedWindows(t: *Threaded, b: *Io.Batch, concurrency: bool) (Io |
| 3279 | 3282 | // TODO integrate with overlapped I/O or equivalent to avoid this error |
| 3280 | 3283 | if (concurrency) return error.ConcurrencyUnavailable; |
| 3281 | 3284 | batchCompleteBlockingWindows(b, operation_userdata, .{ |
| 3282 | .net_receive = netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags), | |
| 3285 | .net_receive = netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.control_buffer, o.flags), | |
| 3283 | 3286 | }); |
| 3284 | 3287 | }, |
| 3285 | 3288 | .net_send => |*o| { |
| ... | ... | @@ -13212,10 +13215,11 @@ fn netSendManyPosix( |
| 13212 | 13215 | } |
| 13213 | 13216 | } |
| 13214 | 13217 | |
| 13215 | fn netReceivePosix( | |
| 13218 | fn netReceiveOnePosix( | |
| 13216 | 13219 | socket_handle: net.Socket.Handle, |
| 13217 | 13220 | message: *net.IncomingMessage, |
| 13218 | 13221 | data_buffer: []u8, |
| 13222 | control_buffer: []u8, | |
| 13219 | 13223 | flags: net.ReceiveFlags, |
| 13220 | 13224 | nonblocking: bool, |
| 13221 | 13225 | ) (net.Socket.ReceiveError || error{WouldBlock})!void { |
| ... | ... | @@ -13238,22 +13242,24 @@ fn netReceivePosix( |
| 13238 | 13242 | .namelen = @sizeOf(PosixAddress), |
| 13239 | 13243 | .iov = (&iov)[0..1], |
| 13240 | 13244 | .iovlen = 1, |
| 13241 | .control = message.control.ptr, | |
| 13242 | .controllen = @intCast(message.control.len), | |
| 13245 | .control = control_buffer.ptr, | |
| 13246 | .controllen = @intCast(control_buffer.len), | |
| 13243 | 13247 | .flags = 0, |
| 13244 | 13248 | }; |
| 13245 | 13249 | |
| 13246 | 13250 | const syscall = try Syscall.start(); |
| 13247 | 13251 | while (true) { |
| 13248 | 13252 | const rc = posix.system.recvmsg(socket_handle, &msg, posix_flags); |
| 13253 | const rc_control = msg.controllen; | |
| 13249 | 13254 | switch (posix.errno(rc)) { |
| 13250 | 13255 | .SUCCESS => { |
| 13251 | 13256 | syscall.finish(); |
| 13252 | 13257 | const data = data_buffer[0..@intCast(rc)]; |
| 13258 | const control = control_buffer[0..@intCast(rc_control)]; | |
| 13253 | 13259 | message.* = .{ |
| 13254 | 13260 | .from = addressFromPosix(&storage), |
| 13255 | 13261 | .data = data, |
| 13256 | .control = if (msg.control) |ptr| @as([*]u8, @ptrCast(ptr))[0..msg.controllen] else message.control, | |
| 13262 | .control = control, | |
| 13257 | 13263 | .flags = .{ |
| 13258 | 13264 | .eor = (msg.flags & posix.MSG.EOR) != 0, |
| 13259 | 13265 | .trunc = (msg.flags & posix.MSG.TRUNC) != 0, |
| ... | ... | @@ -13294,8 +13300,12 @@ fn netReceiveWindows( |
| 13294 | 13300 | socket_handle: net.Socket.Handle, |
| 13295 | 13301 | message_buffer: []net.IncomingMessage, |
| 13296 | 13302 | data_buffer: []u8, |
| 13303 | control_buffer: []u8, | |
| 13297 | 13304 | flags: net.ReceiveFlags, |
| 13298 | 13305 | ) struct { ?net.Socket.ReceiveError, usize } { |
| 13306 | // Windows doesn't have the concept of control/ancillary data. | |
| 13307 | _ = control_buffer; | |
| 13308 | ||
| 13299 | 13309 | t.netReceiveOneWindows(socket_handle, &message_buffer[0], data_buffer, flags) catch |err| |
| 13300 | 13310 | return .{ err, 0 }; |
| 13301 | 13311 | return .{ null, 1 }; |
lib/std/Io/Uring.zig+9-4| ... | ... | @@ -2095,7 +2095,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper |
| 2095 | 2095 | }, |
| 2096 | 2096 | .net_receive => |o| .{ |
| 2097 | 2097 | .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.flags); | |
| 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); | |
| 2099 | 2099 | break :r .{ |
| 2100 | 2100 | if (opt_err) |err| switch (err) { |
| 2101 | 2101 | error.Canceled => |e| return e, |
| ... | ... | @@ -5075,15 +5075,18 @@ fn netReceive( |
| 5075 | 5075 | handle: net.Socket.Handle, |
| 5076 | 5076 | message_buffer: []net.IncomingMessage, |
| 5077 | 5077 | data_buffer: []u8, |
| 5078 | control_buffer: []u8, | |
| 5078 | 5079 | flags: net.ReceiveFlags, |
| 5079 | 5080 | ) struct { ?net.Socket.ReceiveError, usize } { |
| 5080 | 5081 | var message_i: usize = 0; |
| 5081 | 5082 | var data_i: usize = 0; |
| 5083 | var control_i: usize = 0; | |
| 5082 | 5084 | |
| 5083 | 5085 | while (true) { |
| 5084 | 5086 | if (message_buffer.len - message_i == 0) return .{ null, message_i }; |
| 5085 | 5087 | const message = &message_buffer[message_i]; |
| 5086 | 5088 | const remaining_data_buffer = data_buffer[data_i..]; |
| 5089 | const remaining_control_buffer = control_buffer[control_i..]; | |
| 5087 | 5090 | var storage: PosixAddress = undefined; |
| 5088 | 5091 | var iov: iovec = .{ .base = remaining_data_buffer.ptr, .len = remaining_data_buffer.len }; |
| 5089 | 5092 | var msg: linux.msghdr = .{ |
| ... | ... | @@ -5091,8 +5094,8 @@ fn netReceive( |
| 5091 | 5094 | .namelen = @sizeOf(PosixAddress), |
| 5092 | 5095 | .iov = (&iov)[0..1], |
| 5093 | 5096 | .iovlen = 1, |
| 5094 | .control = message.control.ptr, | |
| 5095 | .controllen = @intCast(message.control.len), | |
| 5097 | .control = remaining_control_buffer.ptr, | |
| 5098 | .controllen = @intCast(remaining_control_buffer.len), | |
| 5096 | 5099 | .flags = undefined, |
| 5097 | 5100 | }; |
| 5098 | 5101 | |
| ... | ... | @@ -5121,11 +5124,13 @@ fn netReceive( |
| 5121 | 5124 | switch (completion.errno()) { |
| 5122 | 5125 | .SUCCESS => { |
| 5123 | 5126 | const data = remaining_data_buffer[0..@intCast(completion.result)]; |
| 5127 | const control = remaining_control_buffer[0..@intCast(msg.controllen)]; | |
| 5124 | 5128 | data_i += data.len; |
| 5129 | control_i += @intCast(msg.controllen); | |
| 5125 | 5130 | message.* = .{ |
| 5126 | 5131 | .from = addressFromPosix(&storage), |
| 5127 | 5132 | .data = data, |
| 5128 | .control = if (msg.control) |ptr| @as([*]u8, @ptrCast(ptr))[0..msg.controllen] else message.control, | |
| 5133 | .control = control, | |
| 5129 | 5134 | .flags = .{ |
| 5130 | 5135 | .eor = msg.flags & linux.MSG.EOR != 0, |
| 5131 | 5136 | .trunc = msg.flags & linux.MSG.TRUNC != 0, |
lib/std/Io/net.zig+76-11| ... | ... | @@ -924,22 +924,20 @@ pub const ReceiveFlags = packed struct(u8) { |
| 924 | 924 | _: u5 = 0, |
| 925 | 925 | }; |
| 926 | 926 | |
| 927 | /// Fields are populated by receive functions. | |
| 927 | 928 | pub const IncomingMessage = struct { |
| 928 | /// Populated by receive functions. | |
| 929 | 929 | from: IpAddress, |
| 930 | /// Populated by receive functions, points into the caller-supplied buffer. | |
| 930 | /// Points into caller-supplied buffer. | |
| 931 | 931 | data: []u8, |
| 932 | /// Supplied by caller before calling receive functions; mutated by receive | |
| 933 | /// functions. | |
| 932 | /// Points into caller-supplied buffer. | |
| 934 | 933 | control: []u8, |
| 935 | /// Populated by receive functions. | |
| 936 | 934 | flags: Flags, |
| 937 | 935 | |
| 938 | /// Useful for initializing before calling `receiveManyTimeout`. | |
| 936 | /// Deprecated. Initialize with `undefined`. | |
| 939 | 937 | pub const init: IncomingMessage = .{ |
| 940 | 938 | .from = undefined, |
| 941 | 939 | .data = undefined, |
| 942 | .control = &.{}, | |
| 940 | .control = undefined, | |
| 943 | 941 | .flags = undefined, |
| 944 | 942 | }; |
| 945 | 943 | |
| ... | ... | @@ -1162,13 +1160,30 @@ pub const Socket = struct { |
| 1162 | 1160 | /// Waits for data. Connectionless. |
| 1163 | 1161 | /// |
| 1164 | 1162 | /// See also: |
| 1163 | /// * `receiveWithControl` | |
| 1165 | 1164 | /// * `receiveTimeout` |
| 1165 | /// * `receiveWithControlTimeout` | |
| 1166 | /// * `receiveManyTimeout` | |
| 1167 | /// * `receiveManyWithControlTimeout` | |
| 1166 | 1168 | pub fn receive(s: *const Socket, io: Io, buffer: []u8) ReceiveError!IncomingMessage { |
| 1167 | var message: IncomingMessage = .init; | |
| 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; | |
| 1168 | 1182 | const maybe_err, const count = (try io.operate(.{ .net_receive = .{ |
| 1169 | 1183 | .socket_handle = s.handle, |
| 1170 | 1184 | .message_buffer = (&message)[0..1], |
| 1171 | 1185 | .data_buffer = buffer, |
| 1186 | .control_buffer = control, | |
| 1172 | 1187 | .flags = .{}, |
| 1173 | 1188 | } })).net_receive; |
| 1174 | 1189 | if (maybe_err) |err| return err; |
| ... | ... | @@ -1184,18 +1199,42 @@ pub const Socket = struct { |
| 1184 | 1199 | /// |
| 1185 | 1200 | /// See also: |
| 1186 | 1201 | /// * `receive` |
| 1202 | /// * `receiveWithControl` | |
| 1203 | /// * `receiveWithControlTimeout` | |
| 1187 | 1204 | /// * `receiveManyTimeout` |
| 1205 | /// * `receiveManyWithControlTimeout` | |
| 1188 | 1206 | pub fn receiveTimeout( |
| 1189 | 1207 | s: *const Socket, |
| 1190 | 1208 | io: Io, |
| 1191 | 1209 | buffer: []u8, |
| 1192 | 1210 | timeout: Io.Timeout, |
| 1193 | 1211 | ) ReceiveTimeoutError!IncomingMessage { |
| 1194 | var message: IncomingMessage = .init; | |
| 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; | |
| 1195 | 1233 | const maybe_err, const count = (try io.operateTimeout(.{ .net_receive = .{ |
| 1196 | 1234 | .socket_handle = s.handle, |
| 1197 | 1235 | .message_buffer = (&message)[0..1], |
| 1198 | 1236 | .data_buffer = buffer, |
| 1237 | .control_buffer = control, | |
| 1199 | 1238 | .flags = .{}, |
| 1200 | 1239 | } }, timeout)).net_receive; |
| 1201 | 1240 | if (maybe_err) |err| return err; |
| ... | ... | @@ -1211,21 +1250,47 @@ pub const Socket = struct { |
| 1211 | 1250 | /// |
| 1212 | 1251 | /// See also: |
| 1213 | 1252 | /// * `receive` |
| 1253 | /// * `receiveWithControl` | |
| 1214 | 1254 | /// * `receiveTimeout` |
| 1255 | /// * `receiveWithControlTimeout` | |
| 1256 | /// * `receiveManyWithControlTimeout` | |
| 1215 | 1257 | pub fn receiveManyTimeout( |
| 1216 | 1258 | s: *const Socket, |
| 1217 | 1259 | io: Io, |
| 1218 | /// Function assumes each element has initialized `control` field. | |
| 1219 | /// Initializing with `IncomingMessage.init` may be helpful. | |
| 1220 | 1260 | message_buffer: []IncomingMessage, |
| 1221 | 1261 | data_buffer: []u8, |
| 1222 | 1262 | flags: ReceiveFlags, |
| 1223 | 1263 | 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, | |
| 1224 | 1288 | ) struct { ?ReceiveTimeoutError, usize } { |
| 1225 | 1289 | const result = io.operateTimeout(.{ .net_receive = .{ |
| 1226 | 1290 | .socket_handle = s.handle, |
| 1227 | 1291 | .message_buffer = message_buffer, |
| 1228 | 1292 | .data_buffer = data_buffer, |
| 1293 | .control_buffer = control_buffer, | |
| 1229 | 1294 | .flags = flags, |
| 1230 | 1295 | } }, timeout) catch |err| return .{ err, 0 }; |
| 1231 | 1296 | return result.net_receive; |