1use std::cell::{Cell, UnsafeCell};
2use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
3use std::{cmp, collections::VecDeque, fmt, io, mem, net, ptr, rc::Rc, sync::Arc};
4
5#[cfg(unix)]
6use std::os::unix::net::UnixStream as OsUnixStream;
7
8use ntex_io::Io;
9use ntex_io_uring::cqueue::{self, Entry as CEntry, more};
10use ntex_io_uring::opcode::{AsyncCancel, PollAdd};
11use ntex_io_uring::squeue::{Entry as SEntry, SubmissionQueue};
12use ntex_io_uring::{IoUring, Probe, Submitter, types::CancelBuilder, types::Fd};
13use ntex_rt::{DriverType, Notify, PollResult, Runtime, syscall};
14use ntex_service::cfg::SharedCfg;
15use socket2::{Protocol, SockAddr, Socket, Type};
16
17use super::{TcpStream, UnixStream, stream::StreamOps};
18use crate::channel::Receiver;
19
20pub trait Handler {
21 fn completed(&mut self, id: usize, flags: u32, result: io::Result<usize>);
23
24 fn canceled(&mut self, id: usize);
26
27 fn tick(&mut self);
29
30 fn cleanup(&mut self);
32}
33
34pub struct ReactorApi {
36 batch: u64,
37 inner: Rc<ReactorInner>,
38}
39
40impl ReactorApi {
41 #[inline]
42 pub fn is_new(&self) -> bool {
44 self.inner.flags.get().contains(Flags::NEW)
45 }
46
47 fn submit_inner<F>(&self, f: F)
48 where
49 F: FnOnce(&mut SEntry),
50 {
51 unsafe {
52 let changes = &mut *self.inner.changes.get();
53 let f = if changes.is_empty() {
55 match self.inner.ring.submission_unsynced().try_push_inline(f) {
56 Ok(()) => return,
57 Err(f) => f,
58 }
59 } else {
60 f
61 };
62 changes.push_back(mem::MaybeUninit::uninit());
63 let entry = changes.back_mut().unwrap();
64 ptr::write_bytes(entry.as_mut_ptr(), 0, 1);
65 f(entry.assume_init_mut());
66 }
67 }
68
69 #[inline]
70 pub fn submit(&self, id: u32, entry: SEntry) {
72 self.submit_inner(|en| {
73 *en = entry;
74 en.set_user_data(u64::from(id) | self.batch);
75 });
76 }
77
78 #[inline]
79 pub fn submit_inline<F>(&self, id: u32, f: F)
81 where
82 F: FnOnce(&mut SEntry),
83 {
84 self.submit_inner(|en| {
85 f(en);
86 en.set_user_data(u64::from(id) | self.batch);
87 });
88 }
89
90 #[inline]
91 pub fn cancel(&self, id: u32) {
93 self.submit_inner(|en| {
94 *en = AsyncCancel::new(u64::from(id) | self.batch)
95 .build()
96 .user_data(Reactor::CANCEL);
97 });
98 }
99
100 #[inline]
101 pub fn cancel_all_sync(&self, fd: Fd) -> io::Result<()> {
103 self.inner
104 .ring
105 .submitter()
106 .register_sync_cancel(None, CancelBuilder::fd(fd).all())
107 }
108
109 pub fn is_supported(&self, opcode: u8) -> bool {
111 self.inner.probe.is_supported(opcode)
112 }
113}
114
115pub struct Reactor {
117 fd: RawFd,
118 hid: Cell<u64>,
119 notifier: Notifier,
120 #[allow(clippy::box_collection)]
121 handlers: Cell<Option<Box<Vec<HandlerItem>>>>,
122 inner: Rc<ReactorInner>,
123}
124
125struct HandlerItem {
126 hnd: Box<dyn Handler>,
127 modified: bool,
128}
129
130impl HandlerItem {
131 fn tick(&mut self) {
132 if self.modified {
133 self.modified = false;
134 self.hnd.tick();
135 }
136 }
137}
138
139bitflags::bitflags! {
140 #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
141 struct Flags: u8 {
142 const NEW = 0b0000_0001;
143 const NOTIFIER = 0b0000_0010;
144 }
145}
146
147struct ReactorInner {
148 probe: Probe,
149 flags: Cell<Flags>,
150 ring: IoUring<SEntry, CEntry>,
151 changes: UnsafeCell<VecDeque<mem::MaybeUninit<SEntry>>>,
152}
153
154impl Reactor {
155 const NOTIFY: u64 = u64::MAX;
156 const CANCEL: u64 = u64::MAX - 1;
157 const BATCH: u64 = 48;
158 const BATCH_MASK: u64 = 0xFFFF_0000_0000_0000;
159 const DATA_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
160
161 pub fn new(capacity: u32) -> io::Result<Self> {
163 let (new, ring) = if let Ok(ring) = IoUring::builder()
165 .setup_coop_taskrun()
166 .setup_taskrun_flag()
167 .setup_single_issuer()
168 .setup_defer_taskrun()
169 .build(capacity)
170 {
171 log::info!(
172 "New io-uring driver with single-issuer, coop-taskrun, taskrun-flag, defer-taskrun"
173 );
174 (true, ring)
175 } else if let Ok(ring) = IoUring::builder().setup_single_issuer().build(capacity) {
176 log::info!("New io-uring driver with single-issuer");
177 (true, ring)
178 } else {
179 let ring = IoUring::builder().build(capacity)?;
180 log::info!("New io-uring driver");
181 (false, ring)
182 };
183
184 let mut probe = Probe::new();
185 ring.submitter().register_probe(&mut probe)?;
186
187 let notifier = Notifier::new()?;
189 unsafe {
190 let sq = ring.submission();
191 sq.push(
192 &PollAdd::new(Fd(notifier.as_raw_fd()), libc::POLLIN as _)
193 .multi(true)
194 .build()
195 .user_data(Self::NOTIFY),
196 )
197 .expect("the squeue sould not be full");
198 sq.sync();
199 }
200
201 let fd = ring.as_raw_fd();
202 let inner = Rc::new(ReactorInner {
203 ring,
204 probe,
205 flags: Cell::new(if new { Flags::NEW } else { Flags::empty() }),
206 changes: UnsafeCell::new(VecDeque::with_capacity(32)),
207 });
208
209 Ok(Self {
210 fd,
211 inner,
212 notifier,
213 hid: Cell::new(0),
214 handlers: Cell::new(Some(Box::new(Vec::new()))),
215 })
216 }
217
218 pub const fn tp(&self) -> DriverType {
220 DriverType::IoUring
221 }
222
223 pub fn register<F>(&self, f: F)
225 where
226 F: FnOnce(ReactorApi) -> Box<dyn Handler>,
227 {
228 let id = self.hid.get();
229 let mut handlers = self.handlers.take().unwrap_or_default();
230 handlers.push(HandlerItem {
231 hnd: f(ReactorApi {
232 batch: id << Self::BATCH,
233 inner: self.inner.clone(),
234 }),
235 modified: false,
236 });
237 self.handlers.set(Some(handlers));
238 self.hid.set(id + 1);
239 }
240
241 fn apply_changes(&self, sq: SubmissionQueue<'_, SEntry>) -> bool {
242 unsafe {
243 let changes = &mut *self.inner.changes.get();
244 if changes.is_empty() {
245 false
246 } else {
247 let num = cmp::min(changes.len(), sq.capacity() - sq.len());
248 let (s1, s2) = changes.as_slices();
249 let s1_num = cmp::min(s1.len(), num);
250 let s2_num = cmp::min(s2.len(), num - s1_num);
251 if s1_num > 0 {
253 sq.push_multiple(
254 ((&raw const s1[0..s1_num]) as *const [SEntry])
255 .as_ref()
256 .unwrap(),
257 )
258 .unwrap();
259 }
260 if s2_num > 0 {
262 sq.push_multiple(
263 ((&raw const s2[0..s2_num]) as *const [SEntry])
264 .as_ref()
265 .unwrap(),
266 )
267 .unwrap();
268 }
269 changes.drain(0..num);
270
271 !changes.is_empty()
272 }
273 }
274 }
275
276 fn flush(&self, sq: SubmissionQueue<'_, SEntry>, submitter: &Submitter<'_>) {
278 let mut retries = 0;
279 loop {
280 let more_changes = self.apply_changes(sq);
281 sq.sync();
282
283 if let Err(e) = submitter.submit() {
284 match e.raw_os_error() {
285 Some(libc::ETIME | libc::EBUSY | libc::EAGAIN | libc::EINTR)
286 if retries < 16 =>
287 {
288 retries += 1;
289 continue;
290 }
291 _ => {
292 log::error!("Cannot submit pending operations: {e:?}");
293 break;
294 }
295 }
296 }
297 if !more_changes {
298 break;
299 }
300 }
301 }
302
303 fn poll_completions(
305 &self,
306 cq: &mut cqueue::CompletionQueue<'_, CEntry>,
307 sq: SubmissionQueue<'_, SEntry>,
308 ) {
309 cq.sync();
310
311 if !cqueue::CompletionQueue::<'_, _>::is_empty(cq) {
312 let mut handlers = self.handlers.take().unwrap();
313 for entry in &mut *cq {
314 let user_data = entry.user_data();
315 match user_data {
316 Self::CANCEL => {}
317 Self::NOTIFY => {
318 let flags = entry.flags();
319 self.notifier.clear().expect("cannot clear notifier");
320
321 if !more(flags) {
323 unsafe {
324 sq.push(
325 &PollAdd::new(Fd(self.notifier.as_raw_fd()), libc::POLLIN as _)
326 .multi(true)
327 .build()
328 .user_data(Self::NOTIFY),
329 )
330 }
331 .expect("the squeue sould not be full");
332 }
333 }
334 _ => {
335 let batch = ((user_data & Self::BATCH_MASK) >> Self::BATCH) as usize;
336 let user_data = (user_data & Self::DATA_MASK) as usize;
337
338 let result = entry.result();
339 if result == -libc::ECANCELED && !more(entry.flags()) {
342 handlers[batch].modified = true;
343 handlers[batch].hnd.canceled(user_data);
344 } else {
345 let result = if result < 0 && !cqueue::notif(entry.flags()) {
347 Err(io::Error::from_raw_os_error(-result))
348 } else {
349 #[allow(clippy::cast_sign_loss)]
350 Ok(result as u32 as _)
351 };
352 handlers[batch].modified = true;
353 handlers[batch]
354 .hnd
355 .completed(user_data, entry.flags(), result);
356 }
357 }
358 }
359 }
360 cq.sync();
363
364 for h in handlers.iter_mut() {
365 h.tick();
366 }
367 self.handlers.set(Some(handlers));
368 }
369 }
370}
371
372impl AsRawFd for Reactor {
373 fn as_raw_fd(&self) -> RawFd {
374 self.fd
375 }
376}
377
378impl crate::Reactor for Reactor {
379 fn tcp_connect(&self, addr: net::SocketAddr, cfg: SharedCfg) -> Receiver<Io> {
380 let addr = SockAddr::from(addr);
381 let result = Socket::new(addr.domain(), Type::STREAM, Some(Protocol::TCP))
382 .and_then(crate::helpers::prep_tcp_socket)
383 .and_then(crate::helpers::prep_socket)
384 .map(move |sock| (addr, sock));
385
386 match result {
387 Err(err) => Receiver::new(Err(err)),
388 Ok((addr, sock)) => super::connect::ConnectOps::get(self).connect(sock, addr, cfg),
389 }
390 }
391
392 fn unix_connect(&self, addr: std::path::PathBuf, cfg: SharedCfg) -> Receiver<Io> {
393 let result = SockAddr::unix(addr).and_then(|addr| {
394 Socket::new(addr.domain(), Type::STREAM, None)
395 .and_then(crate::helpers::prep_socket)
396 .map(move |sock| (addr, sock))
397 });
398
399 match result {
400 Err(err) => Receiver::new(Err(err)),
401 Ok((addr, sock)) => super::connect::ConnectOps::get(self).connect(sock, addr, cfg),
402 }
403 }
404
405 fn from_tcp_stream(&self, stream: net::TcpStream, cfg: SharedCfg) -> io::Result<Io> {
406 stream.set_nodelay(true)?;
407
408 Ok(Io::new(
409 TcpStream(
410 crate::helpers::prep_socket(Socket::from(stream))?,
411 StreamOps::get(self),
412 ),
413 cfg,
414 ))
415 }
416
417 #[cfg(unix)]
418 fn from_unix_stream(&self, stream: OsUnixStream, cfg: SharedCfg) -> io::Result<Io> {
419 Ok(Io::new(
420 UnixStream(
421 crate::helpers::prep_socket(Socket::from(stream))?,
422 StreamOps::get(self),
423 ),
424 cfg,
425 ))
426 }
427}
428
429impl ntex_rt::Driver for Reactor {
430 fn run(&self, rt: &Runtime) -> io::Result<()> {
432 let ring = &self.inner.ring;
433 let sq = ring.submission();
434 let mut cq = unsafe { ring.completion_shared() };
435 let submitter = ring.submitter();
436 let result = loop {
437 rt.awake();
439 self.poll_completions(&mut cq, sq);
440
441 let more_tasks = match rt.poll() {
442 PollResult::Pending => false,
443 PollResult::PollAgain => true,
444 PollResult::Ready => break Ok(()),
445 };
446 let more_changes = self.apply_changes(sq);
447
448 sq.sync();
451
452 let result = if more_changes || more_tasks {
453 if more_changes || !sq.is_empty() || sq.taskrun() {
456 submitter.submit()
457 } else {
458 Ok(0)
459 }
460 } else {
461 submitter.submit_and_wait(1)
462 };
463
464 if let Err(e) = result {
465 match e.raw_os_error() {
466 Some(libc::ETIME | libc::EBUSY | libc::EAGAIN | libc::EINTR) => {
467 log::info!("Ring submit interrupted, {e:?}");
468 }
469 _ => break Err(e),
470 }
471 }
472 };
473
474 if result.is_ok() {
476 self.flush(sq, &submitter);
479
480 if let Err(e) = submitter.register_sync_cancel(None, CancelBuilder::any()) {
483 log::error!("Cannot cancel in-flight operations: {e:?}");
484 }
485
486 for mut h in self.handlers.take().unwrap().into_iter() {
487 h.hnd.cleanup();
488 }
489 }
490
491 result
492 }
493
494 fn handle(&self) -> Box<dyn Notify> {
496 Box::new(self.notifier.handle())
497 }
498}
499
500#[derive(Debug)]
501pub(crate) struct Notifier {
502 fd: Arc<OwnedFd>,
503}
504
505impl Notifier {
506 pub(crate) fn new() -> io::Result<Self> {
508 let fd = syscall!(libc::eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK))?;
509 let fd = unsafe { OwnedFd::from_raw_fd(fd) };
510 Ok(Self { fd: Arc::new(fd) })
511 }
512
513 pub(crate) fn clear(&self) -> io::Result<()> {
514 loop {
515 let mut buffer = [0u64];
516 let res = syscall!(libc::read(
517 self.fd.as_raw_fd(),
518 buffer.as_mut_ptr().cast(),
519 mem::size_of::<u64>()
520 ));
521 #[allow(clippy::cast_possible_wrap)]
522 match res {
523 Ok(len) => {
524 debug_assert_eq!(len, mem::size_of::<u64>() as isize);
525 break Ok(());
526 }
527 Err(e) if e.kind() == io::ErrorKind::WouldBlock => break Ok(()),
529 Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
531 Err(e) => break Err(e),
532 }
533 }
534 }
535
536 pub(crate) fn handle(&self) -> NotifyHandle {
537 NotifyHandle::new(self.fd.clone())
538 }
539}
540
541impl AsRawFd for Notifier {
542 fn as_raw_fd(&self) -> RawFd {
543 self.fd.as_raw_fd()
544 }
545}
546
547#[derive(Clone, Debug)]
548pub(crate) struct NotifyHandle {
550 fd: Arc<OwnedFd>,
551}
552
553impl NotifyHandle {
554 pub(crate) fn new(fd: Arc<OwnedFd>) -> Self {
555 Self { fd }
556 }
557}
558
559impl Notify for NotifyHandle {
560 fn notify(&self) -> io::Result<()> {
562 let data = 1u64;
563 syscall!(libc::write(
564 self.fd.as_raw_fd(),
565 (&raw const data).cast(),
566 std::mem::size_of::<u64>(),
567 ))?;
568 Ok(())
569 }
570}
571
572impl fmt::Debug for Reactor {
573 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574 f.debug_struct("Reactor")
575 .field("fd", &self.fd)
576 .field("hid", &self.hid)
577 .field("nodifier", &self.notifier)
578 .finish()
579 }
580}
581
582impl fmt::Debug for ReactorApi {
583 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
584 f.debug_struct("ReactorApi")
585 .field("batch", &self.batch)
586 .finish()
587 }
588}
589
590#[cfg(test)]
591mod tests {
592 use ntex_io_uring::opcode::Nop;
593
594 use super::*;
595
596 #[test]
597 fn apply_wrapped_changes() {
598 let Ok(reactor) = Reactor::new(4) else {
599 return;
600 };
601 let ring = &reactor.inner.ring;
602 let sq = ring.submission();
603 ring.submitter().submit().unwrap();
605 sq.sync();
606 assert_eq!(sq.len(), 0);
607
608 let changes = unsafe { &mut *reactor.inner.changes.get() };
610 let cap = changes.capacity();
611 for _ in 0..cap - 2 {
612 changes.push_back(mem::MaybeUninit::new(Nop::new().build()));
613 }
614 for _ in 0..cap - 2 {
615 changes.pop_front();
616 }
617 for i in 0..4 {
618 changes.push_back(mem::MaybeUninit::new(Nop::new().build().user_data(i)));
619 }
620 assert_eq!(changes.as_slices().0.len(), 2);
621
622 assert!(!reactor.apply_changes(sq));
623 sq.sync();
624 assert!(changes.is_empty());
625 assert_eq!(sq.len(), 4);
626
627 ring.submitter().submit_and_wait(4).unwrap();
628 let mut cq = unsafe { ring.completion_shared() };
629 cq.sync();
630 let ids: Vec<_> = cq.map(|e| e.user_data()).collect();
631 assert_eq!(ids, [0, 1, 2, 3]);
632 }
633}