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, 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 DriverApi {
35 batch: u64,
36 inner: Rc<DriverInner>,
37}
38
39impl DriverApi {
40 #[inline]
41 pub fn is_new(&self) -> bool {
43 self.inner.flags.get().contains(Flags::NEW)
44 }
45
46 fn submit_inner<F>(&self, f: F)
47 where
48 F: FnOnce(&mut SEntry),
49 {
50 unsafe {
51 let changes = &mut *self.inner.changes.get();
52 let sq = self.inner.ring.submission();
53 if !changes.is_empty() || sq.is_full() {
54 changes.push_back(mem::MaybeUninit::uninit());
55 let entry = changes.back_mut().unwrap();
56 ptr::write_bytes(entry.as_mut_ptr(), 0, 1);
57 f(entry.assume_init_mut());
58 } else {
59 sq.push_inline(f).expect("Queue size is checked");
60 }
61 }
62 }
63
64 #[inline]
65 pub fn submit(&self, id: u32, entry: SEntry) {
67 self.submit_inner(|en| {
68 *en = entry;
69 en.set_user_data(u64::from(id) | self.batch);
70 });
71 }
72
73 #[inline]
74 pub fn submit_inline<F>(&self, id: u32, f: F)
76 where
77 F: FnOnce(&mut SEntry),
78 {
79 self.submit_inner(|en| {
80 f(en);
81 en.set_user_data(u64::from(id) | self.batch);
82 });
83 }
84
85 #[inline]
86 pub fn cancel(&self, id: u32) {
88 self.submit_inner(|en| {
89 *en = AsyncCancel::new(u64::from(id) | self.batch)
90 .build()
91 .user_data(Driver::CANCEL);
92 });
93 }
94
95 #[inline]
96 pub fn cancel_all_sync(&self, fd: Fd) -> io::Result<()> {
98 self.inner
99 .ring
100 .submitter()
101 .register_sync_cancel(None, CancelBuilder::fd(fd).all())
102 }
103
104 pub fn is_supported(&self, opcode: u8) -> bool {
106 self.inner.probe.is_supported(opcode)
107 }
108}
109
110pub struct Driver {
112 fd: RawFd,
113 hid: Cell<u64>,
114 notifier: Notifier,
115 #[allow(clippy::box_collection)]
116 handlers: Cell<Option<Box<Vec<HandlerItem>>>>,
117 inner: Rc<DriverInner>,
118}
119
120struct HandlerItem {
121 hnd: Box<dyn Handler>,
122 modified: bool,
123}
124
125impl HandlerItem {
126 fn tick(&mut self) {
127 if self.modified {
128 self.modified = false;
129 self.hnd.tick();
130 }
131 }
132}
133
134bitflags::bitflags! {
135 #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
136 struct Flags: u8 {
137 const NEW = 0b0000_0001;
138 const NOTIFIER = 0b0000_0010;
139 }
140}
141
142struct DriverInner {
143 probe: Probe,
144 flags: Cell<Flags>,
145 ring: IoUring<SEntry, CEntry>,
146 changes: UnsafeCell<VecDeque<mem::MaybeUninit<SEntry>>>,
147}
148
149impl Driver {
150 const NOTIFY: u64 = u64::MAX;
151 const CANCEL: u64 = u64::MAX - 1;
152 const BATCH: u64 = 48;
153 const BATCH_MASK: u64 = 0xFFFF_0000_0000_0000;
154 const DATA_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
155
156 pub fn new(capacity: u32) -> io::Result<Self> {
158 let (new, ring) = if let Ok(ring) = IoUring::builder()
160 .setup_coop_taskrun()
161 .setup_single_issuer()
162 .setup_defer_taskrun()
163 .build(capacity)
164 {
165 log::info!(
166 "New io-uring driver with single-issuer, coop-taskrun, defer-taskrun"
167 );
168 (true, ring)
169 } else if let Ok(ring) = IoUring::builder().setup_single_issuer().build(capacity) {
170 log::info!("New io-uring driver with single-issuer");
171 (true, ring)
172 } else {
173 let ring = IoUring::builder().build(capacity)?;
174 log::info!("New io-uring driver");
175 (false, ring)
176 };
177
178 let mut probe = Probe::new();
179 ring.submitter().register_probe(&mut probe)?;
180
181 let notifier = Notifier::new()?;
183 unsafe {
184 let sq = ring.submission();
185 sq.push(
186 &PollAdd::new(Fd(notifier.as_raw_fd()), libc::POLLIN as _)
187 .multi(true)
188 .build()
189 .user_data(Self::NOTIFY),
190 )
191 .expect("the squeue sould not be full");
192 sq.sync();
193 }
194
195 let fd = ring.as_raw_fd();
196 let inner = Rc::new(DriverInner {
197 ring,
198 probe,
199 flags: Cell::new(if new { Flags::NEW } else { Flags::empty() }),
200 changes: UnsafeCell::new(VecDeque::with_capacity(32)),
201 });
202
203 Ok(Self {
204 fd,
205 inner,
206 notifier,
207 hid: Cell::new(0),
208 handlers: Cell::new(Some(Box::new(Vec::new()))),
209 })
210 }
211
212 pub const fn tp(&self) -> DriverType {
214 DriverType::IoUring
215 }
216
217 pub fn register<F>(&self, f: F)
219 where
220 F: FnOnce(DriverApi) -> Box<dyn Handler>,
221 {
222 let id = self.hid.get();
223 let mut handlers = self.handlers.take().unwrap_or_default();
224 handlers.push(HandlerItem {
225 hnd: f(DriverApi {
226 batch: id << Self::BATCH,
227 inner: self.inner.clone(),
228 }),
229 modified: false,
230 });
231 self.handlers.set(Some(handlers));
232 self.hid.set(id + 1);
233 }
234
235 fn apply_changes(&self, sq: SubmissionQueue<'_, SEntry>) -> bool {
236 unsafe {
237 let changes = &mut *self.inner.changes.get();
238 if changes.is_empty() {
239 false
240 } else {
241 let num = cmp::min(changes.len(), sq.capacity() - sq.len());
242 let (s1, s2) = changes.as_slices();
243 let s1_num = cmp::min(s1.len(), num);
244 if s1_num > 0 {
245 sq.push_multiple(
247 ((&raw const s1[0..s1_num]) as *const [SEntry])
248 .as_ref()
249 .unwrap(),
250 )
251 .unwrap();
252 } else if !s2.is_empty() {
253 let s2_num = cmp::min(s2.len(), num - s1_num);
254 if s2_num > 0 {
255 sq.push_multiple(
256 ((&raw const s2[0..s2_num]) as *const [SEntry])
257 .as_ref()
258 .unwrap(),
259 )
260 .unwrap();
261 }
262 }
263 changes.drain(0..num);
264
265 !changes.is_empty()
266 }
267 }
268 }
269
270 fn poll_completions(
272 &self,
273 cq: &mut cqueue::CompletionQueue<'_, CEntry>,
274 sq: SubmissionQueue<'_, SEntry>,
275 ) {
276 cq.sync();
277
278 if !cqueue::CompletionQueue::<'_, _>::is_empty(cq) {
279 let mut handlers = self.handlers.take().unwrap();
280 for entry in cq {
281 let user_data = entry.user_data();
282 match user_data {
283 Self::CANCEL => {}
284 Self::NOTIFY => {
285 let flags = entry.flags();
286 self.notifier.clear().expect("cannot clear notifier");
287
288 if !more(flags) {
290 unsafe {
291 sq.push(
292 &PollAdd::new(
293 Fd(self.notifier.as_raw_fd()),
294 libc::POLLIN as _,
295 )
296 .multi(true)
297 .build()
298 .user_data(Self::NOTIFY),
299 )
300 }
301 .expect("the squeue sould not be full");
302 }
303 }
304 _ => {
305 let batch =
306 ((user_data & Self::BATCH_MASK) >> Self::BATCH) as usize;
307 let user_data = (user_data & Self::DATA_MASK) as usize;
308
309 let result = entry.result();
310 if result == -libc::ECANCELED {
311 handlers[batch].modified = true;
312 handlers[batch].hnd.canceled(user_data);
313 } else {
314 let result = if result < 0 {
315 Err(io::Error::from_raw_os_error(-result))
316 } else {
317 #[allow(clippy::cast_sign_loss)]
318 Ok(result as _)
319 };
320 handlers[batch].modified = true;
321 handlers[batch]
322 .hnd
323 .completed(user_data, entry.flags(), result);
324 }
325 }
326 }
327 }
328 for h in handlers.iter_mut() {
329 h.tick();
330 }
331 self.handlers.set(Some(handlers));
332 }
333 }
334}
335
336impl AsRawFd for Driver {
337 fn as_raw_fd(&self) -> RawFd {
338 self.fd
339 }
340}
341
342impl crate::Reactor for Driver {
343 fn tcp_connect(&self, addr: net::SocketAddr, cfg: SharedCfg) -> Receiver<Io> {
344 let addr = SockAddr::from(addr);
345 let result = Socket::new(addr.domain(), Type::STREAM, Some(Protocol::TCP))
346 .and_then(crate::helpers::prep_socket)
347 .map(move |sock| (addr, sock));
348
349 match result {
350 Err(err) => Receiver::new(Err(err)),
351 Ok((addr, sock)) => {
352 super::connect::ConnectOps::get(self).connect(sock, addr, cfg)
353 }
354 }
355 }
356
357 fn unix_connect(&self, addr: std::path::PathBuf, cfg: SharedCfg) -> Receiver<Io> {
358 let result = SockAddr::unix(addr).and_then(|addr| {
359 Socket::new(addr.domain(), Type::STREAM, None)
360 .and_then(crate::helpers::prep_socket)
361 .map(move |sock| (addr, sock))
362 });
363
364 match result {
365 Err(err) => Receiver::new(Err(err)),
366 Ok((addr, sock)) => {
367 super::connect::ConnectOps::get(self).connect(sock, addr, cfg)
368 }
369 }
370 }
371
372 fn from_tcp_stream(&self, stream: net::TcpStream, cfg: SharedCfg) -> io::Result<Io> {
373 stream.set_nodelay(true)?;
374
375 Ok(Io::new(
376 TcpStream(
377 crate::helpers::prep_socket(Socket::from(stream))?,
378 StreamOps::get(self),
379 ),
380 cfg,
381 ))
382 }
383
384 #[cfg(unix)]
385 fn from_unix_stream(&self, stream: OsUnixStream, cfg: SharedCfg) -> io::Result<Io> {
386 Ok(Io::new(
387 UnixStream(
388 crate::helpers::prep_socket(Socket::from(stream))?,
389 StreamOps::get(self),
390 ),
391 cfg,
392 ))
393 }
394}
395
396impl ntex_rt::Driver for Driver {
397 fn run(&self, rt: &Runtime) -> io::Result<()> {
399 let ring = &self.inner.ring;
400 let sq = ring.submission();
401 let mut cq = unsafe { ring.completion_shared() };
402 let submitter = ring.submitter();
403 let result = loop {
404 self.poll_completions(&mut cq, sq);
405
406 let more_tasks = match rt.poll() {
407 PollResult::Pending => false,
408 PollResult::PollAgain => true,
409 PollResult::Ready => break Ok(()),
410 };
411 let more_changes = self.apply_changes(sq);
412
413 sq.sync();
416
417 let result = if more_changes || more_tasks {
418 submitter.submit()
419 } else {
420 submitter.submit_and_wait(1)
421 };
422
423 if let Err(e) = result {
424 match e.raw_os_error() {
425 Some(libc::ETIME | libc::EBUSY | libc::EAGAIN | libc::EINTR) => {
426 log::info!("Ring submit interrupted, {e:?}");
427 }
428 _ => break Err(e),
429 }
430 }
431 };
432
433 if result.is_ok() {
435 for mut h in self.handlers.take().unwrap().into_iter() {
436 h.hnd.cleanup();
437 }
438 }
439
440 result
441 }
442
443 fn handle(&self) -> Box<dyn Notify> {
445 Box::new(self.notifier.handle())
446 }
447}
448
449#[derive(Debug)]
450pub(crate) struct Notifier {
451 fd: Arc<OwnedFd>,
452}
453
454impl Notifier {
455 pub(crate) fn new() -> io::Result<Self> {
457 let fd = syscall!(libc::eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK))?;
458 let fd = unsafe { OwnedFd::from_raw_fd(fd) };
459 Ok(Self { fd: Arc::new(fd) })
460 }
461
462 pub(crate) fn clear(&self) -> io::Result<()> {
463 loop {
464 let mut buffer = [0u64];
465 let res = syscall!(libc::read(
466 self.fd.as_raw_fd(),
467 buffer.as_mut_ptr().cast(),
468 mem::size_of::<u64>()
469 ));
470 #[allow(clippy::cast_possible_wrap)]
471 match res {
472 Ok(len) => {
473 debug_assert_eq!(len, mem::size_of::<u64>() as isize);
474 break Ok(());
475 }
476 Err(e) if e.kind() == io::ErrorKind::WouldBlock => break Ok(()),
478 Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
480 Err(e) => break Err(e),
481 }
482 }
483 }
484
485 pub(crate) fn handle(&self) -> NotifyHandle {
486 NotifyHandle::new(self.fd.clone())
487 }
488}
489
490impl AsRawFd for Notifier {
491 fn as_raw_fd(&self) -> RawFd {
492 self.fd.as_raw_fd()
493 }
494}
495
496#[derive(Clone, Debug)]
497pub(crate) struct NotifyHandle {
499 fd: Arc<OwnedFd>,
500}
501
502impl NotifyHandle {
503 pub(crate) fn new(fd: Arc<OwnedFd>) -> Self {
504 Self { fd }
505 }
506}
507
508impl Notify for NotifyHandle {
509 fn notify(&self) -> io::Result<()> {
511 let data = 1u64;
512 syscall!(libc::write(
513 self.fd.as_raw_fd(),
514 (&raw const data).cast(),
515 std::mem::size_of::<u64>(),
516 ))?;
517 Ok(())
518 }
519}
520
521impl fmt::Debug for Driver {
522 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523 f.debug_struct("Driver")
524 .field("fd", &self.fd)
525 .field("hid", &self.hid)
526 .field("nodifier", &self.notifier)
527 .finish()
528 }
529}
530
531impl fmt::Debug for DriverApi {
532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533 f.debug_struct("DriverApi")
534 .field("batch", &self.batch)
535 .finish()
536 }
537}