1use std::{fmt, future::Future, io, marker::PhantomData, panic, rc::Rc, sync::Arc, time};
2
3use crate::{driver::Runner, signals, system::System, system::SystemConfig};
4
5#[derive(Debug, Clone)]
6pub struct Builder {
12 name: String,
14 stack_size: usize,
16 ping_interval: usize,
18 ping_threshold: usize,
20 signals: bool,
22 panics: bool,
24 pool_limit: usize,
26 pool_recv_timeout: time::Duration,
27 testing: bool,
29}
30
31impl Builder {
32 pub(super) fn new() -> Self {
33 Builder {
34 name: "ntex".into(),
35 stack_size: 0,
36 ping_interval: 2000,
37 ping_threshold: 1000,
38 signals: false,
39 panics: false,
40 testing: false,
41 pool_limit: 256,
42 pool_recv_timeout: time::Duration::from_secs(60),
43 }
44 }
45
46 #[must_use]
47 pub fn name<N: AsRef<str>>(mut self, name: N) -> Self {
49 self.name = name.as_ref().into();
50 self
51 }
52
53 #[doc(hidden)]
54 #[deprecated(since = "3.17.0")]
55 #[must_use]
56 pub fn stop_on_panic(self, _: bool) -> Self {
63 self
64 }
65
66 #[must_use]
67 pub fn signals(mut self, eanbled: bool) -> Self {
71 self.signals = eanbled;
72 self
73 }
74
75 #[must_use]
76 pub fn panic_handling(mut self, eanbled: bool) -> Self {
82 self.panics = eanbled;
83 self
84 }
85
86 #[doc(hidden)]
87 #[must_use]
88 pub fn disable_signals(mut self) -> Self {
92 self.signals = false;
93 self
94 }
95
96 #[doc(hidden)]
97 #[must_use]
98 pub fn enable_signals(mut self) -> Self {
102 self.signals = true;
103 self
104 }
105
106 #[must_use]
107 pub fn stack_size(mut self, size: usize) -> Self {
109 self.stack_size = size;
110 self
111 }
112
113 #[must_use]
114 pub fn ping_interval(mut self, interval: usize) -> Self {
119 self.ping_interval = interval;
120 self
121 }
122
123 #[must_use]
124 pub fn ping_threshold(mut self, interval: usize) -> Self {
131 self.ping_threshold = interval;
132 self
133 }
134
135 #[must_use]
136 pub fn thread_pool_limit(mut self, value: usize) -> Self {
139 self.pool_limit = value;
140 self
141 }
142
143 #[must_use]
144 pub fn testing(mut self) -> Self {
146 self.testing = true;
147 self.signals = false;
148 self.panics = false;
149 self
150 }
151
152 #[must_use]
153 pub fn thread_pool_recv_timeout<T>(mut self, timeout: T) -> Self
156 where
157 time::Duration: From<T>,
158 {
159 self.pool_recv_timeout = timeout.into();
160 self
161 }
162
163 pub fn build<R: Runner>(self, runner: R) -> SystemRunner {
167 let config = SystemConfig {
168 name: self.name.clone(),
169 testing: self.testing,
170 stack_size: self.stack_size,
171 ping_interval: self.ping_interval,
172 ping_threshold: self.ping_threshold,
173 pool_limit: self.pool_limit,
174 pool_recv_timeout: self.pool_recv_timeout,
175 runner: Arc::new(runner),
176 };
177 self.build_with(config)
178 }
179
180 pub fn build_with(self, config: SystemConfig) -> SystemRunner {
184 let runner = config.runner.clone();
185
186 SystemRunner {
188 config,
189 runner,
190 signals: self.signals,
191 panics: self.panics,
192 _t: PhantomData,
193 }
194 }
195}
196
197#[must_use = "SystemRunner must be run"]
199pub struct SystemRunner {
200 config: SystemConfig,
201 runner: Arc<dyn Runner>,
202 signals: bool,
203 panics: bool,
204 _t: PhantomData<Rc<()>>,
205}
206
207impl SystemRunner {
208 pub fn run_until_stop(self) -> io::Result<()> {
211 self.run(|| Ok(()))
212 }
213
214 pub fn run<F>(self, f: F) -> io::Result<()>
217 where
218 F: FnOnce() -> io::Result<()> + 'static,
219 {
220 log::info!("Starting {:?} system", self.config.name);
221
222 let SystemRunner {
223 config,
224 runner,
225 signals,
226 panics,
227 ..
228 } = self;
229
230 if panics {
231 signals::enable_panic_handling();
232 }
233
234 crate::driver::block_on_panic(runner.as_ref(), async move {
236 let (system, stop) = System::start(config);
237 if signals {
238 system.enable_signals();
239 }
240
241 f()?;
242
243 match stop.await {
244 Ok(code) => {
245 if code != 0 {
246 Err(io::Error::other(format!("Non-zero exit code: {code}")))
247 } else {
248 Ok(())
249 }
250 }
251 Err(_) => Err(io::Error::other("Closed")),
252 }
253 })
254 }
255
256 #[allow(clippy::missing_panics_doc)]
257 pub fn block_on<F, R>(self, fut: F) -> R
259 where
260 F: Future<Output = R> + 'static,
261 R: 'static,
262 {
263 let SystemRunner {
264 config,
265 runner,
266 signals,
267 panics,
268 ..
269 } = self;
270
271 if panics {
272 signals::enable_panic_handling();
273 }
274
275 crate::driver::block_on_panic(runner.as_ref(), async move {
276 let (system, _) = System::start(config);
277 if signals {
278 system.enable_signals();
279 }
280
281 let loc = current_location();
282 ntex_error::set_backtrace_start(loc.file(), loc.line() + 2);
283 fut.await
284 })
285 }
286
287 #[cfg(feature = "tokio")]
288 pub async fn run_local<F, R>(self, fut: F) -> R
290 where
291 F: Future<Output = R> + 'static,
292 R: 'static,
293 {
294 let SystemRunner { config, .. } = self;
295
296 let result = tok_io::task::LocalSet::new()
298 .run_until(async move {
299 _ = System::start(config);
300
301 let loc = current_location();
302 ntex_error::set_backtrace_start(loc.file(), loc.line() + 2);
303 fut.await
304 })
305 .await;
306
307 unsafe {
308 crate::remove_all_items();
309 }
310 result
311 }
312}
313
314#[track_caller]
315pub(crate) fn current_location() -> &'static panic::Location<'static> {
316 panic::Location::caller()
317}
318
319impl fmt::Debug for SystemRunner {
320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321 f.debug_struct("SystemRunner")
322 .field("config", &self.config)
323 .finish()
324 }
325}