| author | |
| committer | |
| log | 875cb2ff2f321ec40b02f8de40230a28e56d6844 |
| tree | 6ca545a4e9eac344eef1b46a6865afac174c171e |
| parent | 20355494085a6eb59b73816038a5d0c56b5492c6 |
| signature | Commit is signed but in an unrecognized format. |
Also adds a way to read control data using Stream.Reader. Stream.Reader
will buffer control data until it can't no more, in which case it will
be truncated.
The readv syscall in netReadPosix has been replaced with recvmsg.3 files changed, 87 insertions(+), 15 deletions(-)
lib/std/Io.zig+2-1| ... | @@ -431,6 +431,7 @@ pub const Operation = union(enum) { | ... | @@ -431,6 +431,7 @@ pub const Operation = union(enum) { |
| 431 | pub const NetRead = struct { | 431 | pub const NetRead = struct { |
| 432 | socket_handle: net.Socket.Handle, | 432 | socket_handle: net.Socket.Handle, |
| 433 | data: [][]u8, | 433 | data: [][]u8, |
| 434 | control: []u8 = &.{}, | ||
| 434 | 435 | ||
| 435 | pub const Error = error{ | 436 | pub const Error = error{ |
| 436 | SystemResources, | 437 | SystemResources, |
| ... | @@ -444,7 +445,7 @@ pub const Operation = union(enum) { | ... | @@ -444,7 +445,7 @@ pub const Operation = union(enum) { |
| 444 | ConnectionTimedOut, | 445 | ConnectionTimedOut, |
| 445 | } || Io.UnexpectedError; | 446 | } || Io.UnexpectedError; |
| 446 | 447 | ||
| 447 | pub const Result = Error!usize; | 448 | pub const Result = Error!net.Stream.ReadResult; |
| 448 | }; | 449 | }; |
| 449 | 450 | ||
| 450 | pub const NetWrite = struct { | 451 | pub const NetWrite = struct { |
lib/std/Io/Threaded.zig+26-8| ... | @@ -2583,7 +2583,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper | ... | @@ -2583,7 +2583,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper |
| 2583 | }, | 2583 | }, |
| 2584 | }, | 2584 | }, |
| 2585 | .net_read => |o| return .{ | 2585 | .net_read => |o| return .{ |
| 2586 | .net_read = netRead(o.socket_handle, o.data) catch |err| switch (err) { | 2586 | .net_read = netRead(o.socket_handle, o.data, o.control) catch |err| switch (err) { |
| 2587 | error.Canceled => |e| return e, | 2587 | error.Canceled => |e| return e, |
| 2588 | else => |e| e, | 2588 | else => |e| e, |
| 2589 | }, | 2589 | }, |
| ... | @@ -3296,7 +3296,7 @@ fn batchDrainSubmittedWindows(t: *Threaded, b: *Io.Batch, concurrency: bool) (Io | ... | @@ -3296,7 +3296,7 @@ fn batchDrainSubmittedWindows(t: *Threaded, b: *Io.Batch, concurrency: bool) (Io |
| 3296 | // TODO integrate with overlapped I/O or equivalent to avoid this error | 3296 | // TODO integrate with overlapped I/O or equivalent to avoid this error |
| 3297 | if (concurrency) return error.ConcurrencyUnavailable; | 3297 | if (concurrency) return error.ConcurrencyUnavailable; |
| 3298 | batchCompleteBlockingWindows(b, operation_userdata, .{ | 3298 | batchCompleteBlockingWindows(b, operation_userdata, .{ |
| 3299 | .net_read = netRead(o.socket_handle, o.data) catch |err| switch (err) { | 3299 | .net_read = netRead(o.socket_handle, o.data, o.control) catch |err| switch (err) { |
| 3300 | error.Canceled => |e| return e, | 3300 | error.Canceled => |e| return e, |
| 3301 | else => |e| e, | 3301 | else => |e| e, |
| 3302 | }, | 3302 | }, |
| ... | @@ -12886,14 +12886,14 @@ fn deferAcceptAfd(t: *Threaded, listen_handle: net.Socket.Handle, info: windows. | ... | @@ -12886,14 +12886,14 @@ fn deferAcceptAfd(t: *Threaded, listen_handle: net.Socket.Handle, info: windows. |
| 12886 | } | 12886 | } |
| 12887 | } | 12887 | } |
| 12888 | 12888 | ||
| 12889 | fn netRead(socket_handle: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize { | 12889 | fn netRead(socket_handle: net.Socket.Handle, data: [][]u8, control: []u8) net.Stream.Reader.Error!net.Stream.ReadResult { |
| 12890 | if (!have_networking) return error.NetworkDown; | 12890 | if (!have_networking) return error.NetworkDown; |
| 12891 | 12891 | ||
| 12892 | if (is_windows) return netReadWindows(socket_handle, data); | 12892 | if (is_windows) return .{ .data_len = try netReadWindows(socket_handle, data) }; |
| 12893 | return netReadPosix(socket_handle, data); | 12893 | return netReadPosix(socket_handle, data, control); |
| 12894 | } | 12894 | } |
| 12895 | 12895 | ||
| 12896 | fn netReadPosix(fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize { | 12896 | fn netReadPosix(fd: net.Socket.Handle, data: [][]u8, control: []u8) net.Stream.Reader.Error!net.Stream.ReadResult { |
| 12897 | var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; | 12897 | var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; |
| 12898 | var i: usize = 0; | 12898 | var i: usize = 0; |
| 12899 | for (data) |buf| { | 12899 | for (data) |buf| { |
| ... | @@ -12939,13 +12939,31 @@ fn netReadPosix(fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usi | ... | @@ -12939,13 +12939,31 @@ fn netReadPosix(fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usi |
| 12939 | } | 12939 | } |
| 12940 | } | 12940 | } |
| 12941 | 12941 | ||
| 12942 | var msg: posix.msghdr = .{ | ||
| 12943 | .name = null, | ||
| 12944 | .namelen = 0, | ||
| 12945 | .iov = dest.ptr, | ||
| 12946 | .iovlen = @intCast(dest.len), | ||
| 12947 | .control = control.ptr, | ||
| 12948 | .controllen = @intCast(control.len), | ||
| 12949 | .flags = 0, | ||
| 12950 | }; | ||
| 12951 | |||
| 12952 | const flags: u32 = | ||
| 12953 | @as(u32, if (@hasDecl(posix.MSG, "CMSG_CLOEXEC")) posix.MSG.CMSG_CLOEXEC else 0); | ||
| 12954 | |||
| 12942 | const syscall: Syscall = try .start(); | 12955 | const syscall: Syscall = try .start(); |
| 12943 | while (true) { | 12956 | while (true) { |
| 12944 | const rc = posix.system.readv(fd, dest.ptr, @intCast(dest.len)); | 12957 | const rc = posix.system.recvmsg(fd, &msg, flags); |
| 12958 | const rc_control = msg.controllen; | ||
| 12945 | switch (posix.errno(rc)) { | 12959 | switch (posix.errno(rc)) { |
| 12946 | .SUCCESS => { | 12960 | .SUCCESS => { |
| 12947 | syscall.finish(); | 12961 | syscall.finish(); |
| 12948 | return @intCast(rc); | 12962 | return .{ |
| 12963 | .data_len = @intCast(rc), | ||
| 12964 | .control_len = @intCast(rc_control), | ||
| 12965 | .control_truncated = (msg.flags & posix.MSG.CTRUNC) != 0, | ||
| 12966 | }; | ||
| 12949 | }, | 12967 | }, |
| 12950 | .INTR => { | 12968 | .INTR => { |
| 12951 | try syscall.checkCancel(); | 12969 | try syscall.checkCancel(); |
lib/std/Io/net.zig+59-6| ... | @@ -1334,12 +1334,36 @@ pub const Stream = struct { | ... | @@ -1334,12 +1334,36 @@ pub const Stream = struct { |
| 1334 | 1334 | ||
| 1335 | const max_iovecs_len = 8; | 1335 | const max_iovecs_len = 8; |
| 1336 | 1336 | ||
| 1337 | pub const ReadResult = struct { | ||
| 1338 | data_len: usize, | ||
| 1339 | control_len: usize = 0, | ||
| 1340 | /// Whether only some of the control data was received. | ||
| 1341 | /// | ||
| 1342 | /// When control data is truncated, the extra data essentially | ||
| 1343 | /// disappears into the ether. This is indicative of a design problem, | ||
| 1344 | /// such as the control buffer being too small. | ||
| 1345 | control_truncated: bool = false, | ||
| 1346 | }; | ||
| 1347 | |||
| 1337 | /// This is a low-level API that calls the `Io` interface function directly. | 1348 | /// This is a low-level API that calls the `Io` interface function directly. |
| 1338 | /// For a higher level API, see `reader`. | 1349 | /// For a higher level API, see `reader`. |
| 1339 | pub fn read(s: *const Stream, io: Io, data: [][]u8) Reader.Error!usize { | 1350 | pub fn read(s: *const Stream, io: Io, data: [][]u8) Reader.Error!usize { |
| 1340 | return (try io.operate(.{ .net_read = .{ | 1351 | const rc, _ = try (try io.operate(.{ .net_read = .{ |
| 1352 | .socket_handle = s.socket.handle, | ||
| 1353 | .data = data, | ||
| 1354 | } })).net_read; | ||
| 1355 | return rc; | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | /// Read with control data. | ||
| 1359 | /// | ||
| 1360 | /// This is a low-level API that calls the `Io` interface function directly. | ||
| 1361 | /// For a higher level API, see `reader`. | ||
| 1362 | pub fn readWithControl(s: *const Stream, io: Io, data: [][]u8, control: []u8) Reader.Error!ReadResult { | ||
| 1363 | return try (try io.operate(.{ .net_read = .{ | ||
| 1341 | .socket_handle = s.socket.handle, | 1364 | .socket_handle = s.socket.handle, |
| 1342 | .data = data, | 1365 | .data = data, |
| 1366 | .control = control, | ||
| 1343 | } })).net_read; | 1367 | } })).net_read; |
| 1344 | } | 1368 | } |
| 1345 | 1369 | ||
| ... | @@ -1354,12 +1378,20 @@ pub const Stream = struct { | ... | @@ -1354,12 +1378,20 @@ pub const Stream = struct { |
| 1354 | pub const Reader = struct { | 1378 | pub const Reader = struct { |
| 1355 | io: Io, | 1379 | io: Io, |
| 1356 | interface: Io.Reader, | 1380 | interface: Io.Reader, |
| 1381 | control_buffer: []u8, | ||
| 1382 | control_len: usize, | ||
| 1383 | control_truncated: bool, | ||
| 1357 | stream: Stream, | 1384 | stream: Stream, |
| 1358 | err: ?Error, | 1385 | err: ?Error, |
| 1359 | 1386 | ||
| 1360 | pub const Error = Io.Operation.NetRead.Error || Io.Cancelable; | 1387 | pub const Error = Io.Operation.NetRead.Error || Io.Cancelable; |
| 1361 | 1388 | ||
| 1362 | pub fn init(stream: Stream, io: Io, buffer: []u8) Reader { | 1389 | pub fn init(stream: Stream, io: Io, buffer: []u8) Reader { |
| 1390 | return initWithControl(stream, io, buffer, &.{}); | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | /// Same as `init`, but also provides a buffer for storing control data. | ||
| 1394 | pub fn initWithControl(stream: Stream, io: Io, buffer: []u8, control_buffer: []u8) Reader { | ||
| 1363 | return .{ | 1395 | return .{ |
| 1364 | .io = io, | 1396 | .io = io, |
| 1365 | .interface = .{ | 1397 | .interface = .{ |
| ... | @@ -1371,6 +1403,9 @@ pub const Stream = struct { | ... | @@ -1371,6 +1403,9 @@ pub const Stream = struct { |
| 1371 | .seek = 0, | 1403 | .seek = 0, |
| 1372 | .end = 0, | 1404 | .end = 0, |
| 1373 | }, | 1405 | }, |
| 1406 | .control_buffer = control_buffer, | ||
| 1407 | .control_len = 0, | ||
| 1408 | .control_truncated = false, | ||
| 1374 | .stream = stream, | 1409 | .stream = stream, |
| 1375 | .err = null, | 1410 | .err = null, |
| 1376 | }; | 1411 | }; |
| ... | @@ -1391,18 +1426,31 @@ pub const Stream = struct { | ... | @@ -1391,18 +1426,31 @@ pub const Stream = struct { |
| 1391 | const dest_n, const data_size = try io_r.writableVector(&iovecs_buffer, data); | 1426 | const dest_n, const data_size = try io_r.writableVector(&iovecs_buffer, data); |
| 1392 | const dest = iovecs_buffer[0..dest_n]; | 1427 | const dest = iovecs_buffer[0..dest_n]; |
| 1393 | assert(dest[0].len > 0); | 1428 | assert(dest[0].len > 0); |
| 1394 | const n = r.stream.read(io, dest) catch |err| { | 1429 | const result = r.stream.readWithControl(io, dest, r.control_buffer[r.control_len..]) catch |err| { |
| 1395 | r.err = err; | 1430 | r.err = err; |
| 1396 | return error.ReadFailed; | 1431 | return error.ReadFailed; |
| 1397 | }; | 1432 | }; |
| 1398 | if (n == 0) { | 1433 | r.control_len += result.control_len; |
| 1434 | r.control_truncated = r.control_truncated or result.control_truncated; | ||
| 1435 | if (result.data_len == 0) { | ||
| 1399 | return error.EndOfStream; | 1436 | return error.EndOfStream; |
| 1400 | } | 1437 | } |
| 1401 | if (n > data_size) { | 1438 | if (result.data_len > data_size) { |
| 1402 | r.interface.end += n - data_size; | 1439 | r.interface.end += result.data_len - data_size; |
| 1403 | return data_size; | 1440 | return data_size; |
| 1404 | } | 1441 | } |
| 1405 | return n; | 1442 | return result.data_len; |
| 1443 | } | ||
| 1444 | |||
| 1445 | /// Get buffered control data. | ||
| 1446 | pub fn controlSlice(r: *const Reader) []u8 { | ||
| 1447 | return r.control_buffer[0..r.control_len]; | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | /// Clear buffered control data. | ||
| 1451 | pub fn clearControl(r: *Reader) void { | ||
| 1452 | r.control_len = 0; | ||
| 1453 | r.control_truncated = false; | ||
| 1406 | } | 1454 | } |
| 1407 | }; | 1455 | }; |
| 1408 | 1456 | ||
| ... | @@ -1489,6 +1537,11 @@ pub const Stream = struct { | ... | @@ -1489,6 +1537,11 @@ pub const Stream = struct { |
| 1489 | return .init(stream, io, buffer); | 1537 | return .init(stream, io, buffer); |
| 1490 | } | 1538 | } |
| 1491 | 1539 | ||
| 1540 | /// Same as `reader`, but also provides a buffer for storing control data. | ||
| 1541 | pub fn readerWithControl(stream: Stream, io: Io, buffer: []u8, control_buffer: []u8) Reader { | ||
| 1542 | return .initWithControl(stream, io, buffer, control_buffer); | ||
| 1543 | } | ||
| 1544 | |||
| 1492 | pub fn writer(stream: Stream, io: Io, buffer: []u8) Writer { | 1545 | pub fn writer(stream: Stream, io: Io, buffer: []u8) Writer { |
| 1493 | return .init(stream, io, buffer); | 1546 | return .init(stream, io, buffer); |
| 1494 | } | 1547 | } |