11require " c/linux/io_uring"
22require " ./syscall"
3- require " ./eventfd"
43
54# WARNING: while the syscalls are thread safe, the rings and the overall
65# abstraction are not: accesses to the SQ and CQ rings aren't synchronized!
@@ -14,6 +13,8 @@ class Crystal::System::IoUring
1413 @@opcodes : Slice (LibC ::IoUringProbeOp )?
1514
1615 class_getter?(supported : Bool ) { check_kernel_support? }
16+ class_getter? supports_register_send_msg_ring : Bool = false
17+ class_getter? supports_register_sync_cancel : Bool = false
1718
1819 def self.check_kernel_support ? : Bool
1920 # try with "no sqarray" flag first (available since linux 6.6)
@@ -32,20 +33,35 @@ class Crystal::System::IoUring
3233 @@no_sqarray = false
3334 end
3435
36+ # record supported features
3537 @@features = params.features
3638
3739 begin
40+ # probe supported opcodes
3841 probe_sz = sizeof(LibC ::IoUringProbe ) + LibC ::IORING_OP_LAST * sizeof(LibC ::IoUringProbeOp )
39- probe = GC .malloc (probe_sz).as(LibC ::IoUringProbe * )
42+ probe = GC .malloc_atomic (probe_sz).as(LibC ::IoUringProbe * )
4043 raise RuntimeError .from_errno(" malloc" ) unless probe
4144 LibIntrinsics .memset(probe, 0 _u8 , probe_sz, false )
4245
4346 ret = Syscall .io_uring_register(fd, LibC ::IORING_REGISTER_PROBE , probe.as(Void * ), LibC ::IORING_OP_LAST )
4447 raise RuntimeError .from_os_error(" io_uring_register" , Errno .new(- ret)) if ret < 0
4548
4649 @@opcodes = Slice (LibC ::IoUringProbeOp ).new(probe.value.ops.to_unsafe, LibC ::IORING_OP_LAST )
50+
51+ # probe supported register opcodes (must test one by one)
52+ sqe = LibC ::IoUringSqe .new
53+ sqe.opcode = LibC ::IORING_OP_MSG_RING
54+ sqe.fd = fd
55+ ret = System ::Syscall .io_uring_register(-1 , LibC ::IORING_REGISTER_SEND_MSG_RING , pointerof (sqe).as(Void * ), 1 )
56+ @@supports_register_send_msg_ring = ret == 0
57+
58+ reg = LibC ::IoUringSyncCancelReg .new
59+ reg.flags = LibC ::IORING_ASYNC_CANCEL_FD
60+ reg.fd = 1
61+ ret = System ::Syscall .io_uring_register(fd, LibC ::IORING_REGISTER_SYNC_CANCEL , pointerof (reg).as(Void * ), 1 )
62+ @@supports_register_sync_cancel = ret != - LibC ::ENOENT
4763 ensure
48- LibC .close(fd) # if fd > 0
64+ LibC .close(fd)
4965 end
5066
5167 true
@@ -55,6 +71,13 @@ class Crystal::System::IoUring
5571 (@@features .not_nil! & feature) == feature
5672 end
5773
74+ def self.supports_register_send_msg_ring ? : Bool
75+ sqe = LibC ::IoUringSqe .new
76+ sqe.opcode = LibC ::IORING_OP_MSG_RING
77+ sqe.fd = waiting_ring.fd
78+ ret = System ::Syscall .io_uring_register(-1 , LibC ::IORING_REGISTER_SEND_MSG_RING , pointerof (sqe).as(Void * ), 1 )
79+ end
80+
5881 def self.supports_opcode ?(opcode : UInt32 ) : Bool
5982 (@@opcodes .not_nil![opcode].flags & LibC ::IO_URING_OP_SUPPORTED ) == LibC ::IO_URING_OP_SUPPORTED
6083 end
@@ -192,7 +215,6 @@ class Crystal::System::IoUring
192215 LibC .munmap(@sqes , @sqes_size )
193216
194217 LibC .close(fd)
195- @eventfd .try(& .close)
196218 end
197219
198220 def sq_poll ? : Bool
@@ -204,17 +226,18 @@ class Crystal::System::IoUring
204226 (sq_flags & LibC ::IORING_SQ_NEED_WAKEUP ) == LibC ::IORING_SQ_NEED_WAKEUP
205227 end
206228
207- # Call `io_uring_register` syscall, and raises on errno .
229+ # Call `io_uring_register` syscall. Raises on error .
208230 def register (opcode : UInt32 , arg : Pointer | Nil = nil , arg_sz = 0 ) : Nil
209- argp = arg ? arg.as( Void * ) : Pointer ( Void ).null
210- err = Syscall .io_uring_register( @fd , opcode, argp, arg_sz.to_u32 )
211- raise RuntimeError .from_os_error( " io_uring_register " , Errno .new( - err)) if err < 0
231+ if errno = register?(opcode, arg, arg_sz)
232+ raise RuntimeError .from_os_error( " io_uring_register " , errno )
233+ end
212234 end
213235
214- # Register an `EventFD`.
215- def register (@eventfd : EventFD ) : Nil
216- efd = eventfd.fd
217- register(LibC ::IORING_REGISTER_EVENTFD , pointerof (efd), 1 )
236+ # Call `io_uring_register` syscall. Returns Errno on error.
237+ def register ?(opcode : UInt32 , arg : Pointer | Nil = nil , arg_sz = 0 ) : Errno ?
238+ argp = arg ? arg.as(Void * ) : Pointer (Void ).null
239+ err = Syscall .io_uring_register(@fd , opcode, argp, arg_sz.to_u32)
240+ Errno .new(- err) if err < 0
218241 end
219242
220243 # Makes sure there is at least *count* SQE available in the SQ ring so we can
0 commit comments