| ... | @@ -731,6 +731,22 @@ pub const IO_Uring = struct { | ... | @@ -731,6 +731,22 @@ pub const IO_Uring = struct { |
| 731 | return sqe; | 731 | return sqe; |
| 732 | } | 732 | } |
| 733 | | 733 | |
| | 734 | /// Queues (but does not submit) an SQE to perform a `shutdown(2)`. |
| | 735 | /// Returns a pointer to the SQE. |
| | 736 | /// |
| | 737 | /// The operation is identified by its `user_data`. |
| | 738 | pub fn shutdown( |
| | 739 | self: *IO_Uring, |
| | 740 | user_data: u64, |
| | 741 | sockfd: os.socket_t, |
| | 742 | how: u32, |
| | 743 | ) !*io_uring_sqe { |
| | 744 | const sqe = try self.get_sqe(); |
| | 745 | io_uring_prep_shutdown(sqe, sockfd, how); |
| | 746 | sqe.user_data = user_data; |
| | 747 | return sqe; |
| | 748 | } |
| | 749 | |
| 734 | /// Registers an array of file descriptors. | 750 | /// Registers an array of file descriptors. |
| 735 | /// Every time a file descriptor is put in an SQE and submitted to the kernel, the kernel must | 751 | /// Every time a file descriptor is put in an SQE and submitted to the kernel, the kernel must |
| 736 | /// retrieve a reference to the file, and once I/O has completed the file reference must be | 752 | /// retrieve a reference to the file, and once I/O has completed the file reference must be |
| ... | @@ -798,7 +814,7 @@ pub const IO_Uring = struct { | ... | @@ -798,7 +814,7 @@ pub const IO_Uring = struct { |
| 798 | } | 814 | } |
| 799 | | 815 | |
| 800 | /// Registers the file descriptor for an eventfd that will be notified of completion events on | 816 | /// Registers the file descriptor for an eventfd that will be notified of completion events on |
| 801 | /// an io_uring instance. Notifications are only posted for events that complete in an async manner. | 817 | /// an io_uring instance. Notifications are only posted for events that complete in an async manner. |
| 802 | /// This means that events that complete inline while being submitted do not trigger a notification event. | 818 | /// This means that events that complete inline while being submitted do not trigger a notification event. |
| 803 | /// Only a single eventfd can be registered at any given point in time. | 819 | /// Only a single eventfd can be registered at any given point in time. |
| 804 | pub fn register_eventfd_async(self: *IO_Uring, fd: os.fd_t) !void { | 820 | pub fn register_eventfd_async(self: *IO_Uring, fd: os.fd_t) !void { |
| ... | @@ -1279,6 +1295,14 @@ pub fn io_uring_prep_cancel( | ... | @@ -1279,6 +1295,14 @@ pub fn io_uring_prep_cancel( |
| 1279 | sqe.rw_flags = flags; | 1295 | sqe.rw_flags = flags; |
| 1280 | } | 1296 | } |
| 1281 | | 1297 | |
| | 1298 | pub fn io_uring_prep_shutdown( |
| | 1299 | sqe: *io_uring_sqe, |
| | 1300 | sockfd: os.socket_t, |
| | 1301 | how: u32, |
| | 1302 | ) void { |
| | 1303 | io_uring_prep_rw(.SHUTDOWN, sqe, sockfd, 0, how, 0); |
| | 1304 | } |
| | 1305 | |
| 1282 | test "structs/offsets/entries" { | 1306 | test "structs/offsets/entries" { |
| 1283 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 1307 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 1284 | | 1308 | |
| ... | @@ -2191,3 +2215,63 @@ test "register_files_update" { | ... | @@ -2191,3 +2215,63 @@ test "register_files_update" { |
| 2191 | | 2215 | |
| 2192 | try ring.unregister_files(); | 2216 | try ring.unregister_files(); |
| 2193 | } | 2217 | } |
| | 2218 | |
| | 2219 | test "shutdown" { |
| | 2220 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| | 2221 | |
| | 2222 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| | 2223 | error.SystemOutdated => return error.SkipZigTest, |
| | 2224 | error.PermissionDenied => return error.SkipZigTest, |
| | 2225 | else => return err, |
| | 2226 | }; |
| | 2227 | defer ring.deinit(); |
| | 2228 | |
| | 2229 | const address = try net.Address.parseIp4("127.0.0.1", 3131); |
| | 2230 | |
| | 2231 | // Socket bound, expect shutdown to work |
| | 2232 | { |
| | 2233 | const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); |
| | 2234 | defer os.close(server); |
| | 2235 | try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); |
| | 2236 | try os.bind(server, &address.any, address.getOsSockLen()); |
| | 2237 | try os.listen(server, 1); |
| | 2238 | |
| | 2239 | var shutdown_sqe = try ring.shutdown(0x445445445, server, os.linux.SHUT.RD); |
| | 2240 | try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode); |
| | 2241 | try testing.expectEqual(@as(i32, server), shutdown_sqe.fd); |
| | 2242 | |
| | 2243 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 2244 | |
| | 2245 | const cqe = try ring.copy_cqe(); |
| | 2246 | switch (cqe.err()) { |
| | 2247 | .SUCCESS => {}, |
| | 2248 | // This kernel's io_uring does not yet implement shutdown (kernel version < 5.11) |
| | 2249 | .INVAL => return error.SkipZigTest, |
| | 2250 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 2251 | } |
| | 2252 | |
| | 2253 | try testing.expectEqual(linux.io_uring_cqe{ |
| | 2254 | .user_data = 0x445445445, |
| | 2255 | .res = 0, |
| | 2256 | .flags = 0, |
| | 2257 | }, cqe); |
| | 2258 | } |
| | 2259 | |
| | 2260 | // Socket not bound, expect to fail with ENOTCONN |
| | 2261 | { |
| | 2262 | const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); |
| | 2263 | defer os.close(server); |
| | 2264 | |
| | 2265 | var shutdown_sqe = ring.shutdown(0x445445445, server, os.linux.SHUT.RD) catch |err| switch (err) { |
| | 2266 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 2267 | }; |
| | 2268 | try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode); |
| | 2269 | try testing.expectEqual(@as(i32, server), shutdown_sqe.fd); |
| | 2270 | |
| | 2271 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 2272 | |
| | 2273 | const cqe = try ring.copy_cqe(); |
| | 2274 | try testing.expectEqual(@as(u64, 0x445445445), cqe.user_data); |
| | 2275 | try testing.expectEqual(os.linux.E.NOTCONN, cqe.err()); |
| | 2276 | } |
| | 2277 | } |