From aa15c1d9f840685bc66229435e413549c3162e5c Mon Sep 17 00:00:00 2001 From: meshya Date: Wed, 22 Jul 2026 15:36:04 +0330 Subject: [PATCH 1/4] [fix] add `Param.__new__/__del__` --- src/liburing/class.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/liburing/class.zig b/src/liburing/class.zig index fbf7398..8162b40 100644 --- a/src/liburing/class.zig +++ b/src/liburing/class.zig @@ -421,6 +421,17 @@ pub const Param = extern struct { const Self = @This(); + pub fn __new__(flags: ?u32) !Self { + const p: *c.io_uring_params = try std.heap.c_allocator.create(c.io_uring_params); + p.* = std.mem.zeroes(c.io_uring_params); + p.flags = flags orelse 0; + return .{ ._io_uring_params = p }; + } + + pub fn __del__(self: *const Self) void { + if (self._io_uring_params) |p| std.heap.c_allocator.destroy(p); + } + pub fn get_sq_entries(self: *const Self) ?u32 { if (self._io_uring_params) |p| return p.sq_entries; return oz.raiseRuntimeError("`Param()` not initialized properly!"); From e6c406903a0733d488564621f5f838a2391e9c99 Mon Sep 17 00:00:00 2001 From: meshya Date: Wed, 22 Jul 2026 15:38:00 +0330 Subject: [PATCH 2/4] [refactor] change `io_uring_queue_init_mem` behavior `queue_init_mem` needs a page-aligned & zeroed buffer as it explained in man pages. refactor `queue_init_mem` how it accepts python's std `mmap.mmap` as buf. --- src/liburing/__init__.py | 3 ++- src/liburing/root.zig | 5 +++++ src/liburing/uring.py | 29 +++++++++++++++++++++++++++++ src/liburing/uring.zig | 11 +++++++---- 4 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 src/liburing/uring.py diff --git a/src/liburing/__init__.py b/src/liburing/__init__.py index c0a24c7..e3efc83 100644 --- a/src/liburing/__init__.py +++ b/src/liburing/__init__.py @@ -1,5 +1,6 @@ from .liburing import * # noqa from .version import * # noqa - +from .uring import * # noqa __version__ = "2026.3.30" + diff --git a/src/liburing/root.zig b/src/liburing/root.zig index aead61f..95de87f 100644 --- a/src/liburing/root.zig +++ b/src/liburing/root.zig @@ -3,6 +3,11 @@ const oz = @import("PyOZ"); pub const Liburing = oz.module(.{ .name = "liburing", + + // `uring.py` wraps it. + .funcs = &.{ + oz.func("_io_uring_queue_init_mem", @import("uring.zig")._io_uring_queue_init_mem, null), + }, .from = &.{ oz.withSource(@import("const.zig"), @embedFile("const.zig")), oz.withSource(@import("class.zig"), @embedFile("class.zig")), diff --git a/src/liburing/uring.py b/src/liburing/uring.py new file mode 100644 index 0000000..e6f75ae --- /dev/null +++ b/src/liburing/uring.py @@ -0,0 +1,29 @@ +''' +Liburing + +Python layer on top of PYoZ built functions +''' + +from .liburing import _io_uring_queue_init_mem + +def io_uring_queue_init_mem(entries, ring, param, buf): + """Setup `Ring` using caller supplied memory. Returns bytes used from `buf`. + + Example + >>> import mmap + >>> ring = Ring() + >>> param = Param() + >>> buf = mmap.mmap(-1, 1024**2 * 2) # page-aligned & zeroed + >>> io_uring_queue_init_mem(8, ring, param, buf) + + Note + - `buf` must be page-size aligned and zeroed; `mmap.mmap(-1, size)` satisfies both. + A `bytearray` does not (kernel rejects it with `EINVAL`). + - Hold a reference to `buf` until `io_uring_queue_exit`, else the memory gets + freed while the ring still uses it. + """ + return _io_uring_queue_init_mem(entries, ring, param, memoryview(buf)) + +__all__ = [ + "io_uring_queue_init_mem" +] \ No newline at end of file diff --git a/src/liburing/uring.zig b/src/liburing/uring.zig index 7d2f7e6..0894936 100644 --- a/src/liburing/uring.zig +++ b/src/liburing/uring.zig @@ -69,10 +69,13 @@ pub fn io_uring_opcode_supported(probe: *Probe, op: i32) bool { return (c.io_uring_opcode_supported(probe._io_uring_probe, op) == 1); } -///Warning -/// - Coded but not tested!!! -pub fn io_uring_queue_init_mem(entries: u32, ring: *Ring, p: *Param, buf: ?*anyopaque, buf_size: usize) ?i32 { - return e.trap_error(c.io_uring_queue_init_mem(entries, ring._io_uring, p._io_uring_params, buf, buf_size)); +///Internal - wrapped by Python `io_uring_queue_init_mem` (see `uring.py`) which accepts +///the buffer directly and passes a `memoryview` here. Returns bytes used from `buf`. +pub fn _io_uring_queue_init_mem(entries: u32, ring: *Ring, p: *Param, buf: oz.MemoryView) ?i32 { + if (ring._io_uring.ring_fd > 0) return oz.raiseRuntimeError("`io_uring_queue_init_mem(ring)` already initialized!"); + const params = p._io_uring_params orelse + return oz.raiseValueError("`io_uring_queue_init_mem` - `Param` not initialized!!"); + return e.trap_error(c.io_uring_queue_init_mem(entries, ring._io_uring, params, @ptrCast(@constCast(buf.data.ptr)), buf.data.len)); } pub fn io_uring_queue_init_params(entries: u32, ring: *Ring, param: *Param) ?i32 { From e5a38eccb66f7a38521df8365f2f6780dca42ce6 Mon Sep 17 00:00:00 2001 From: meshya Date: Wed, 22 Jul 2026 15:42:30 +0330 Subject: [PATCH 3/4] [test] add test for `io_uring_queue_init_mem` --- test/queue/init_exit_test.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/queue/init_exit_test.py b/test/queue/init_exit_test.py index 6a64eb2..8416b67 100644 --- a/test/queue/init_exit_test.py +++ b/test/queue/init_exit_test.py @@ -1,3 +1,4 @@ +import mmap import pytest import liburing @@ -83,3 +84,24 @@ def test_io_uring_submit_and_wait(ring, cqe): assert liburing.io_uring_sq_ready(ring) == 0 liburing.io_uring_peek_cqe(ring, cqe) assert cqe[0].user_data == 123 + + +def test_io_uring_init_mem(): + ring = liburing.Ring() + param = liburing.Param() + buf = mmap.mmap(-1, 2 * 1024 * 1024) # page-aligned & zeroed; keep alive until exit + used = liburing.io_uring_queue_init_mem(8, ring, param, buf) + + try: + assert used > 0 + + sqe = liburing.io_uring_get_sqe(ring) + liburing.io_uring_prep_nop(sqe) + sqe.user_data = 7 + assert liburing.io_uring_submit(ring) == 1 + cqe = liburing.Cqe() + liburing.io_uring_wait_cqe(ring, cqe) + assert cqe[0].user_data == 7 + liburing.io_uring_cqe_seen(ring, cqe[0]) + finally: + liburing.io_uring_queue_exit(ring) \ No newline at end of file From 4aec5b39e5709b7362073d741e638c6874e25b53 Mon Sep 17 00:00:00 2001 From: meshya Date: Wed, 22 Jul 2026 15:43:27 +0330 Subject: [PATCH 4/4] [test] test `io_uring_queue_init_mem` initialized queue in all test cases. --- test/conftest.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 18046a2..989c3be 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -5,6 +5,7 @@ import getpass import tempfile import liburing +import mmap @pytest.fixture @@ -34,11 +35,24 @@ def tmp_dir(): # liburing start >>> -@pytest.fixture -def ring(): +@pytest.fixture(params=['queue_init', 'queue_init_mem']) +def ring(request): + ''' + ======================= + params: + queue_init: + Init queue using `io_uring_queue_init` + queue_init_mem: + Init queue using `io_uring_queue_init_mem` + ''' ring = liburing.Ring() try: - liburing.io_uring_queue_init(1024, ring) + if request.param == 'queue_init': + liburing.io_uring_queue_init(1024, ring) + else: + buf = mmap.mmap(-1, 8 * 1024 * 1024) + param = liburing.Param() + liburing.io_uring_queue_init_mem(1024, ring, param, buf) yield ring finally: liburing.io_uring_queue_exit(ring)