| ... | ... | @@ -607,6 +607,34 @@ pub const IO_Uring = struct { |
| 607 | 607 | return sqe; |
| 608 | 608 | } |
| 609 | 609 | |
| 610 | /// Queues (but does not submit) an SQE to add a link timeout operation. |
| 611 | /// Returns a pointer to the SQE. |
| 612 | /// |
| 613 | /// You need to set linux.IOSQE_IO_LINK to flags of the target operation |
| 614 | /// and then call this method right after the target operation. |
| 615 | /// See https://lwn.net/Articles/803932/ for detail. |
| 616 | /// |
| 617 | /// If the dependent request finishes before the linked timeout, the timeout |
| 618 | /// is canceled. If the timeout finishes before the dependent request, the |
| 619 | /// dependent request will be canceled. |
| 620 | /// |
| 621 | /// The completion event result of the link_timeout will be |
| 622 | /// `-ETIME` if the timeout finishes before the dependent request |
| 623 | /// (in this case, the completion event result of the dependent request will |
| 624 | /// be `-ECANCELED`), or |
| 625 | /// `-EALREADY` if the dependent request finishes before the linked timeout. |
| 626 | pub fn link_timeout( |
| 627 | self: *IO_Uring, |
| 628 | user_data: u64, |
| 629 | ts: *const os.linux.kernel_timespec, |
| 630 | flags: u32, |
| 631 | ) !*io_uring_sqe { |
| 632 | const sqe = try self.get_sqe(); |
| 633 | io_uring_prep_link_timeout(sqe, ts, flags); |
| 634 | sqe.user_data = user_data; |
| 635 | return sqe; |
| 636 | } |
| 637 | |
| 610 | 638 | /// Queues (but does not submit) an SQE to perform a `poll(2)`. |
| 611 | 639 | /// Returns a pointer to the SQE. |
| 612 | 640 | pub fn poll_add( |
| ... | ... | @@ -1170,6 +1198,15 @@ pub fn io_uring_prep_timeout_remove(sqe: *io_uring_sqe, timeout_user_data: u64, |
| 1170 | 1198 | }; |
| 1171 | 1199 | } |
| 1172 | 1200 | |
| 1201 | pub fn io_uring_prep_link_timeout( |
| 1202 | sqe: *io_uring_sqe, |
| 1203 | ts: *const os.linux.kernel_timespec, |
| 1204 | flags: u32, |
| 1205 | ) void { |
| 1206 | linux.io_uring_prep_rw(.LINK_TIMEOUT, sqe, -1, @ptrToInt(ts), 1, 0); |
| 1207 | sqe.rw_flags = flags; |
| 1208 | } |
| 1209 | |
| 1173 | 1210 | pub fn io_uring_prep_poll_add( |
| 1174 | 1211 | sqe: *io_uring_sqe, |
| 1175 | 1212 | fd: os.fd_t, |
| ... | ... | @@ -1803,6 +1840,69 @@ test "timeout_remove" { |
| 1803 | 1840 | }, cqe_timeout_remove); |
| 1804 | 1841 | } |
| 1805 | 1842 | |
| 1843 | test "timeout_link_chain1" { |
| 1844 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 1845 | |
| 1846 | var ring = IO_Uring.init(8, 0) catch |err| switch (err) { |
| 1847 | error.SystemOutdated => return error.SkipZigTest, |
| 1848 | error.PermissionDenied => return error.SkipZigTest, |
| 1849 | else => return err, |
| 1850 | }; |
| 1851 | defer ring.deinit(); |
| 1852 | |
| 1853 | var fds = try os.pipe(); |
| 1854 | defer { |
| 1855 | os.close(fds[0]); |
| 1856 | os.close(fds[1]); |
| 1857 | } |
| 1858 | |
| 1859 | var buffer = [_]u8{0} ** 128; |
| 1860 | const iovecs = [_]os.iovec{os.iovec{ .iov_base = &buffer, .iov_len = buffer.len }}; |
| 1861 | const sqe_readv = try ring.readv(0x11111111, fds[0], &iovecs, 0); |
| 1862 | sqe_readv.flags |= linux.IOSQE_IO_LINK; |
| 1863 | |
| 1864 | const ts = os.linux.kernel_timespec{ .tv_sec = 0, .tv_nsec = 1000000 }; |
| 1865 | const seq_link_timeout = try ring.link_timeout(0x22222222, &ts, 0); |
| 1866 | seq_link_timeout.flags |= linux.IOSQE_IO_LINK; |
| 1867 | |
| 1868 | _ = try ring.nop(0x33333333); |
| 1869 | |
| 1870 | const nr_wait = try ring.submit(); |
| 1871 | try testing.expectEqual(@as(u32, 3), nr_wait); |
| 1872 | |
| 1873 | var i: usize = 0; |
| 1874 | while (i < nr_wait) : (i += 1) { |
| 1875 | const cqe = try ring.copy_cqe(); |
| 1876 | switch (cqe.user_data) { |
| 1877 | // poll cancel really should return -ECANCEL... |
| 1878 | 0x11111111 => { |
| 1879 | if (cqe.res != -@as(i32, @enumToInt(linux.E.INTR)) and |
| 1880 | cqe.res != -@as(i32, @enumToInt(linux.E.CANCELED))) |
| 1881 | { |
| 1882 | std.debug.print("Req 0x{x} got {d}\n", .{ cqe.user_data, cqe.res }); |
| 1883 | try testing.expect(false); |
| 1884 | } |
| 1885 | }, |
| 1886 | 0x22222222 => { |
| 1887 | // FASTPOLL kernels can cancel successfully |
| 1888 | if (cqe.res != -@as(i32, @enumToInt(linux.E.ALREADY)) and |
| 1889 | cqe.res != -@as(i32, @enumToInt(linux.E.TIME))) |
| 1890 | { |
| 1891 | std.debug.print("Req 0x{x} got {d}\n", .{ cqe.user_data, cqe.res }); |
| 1892 | try testing.expect(false); |
| 1893 | } |
| 1894 | }, |
| 1895 | 0x33333333 => { |
| 1896 | if (cqe.res != -@as(i32, @enumToInt(linux.E.CANCELED))) { |
| 1897 | std.debug.print("Req 0x{x} got {d}\n", .{ cqe.user_data, cqe.res }); |
| 1898 | try testing.expect(false); |
| 1899 | } |
| 1900 | }, |
| 1901 | else => @panic("should not happen"), |
| 1902 | } |
| 1903 | } |
| 1904 | } |
| 1905 | |
| 1806 | 1906 | test "fallocate" { |
| 1807 | 1907 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 1808 | 1908 | |