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::{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")]
36mod 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
104scoped_tls::scoped_thread_local!(static CURRENT_DRIVER: Box<dyn Reactor>);
105
106#[derive(Debug)]
107pub struct DefaultRuntime;
108
109impl Runner for DefaultRuntime {
110 #[allow(unused_variables, clippy::too_many_lines)]
111 fn block_on(&self, fut: BlockFuture) {
112 #[cfg(feature = "tokio")]
113 {
114 let driver: Box<dyn Reactor> = Box::new(self::tokio::TokioDriver);
115
116 CURRENT_DRIVER.set(&driver, || {
117 crate::tokio::block_on(fut);
118 unsafe {
119 ntex_rt::remove_all_items();
120 }
121 });
122 }
123
124 #[cfg(all(feature = "compio", not(feature = "tokio")))]
125 {
126 let driver: Box<dyn Reactor> = Box::new(self::compio::CompioDriver);
127
128 CURRENT_DRIVER.set(&driver, || {
129 crate::compio::block_on(fut);
130 unsafe {
131 ntex_rt::remove_all_items();
132 }
133 });
134 }
135
136 #[cfg(all(windows, not(feature = "tokio"), not(feature = "compio")))]
137 {
138 let driver = crate::iocp::Driver::new().expect("Cannot construct driver");
139 let driver: Box<dyn Reactor> = Box::new(driver);
140
141 CURRENT_DRIVER.set(&driver, || {
142 let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
143 let rt = ntex_rt::Runtime::new(driver.handle());
144 rt.block_on(fut, &*driver);
145 }));
146 unsafe {
147 if let Err(err) = res {
148 ntex_rt::remove_all_items();
149 panic::resume_unwind(err);
150 } else {
151 ntex_rt::remove_all_items();
152 }
153 }
154 });
155 }
156
157 #[cfg(all(unix, not(feature = "tokio"), not(feature = "compio")))]
158 {
159 #[cfg(feature = "neon-polling")]
160 {
161 let driver =
162 crate::polling::Driver::new().expect("Cannot construct driver");
163 let driver: Box<dyn Reactor> = Box::new(driver);
164
165 CURRENT_DRIVER.set(&driver, || {
166 let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
167 let rt = ntex_rt::Runtime::new(driver.handle());
168 rt.block_on(fut, &*driver);
169 }));
170 unsafe {
171 if let Err(err) = res {
172 ntex_rt::remove_all_items();
173 panic::resume_unwind(err);
174 } else {
175 ntex_rt::remove_all_items();
176 }
177 }
178 });
179 }
180
181 #[cfg(all(target_os = "linux", feature = "neon-uring"))]
182 {
183 let driver =
184 crate::uring::Driver::new(2048).expect("Cannot construct driver");
185 let driver: Box<dyn Reactor> = Box::new(driver);
186
187 CURRENT_DRIVER.set(&driver, || {
188 let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
189 let rt = ntex_rt::Runtime::new(driver.handle());
190 rt.block_on(fut, &*driver);
191 }));
192 unsafe {
193 if let Err(err) = res {
194 ntex_rt::remove_all_items();
195 panic::resume_unwind(err);
196 } else {
197 ntex_rt::remove_all_items();
198 }
199 }
200 });
201 }
202
203 #[cfg(all(not(feature = "neon-uring"), not(feature = "neon-polling")))]
204 {
205 #[cfg(target_os = "linux")]
206 let driver: Box<dyn Reactor> =
207 if let Ok(driver) = crate::uring::Driver::new(2048) {
208 Box::new(driver)
209 } else {
210 Box::new(
211 crate::polling::Driver::new().expect("Cannot construct driver"),
212 )
213 };
214
215 #[cfg(not(target_os = "linux"))]
216 let driver: Box<dyn Reactor> = Box::new(
217 crate::polling::Driver::new().expect("Cannot construct driver"),
218 );
219
220 CURRENT_DRIVER.set(&driver, || {
221 let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
222 let rt = ntex_rt::Runtime::new(driver.handle());
223 rt.block_on(fut, &*driver);
224 }));
225 unsafe {
226 if let Err(err) = res {
227 ntex_rt::remove_all_items();
228 panic::resume_unwind(err);
229 } else {
230 ntex_rt::remove_all_items();
231 }
232 }
233 });
234 }
235 }
236 }
237}