1#![deny(clippy::pedantic)]
3#![allow(
4 clippy::clone_on_copy,
5 clippy::cast_possible_truncation,
6 clippy::missing_fields_in_debug,
7 clippy::must_use_candidate,
8 clippy::missing_errors_doc,
9 clippy::missing_panics_doc,
10 clippy::unused_async_trait_impl
11)]
12use std::{any::Any, io, net, net::SocketAddr, panic};
13
14use ntex_io::Io;
15use ntex_rt::{BlockFuture, Driver, Runner};
16use ntex_service::cfg::SharedCfg;
17
18pub mod channel;
19pub mod connect;
20
21#[cfg(unix)]
22pub mod polling;
23
24#[cfg(target_os = "linux")]
25pub mod uring;
26
27#[cfg(windows)]
28pub mod iocp;
29
30#[cfg(any(unix, windows))]
31mod helpers;
32
33#[cfg(feature = "tokio")]
34pub mod tokio;
35
36#[cfg(feature = "compio")]
37pub mod compio;
38
39#[allow(clippy::wrong_self_convention)]
40pub trait Reactor: Driver {
41 fn tcp_connect(&self, addr: net::SocketAddr, cfg: SharedCfg) -> channel::Receiver<Io>;
42
43 fn unix_connect(&self, addr: std::path::PathBuf, cfg: SharedCfg) -> channel::Receiver<Io>;
44
45 fn from_tcp_stream(&self, stream: net::TcpStream, cfg: SharedCfg) -> io::Result<Io>;
47
48 #[cfg(unix)]
49 fn from_unix_stream(&self, _: std::os::unix::net::UnixStream, _: SharedCfg) -> io::Result<Io>;
51}
52
53#[inline]
54pub async fn tcp_connect(addr: SocketAddr, cfg: SharedCfg) -> io::Result<Io> {
56 with_current(|driver| driver.tcp_connect(addr, cfg)).await
57}
58
59#[inline]
60pub async fn unix_connect<'a, P>(addr: P, cfg: SharedCfg) -> io::Result<Io>
62where
63 P: AsRef<std::path::Path> + 'a,
64{
65 with_current(|driver| driver.unix_connect(addr.as_ref().into(), cfg)).await
66}
67
68#[inline]
69pub fn from_tcp_stream(stream: net::TcpStream, cfg: SharedCfg) -> io::Result<Io> {
71 with_current(|driver| driver.from_tcp_stream(stream, cfg))
72}
73
74#[cfg(unix)]
75#[inline]
76pub fn from_unix_stream(stream: std::os::unix::net::UnixStream, cfg: SharedCfg) -> io::Result<Io> {
78 with_current(|driver| driver.from_unix_stream(stream, cfg))
79}
80
81fn with_current<T, F: FnOnce(&dyn Reactor) -> T>(f: F) -> T {
82 #[cold]
83 fn not_in_ntex_driver() -> ! {
84 panic!("not in a ntex driver")
85 }
86
87 if CURRENT_DRIVER.is_set() {
88 CURRENT_DRIVER.with(|d| f(&**d))
89 } else {
90 not_in_ntex_driver()
91 }
92}
93
94#[allow(clippy::borrowed_box)]
95pub fn with_reactor<R, F: FnOnce() -> R>(r: &Box<dyn Reactor>, f: F) -> R {
97 #[cold]
98 fn reactor_is_set() -> ! {
99 panic!("reactor is already set");
100 }
101
102 if CURRENT_DRIVER.is_set() {
103 reactor_is_set()
104 }
105 CURRENT_DRIVER.set(r, f)
106}
107
108scoped_tls::scoped_thread_local!(static CURRENT_DRIVER: Box<dyn Reactor>);
109
110#[derive(Copy, Clone, Debug)]
115pub struct DefaultRuntime;
116
117impl Runner for DefaultRuntime {
118 #[allow(unused_variables, clippy::too_many_lines)]
119 fn block_on(&self, fut: BlockFuture) -> Result<(), Box<dyn Any + Send>> {
120 #[cfg(feature = "tokio")]
121 {
122 let driver: Box<dyn Reactor> = Box::new(self::tokio::Reactor);
123
124 with_reactor(&driver, || crate::tokio::block_on(fut));
125 Ok(())
126 }
127
128 #[cfg(all(feature = "compio", not(feature = "tokio")))]
129 {
130 let driver: Box<dyn Reactor> = Box::new(self::compio::Reactor);
131
132 with_reactor(&driver, || crate::compio::block_on(fut));
133 Ok(())
134 }
135
136 #[cfg(all(windows, not(feature = "tokio"), not(feature = "compio")))]
137 {
138 let driver: Box<dyn Reactor> =
139 Box::new(crate::iocp::Reactor::new().expect("Cannot construct driver"));
140
141 with_reactor(&driver, || {
142 panic::catch_unwind(panic::AssertUnwindSafe(|| {
143 let rt = ntex_rt::Runtime::new(driver.handle());
144 rt.block_on(fut, &*driver);
145 }))
146 })
147 }
148
149 #[cfg(all(unix, not(feature = "tokio"), not(feature = "compio")))]
150 {
151 #[cfg(feature = "neon-polling")]
152 {
153 let driver: Box<dyn Reactor> = Box::new(
154 crate::polling::Reactor::new().expect("Cannot construct polling reactor"),
155 );
156
157 with_reactor(&driver, || {
158 panic::catch_unwind(panic::AssertUnwindSafe(|| {
159 let rt = ntex_rt::Runtime::new(driver.handle());
160 rt.block_on(fut, &*driver);
161 }))
162 })
163 }
164
165 #[cfg(all(target_os = "linux", feature = "neon-uring"))]
166 {
167 let driver: Box<dyn Reactor> = Box::new(
168 crate::uring::Reactor::new(2048).expect("Cannot construct io-uring reactor"),
169 );
170
171 with_reactor(&driver, || {
172 panic::catch_unwind(panic::AssertUnwindSafe(|| {
173 let rt = ntex_rt::Runtime::new(driver.handle());
174 rt.block_on(fut, &*driver);
175 }))
176 })
177 }
178
179 #[cfg(all(not(feature = "neon-uring"), not(feature = "neon-polling")))]
180 {
181 #[cfg(target_os = "linux")]
182 let driver: Box<dyn Reactor> = if let Ok(reactor) = crate::uring::Reactor::new(2048)
183 {
184 Box::new(reactor)
185 } else {
186 Box::new(
187 crate::polling::Reactor::new().expect("Cannot construct io-uring reactor"),
188 )
189 };
190
191 #[cfg(not(target_os = "linux"))]
192 let driver: Box<dyn Reactor> = Box::new(
193 crate::polling::Reactor::new().expect("Cannot construct polling reactor"),
194 );
195
196 with_reactor(&driver, || {
197 panic::catch_unwind(panic::AssertUnwindSafe(|| {
198 let rt = ntex_rt::Runtime::new(driver.handle());
199 rt.block_on(fut, &*driver);
200 }))
201 })
202 }
203 }
204 }
205}