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