| ... | ... | @@ -731,6 +731,22 @@ pub const IO_Uring = struct { |
| 731 | 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 | 750 | /// Registers an array of file descriptors. |
| 735 | 751 | /// Every time a file descriptor is put in an SQE and submitted to the kernel, the kernel must |
| 736 | 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 | 814 | } |
| 799 | 815 | |
| 800 | 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 | 818 | /// This means that events that complete inline while being submitted do not trigger a notification event. |
| 803 | 819 | /// Only a single eventfd can be registered at any given point in time. |
| 804 | 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 | 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 | 1306 | test "structs/offsets/entries" { |
| 1283 | 1307 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 1284 | 1308 | |
| ... | ... | @@ -2191,3 +2215,63 @@ test "register_files_update" { |
| 2191 | 2215 | |
| 2192 | 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 | } |