Skip to content

Commit

Permalink
queue: improved io_uring_cqe, now user_data=0 not supported.
Browse files Browse the repository at this point in the history
Its hard to know how many `cqe` are filled, so using `user_data` to
determine that. Updated effected modules/tests.
  • Loading branch information
YoSTEALTH committed Apr 22, 2024
1 parent eea7ca6 commit dd35fe7
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 32 deletions.
2 changes: 1 addition & 1 deletion libs/liburing
2 changes: 1 addition & 1 deletion src/liburing/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from dynamic_import import importer


__version__ = '2024.4.20'
__version__ = '2024.4.22'


importer(exclude_dir=['lib', 'include'])
Expand Down
12 changes: 8 additions & 4 deletions src/liburing/queue.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ cdef class io_uring:

cdef class io_uring_sqe:
cdef:
__io_uring_sqe* ptr
__u16 len
list ref
__io_uring_sqe* ptr
__u16 len
list[io_uring_sqe] ref


cdef class io_uring_cqe:
cdef __io_uring_cqe * ptr
cdef:
__io_uring_cqe* ptr
bint active

cdef tuple[__s32, __u64] get_index(self, unsigned int index) noexcept nogil


cdef class io_uring_params:
Expand Down
42 changes: 23 additions & 19 deletions src/liburing/queue.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ cdef class io_uring_sqe:

Note
- `io_uring_sqe` is not the same as `io_uring_get_sqe()`.
- This class has multiple uses:
- This class has dual usage:
1. It works as a base class for `io_uring_get_sqe()` return.
2. It can also be used as `sqe = io_uring_sqe(<int>)`, rather than "get" sqe(s)
you are going to "put" pre-made sqe(s) into the ring later.
- Refer to `help(io_uring_put_sqe)` to see more detail.
you are going to "put" pre-made sqe(s) into the ring later. Refer to
`help(io_uring_put_sqe)` to see more detail.
'''
def __cinit__(self, __u16 num=1):
cdef str msg
Expand Down Expand Up @@ -90,22 +90,20 @@ cdef class io_uring_sqe:
def __len__(self):
return self.len
def __getitem__(self, unsigned int index):
cdef io_uring_sqe sqe
if self.ptr is not NULL:
if index == 0:
return self
elif self.len and index < self.len:
if (sqe := self.ref[index-1]) is not None:
return sqe # from reference cache
return sqe # return from reference cache
# create new reference class
sqe = io_uring_sqe(0) # `0` is set to indicated `ptr` is being set for internal use
sqe = io_uring_sqe(0) # `0` is set to indicated `ptr` memory is not managed
sqe.ptr = &self.ptr[index]
if sqe.ptr is not NULL:
# cache sqe as this class attribute
self.ref[index-1] = sqe
return sqe
self.ref[index-1] = sqe # cache sqe as this class attribute
return sqe
index_error(self, index, 'out of `sqe`')
@property
Expand All @@ -122,7 +120,12 @@ cdef class io_uring_sqe:
@user_data.setter
def user_data(self, __u64 data):
__io_uring_sqe_set_data64(self.ptr, data)
cdef str msg
if data != 0:
__io_uring_sqe_set_data64(self.ptr, data)
else:
msg = f'`{self.__class__.__name__}.user_data` can not be {data!r}'
raise ValueError(msg)
cdef class io_uring_cqe:
Expand Down Expand Up @@ -164,37 +167,38 @@ cdef class io_uring_cqe:
if self.ptr is not NULL:
if index == 0:
return self
cqe = io_uring_cqe()
cqe.ptr = &self.ptr[index]
if cqe.ptr is not NULL:
elif self.ptr[index].user_data != 0:
cqe = io_uring_cqe()
cqe.ptr = &self.ptr[index]
cqe.active = True
return cqe
index_error(self, index, 'out of `cqe`')
# note: no need to cache items since `cqe` is normally called once or passed around.
def __bool__(self):
return self.ptr is not NULL
return True if self.active else self.ptr is not NULL
def __repr__(self):
if self.ptr is not NULL:
if self.active or self.ptr is not NULL:
return f'{self.__class__.__name__}(user_data={self.ptr.user_data!r}, ' \
f'res={self.ptr.res!r}, flags={self.ptr.flags!r})'
memory_error(self, 'out of `cqe`')
@property
def user_data(self) -> __u64:
if self.ptr is not NULL:
if self.active or self.ptr is not NULL:
return self.ptr.user_data
memory_error(self, 'out of `cqe`')
@property
def res(self) -> __s32:
if self.ptr is not NULL:
if self.active or self.ptr is not NULL:
return self.ptr.res
memory_error(self, 'out of `cqe`')
@property
def flags(self) -> __u32:
if self.ptr is not NULL:
if self.active or self.ptr is not NULL:
return self.ptr.flags
memory_error(self, 'out of `cqe`')
Expand Down
4 changes: 2 additions & 2 deletions test/helper/io_uring_put_sqe_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ def _loop(entries, num):
sqe = io_uring_sqe(num)
for i in range(num):
io_uring_prep_nop(sqe[i])
sqe[i].user_data = i
sqe[i].user_data = i+1 # user_data can not be `0`
if io_uring_put_sqe(ring, sqe):
io_uring_submit(ring)
else:
return False
if num:
assert io_uring_wait_cqes(ring, cqe, num) == 0
for i in range(num):
assert cqe[i].user_data == i
assert cqe[i].user_data == i+1
io_uring_cq_advance(ring, num)
return True
finally:
Expand Down
6 changes: 3 additions & 3 deletions test/queue/init_exit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@ def test_init_max():
sqe = liburing.io_uring_get_sqe(ring)
liburing.io_uring_prep_nop(sqe)
sqe.flags = liburing.IOSQE_IO_LINK | liburing.IOSQE_ASYNC
sqe.user_data = i
sqe.user_data = i+1 # `sqe.user_data` can not be `0`
else: # last
sqe = liburing.io_uring_get_sqe(ring)
liburing.io_uring_prep_nop(sqe)
sqe.flags = liburing.IOSQE_ASYNC
sqe.user_data = i+1
sqe.user_data = i+1+1

assert liburing.io_uring_submit_and_wait_timeout(ring, cqe, maximum) == maximum

for i in range(maximum):
assert cqe[i].user_data == i
assert cqe[i].user_data == i+1

liburing.io_uring_cq_advance(ring, maximum)
assert liburing.io_uring_peek_cqe(ring, cqe) == -errno.EAGAIN
Expand Down
28 changes: 26 additions & 2 deletions test/queue/sqe_cqe_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,33 @@ def test_io_uring_sqe():
liburing.io_uring_sqe(None)


def test_io_uring_cqe():
cqe = liburing.io_uring_cqe()
def test_io_uring_cqe(ring, cqe):
with pytest.raises(IndexError, match=re.escape('`io_uring_cqe()[0]` out of `cqe`')):
cqe[0]
with pytest.raises(IndexError, match=re.escape('`io_uring_cqe()[1]` out of `cqe`')):
cqe[1]
with pytest.raises(MemoryError, match=re.escape('`io_uring_cqe()` out of `cqe`')):
cqe.user_data
# with pytest.raises(MemoryError, match=re.escape('`io_uring_cqe()` out of `cqe`')):
assert not (True if cqe else False)

sqe = liburing.io_uring_get_sqe(ring)
liburing.io_uring_prep_nop(sqe)
sqe.user_data = 1

sqe = liburing.io_uring_get_sqe(ring)
liburing.io_uring_prep_nop(sqe)
sqe.user_data = 2

assert liburing.io_uring_submit_and_wait_timeout(ring, cqe, 2) == 2

assert True if cqe else False
assert cqe.user_data == 1
assert True if cqe[1] else False
assert cqe[1].user_data == 2
with pytest.raises(IndexError, match=re.escape('`io_uring_cqe()[2]` out of `cqe`')):
assert cqe[2]


def test_cqe_get_index():
liburing.test_cqe_get_index()

0 comments on commit dd35fe7

Please sign in to comment.