authorgravatar for hnakamur@gmail.comHiroaki Nakamura <hnakamur@gmail.com> 2021-11-02 00:20:55+09:00
committergravatar for hnakamur@gmail.comHiroaki Nakamura <hnakamur@gmail.com> 2021-11-12 10:51:49+09:00
log2a54c2ff199f9f93dc50d9d1d98e32a9bb8423e3
treee7ab04ed37902947eb618130a12a961fd15f61ef
parent08d6876d20303458a4a108300d5a2492e8bd7856

std.os.linux: Add cancel and io_uring_prep_cancel

Signed-off-by: Hiroaki Nakamura <hnakamur@gmail.com>

1 files changed, 120 insertions(+), 0 deletions(-)

lib/std/os/linux/io_uring.zig+120
...@@ -667,6 +667,26 @@ pub const IO_Uring = struct {...@@ -667,6 +667,26 @@ pub const IO_Uring = struct {
667 return sqe;667 return sqe;
668 }668 }
669669
670 /// Queues (but does not submit) an SQE to remove an existing operation.
671 /// Returns a pointer to the SQE.
672 ///
673 /// The operation is identified by its `user_data`.
674 ///
675 /// The completion event result will be `0` if the operation was found and cancelled successfully,
676 /// `-EALREADY` if the operation was found but was already in progress, or
677 /// `-ENOENT` if the operation was not found.
678 pub fn cancel(
679 self: *IO_Uring,
680 user_data: u64,
681 cancel_user_data: u64,
682 flags: u32,
683 ) !*io_uring_sqe {
684 const sqe = try self.get_sqe();
685 io_uring_prep_cancel(sqe, cancel_user_data, flags);
686 sqe.user_data = user_data;
687 return sqe;
688 }
689
670 /// Registers an array of file descriptors.690 /// Registers an array of file descriptors.
671 /// Every time a file descriptor is put in an SQE and submitted to the kernel, the kernel must691 /// Every time a file descriptor is put in an SQE and submitted to the kernel, the kernel must
672 /// retrieve a reference to the file, and once I/O has completed the file reference must be692 /// retrieve a reference to the file, and once I/O has completed the file reference must be
...@@ -1145,6 +1165,28 @@ pub fn io_uring_prep_statx(...@@ -1145,6 +1165,28 @@ pub fn io_uring_prep_statx(
1145 sqe.rw_flags = flags;1165 sqe.rw_flags = flags;
1146}1166}
11471167
1168pub fn io_uring_prep_cancel(
1169 sqe: *io_uring_sqe,
1170 cancel_user_data: u64,
1171 flags: u32,
1172) void {
1173 sqe.* = .{
1174 .opcode = .ASYNC_CANCEL,
1175 .flags = 0,
1176 .ioprio = 0,
1177 .fd = -1,
1178 .off = 0,
1179 .addr = cancel_user_data,
1180 .len = 0,
1181 .rw_flags = flags,
1182 .user_data = 0,
1183 .buf_index = 0,
1184 .personality = 0,
1185 .splice_fd_in = 0,
1186 .__pad2 = [2]u64{ 0, 0 },
1187 };
1188}
1189
1148test "structs/offsets/entries" {1190test "structs/offsets/entries" {
1149 if (builtin.os.tag != .linux) return error.SkipZigTest;1191 if (builtin.os.tag != .linux) return error.SkipZigTest;
11501192
...@@ -1805,3 +1847,81 @@ test "statx" {...@@ -1805,3 +1847,81 @@ test "statx" {
1805 try testing.expect(buf.mask & os.linux.STATX_SIZE == os.linux.STATX_SIZE);1847 try testing.expect(buf.mask & os.linux.STATX_SIZE == os.linux.STATX_SIZE);
1806 try testing.expectEqual(@as(u64, 6), buf.size);1848 try testing.expectEqual(@as(u64, 6), buf.size);
1807}1849}
1850
1851test "accept/connect/recv/cancel" {
1852 if (builtin.os.tag != .linux) return error.SkipZigTest;
1853
1854 var ring = IO_Uring.init(16, 0) catch |err| switch (err) {
1855 error.SystemOutdated => return error.SkipZigTest,
1856 error.PermissionDenied => return error.SkipZigTest,
1857 else => return err,
1858 };
1859 defer ring.deinit();
1860
1861 const address = try net.Address.parseIp4("127.0.0.1", 3131);
1862 const kernel_backlog = 1;
1863 const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);
1864 defer os.close(server);
1865 try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
1866 try os.bind(server, &address.any, address.getOsSockLen());
1867 try os.listen(server, kernel_backlog);
1868
1869 var buffer_recv = [_]u8{ 0, 1, 0, 1, 0 };
1870
1871 var accept_addr: os.sockaddr = undefined;
1872 var accept_addr_len: os.socklen_t = @sizeOf(@TypeOf(accept_addr));
1873 _ = try ring.accept(0xaaaaaaaa, server, &accept_addr, &accept_addr_len, 0);
1874 try testing.expectEqual(@as(u32, 1), try ring.submit());
1875
1876 const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);
1877 defer os.close(client);
1878 _ = try ring.connect(0xcccccccc, client, &address.any, address.getOsSockLen());
1879 try testing.expectEqual(@as(u32, 1), try ring.submit());
1880
1881 var cqe_accept = try ring.copy_cqe();
1882 if (cqe_accept.err() == .INVAL) return error.SkipZigTest;
1883 var cqe_connect = try ring.copy_cqe();
1884 if (cqe_connect.err() == .INVAL) return error.SkipZigTest;
1885
1886 // The accept/connect CQEs may arrive in any order, the connect CQE will sometimes come first:
1887 if (cqe_accept.user_data == 0xcccccccc and cqe_connect.user_data == 0xaaaaaaaa) {
1888 const a = cqe_accept;
1889 const b = cqe_connect;
1890 cqe_accept = b;
1891 cqe_connect = a;
1892 }
1893
1894 try testing.expectEqual(@as(u64, 0xaaaaaaaa), cqe_accept.user_data);
1895 if (cqe_accept.res <= 0) std.debug.print("\ncqe_accept.res={}\n", .{cqe_accept.res});
1896 try testing.expect(cqe_accept.res > 0);
1897 try testing.expectEqual(@as(u32, 0), cqe_accept.flags);
1898 try testing.expectEqual(linux.io_uring_cqe{
1899 .user_data = 0xcccccccc,
1900 .res = 0,
1901 .flags = 0,
1902 }, cqe_connect);
1903
1904 _ = try ring.recv(0xffffffff, cqe_accept.res, buffer_recv[0..], 0);
1905 try testing.expectEqual(@as(u32, 1), try ring.submit());
1906
1907 const sqe_cancel = try ring.cancel(0x99999999, 0xffffffff, 0);
1908 try testing.expectEqual(linux.IORING_OP.ASYNC_CANCEL, sqe_cancel.opcode);
1909 try testing.expectEqual(@as(u64, 0xffffffff), sqe_cancel.addr);
1910 try testing.expectEqual(@as(u64, 0x99999999), sqe_cancel.user_data);
1911 try testing.expectEqual(@as(u32, 1), try ring.submit());
1912
1913 const cqe_recv = try ring.copy_cqe();
1914 if (cqe_recv.err() == .INVAL) return error.SkipZigTest;
1915 try testing.expectEqual(linux.io_uring_cqe{
1916 .user_data = 0xffffffff,
1917 .res = -@as(i32, @enumToInt(linux.E.CANCELED)),
1918 .flags = 0,
1919 }, cqe_recv);
1920
1921 const cqe_cancel = try ring.copy_cqe();
1922 try testing.expectEqual(linux.io_uring_cqe{
1923 .user_data = 0x99999999,
1924 .res = 0,
1925 .flags = 0,
1926 }, cqe_cancel);
1927}