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 {
11 name: String,
13 stack_size: usize,
15 ping_interval: usize,
17 ping_threshold: usize,
19 signals: bool,
21 panics: bool,
23 pool_limit: usize,
25 pool_recv_timeout: time::Duration,
26 testing: bool,
28}
29
30impl Builder {
31 pub(super) fn new() -> Self {
32 Builder {
33 name: "ntex".into(),
34 stack_size: 0,
35 ping_interval: 2000,
36 ping_threshold: 1000,
37 signals: false,
38 panics: false,
39 testing: false,
40 pool_limit: 256,
41 pool_recv_timeout: time::Duration::from_secs(60),
42 }
43 }
44
45 #[must_use]
46 pub fn name<N: AsRef<str>>(mut self, name: N) -> Self {
48 self.name = name.as_ref().into();
49 self
50 }
51
52 #[doc(hidden)]
53 #[deprecated(since = "3.17.0")]
54 #[must_use]
55 pub fn stop_on_panic(self, _: bool) -> Self {
62 self
63 }
64
65 #[must_use]
66 pub fn signals(mut self, eanbled: bool) -> Self {
70 self.signals = eanbled;
71 self
72 }
73
74 #[must_use]
75 pub fn panic_handling(mut self, eanbled: bool) -> Self {
81 self.panics = eanbled;
82 self
83 }
84
85 #[doc(hidden)]
86 #[must_use]
87 pub fn disable_signals(mut self) -> Self {
91 self.signals = false;
92 self
93 }
94
95 #[doc(hidden)]
96 #[must_use]
97 pub fn enable_signals(mut self) -> Self {
101 self.signals = true;
102 self
103 }
104
105 #[must_use]
106 pub fn stack_size(mut self, size: usize) -> Self {
108 self.stack_size = size;
109 self
110 }
111
112 #[must_use]
113 pub fn ping_interval(mut self, interval: usize) -> Self {
118 self.ping_interval = interval;
119 self
120 }
121
122 #[must_use]
123 pub fn ping_threshold(mut self, interval: usize) -> Self {
130 self.ping_threshold = interval;
131 self
132 }
133
134 #[must_use]
135 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 {
148 self.testing = true;
149 self.signals = false;
150 self.panics = false;
151 self
152 }
153
154 #[must_use]
155 pub fn thread_pool_recv_timeout<T>(mut self, timeout: T) -> Self
159 where
160 time::Duration: From<T>,
161 {
162 self.pool_recv_timeout = timeout.into();
163 self
164 }
165
166 pub fn build<R: Runner>(self, runner: R) -> SystemRunner {
172 let config = SystemConfig {
173 name: self.name.clone(),
174 testing: self.testing,
175 stack_size: self.stack_size,
176 ping_interval: self.ping_interval,
177 ping_threshold: self.ping_threshold,
178 pool_limit: self.pool_limit,
179 pool_recv_timeout: self.pool_recv_timeout,
180 runner: Arc::new(runner),
181 };
182 self.build_with(config)
183 }
184
185 pub fn build_with(self, config: SystemConfig) -> SystemRunner {
191 let runner = config.runner.clone();
192
193 SystemRunner {
195 config,
196 runner,
197 signals: self.signals,
198 panics: self.panics,
199 _t: PhantomData,
200 }
201 }
202}
203
204#[must_use = "SystemRunner must be run"]
206pub struct SystemRunner {
207 config: SystemConfig,
208 runner: Arc<dyn Runner>,
209 signals: bool,
210 panics: bool,
211 _t: PhantomData<Rc<()>>,
212}
213
214impl SystemRunner {
215 pub fn run_until_stop(self) -> io::Result<()> {
217 self.run(|| Ok(()))
218 }
219
220 pub fn run<F>(self, f: F) -> io::Result<()>
222 where
223 F: FnOnce() -> io::Result<()> + 'static,
224 {
225 log::info!("Starting {:?} system", self.config.name);
226
227 let SystemRunner {
228 config,
229 runner,
230 signals,
231 panics,
232 ..
233 } = self;
234
235 if panics {
236 signals::enable_panic_handling();
237 }
238
239 crate::driver::block_on_panic(runner.as_ref(), async move {
241 let (system, stop) = System::start(config);
242 if signals {
243 system.enable_signals();
244 }
245
246 f()?;
247
248 match stop.await {
249 Ok(code) => {
250 if code != 0 {
251 Err(io::Error::other(format!("Non-zero exit code: {code}")))
252 } else {
253 Ok(())
254 }
255 }
256 Err(_) => Err(io::Error::other("Closed")),
257 }
258 })
259 }
260
261 #[allow(clippy::missing_panics_doc)]
262 pub fn block_on<F, R>(self, fut: F) -> R
264 where
265 F: Future<Output = R> + 'static,
266 R: 'static,
267 {
268 let SystemRunner {
269 config,
270 runner,
271 signals,
272 panics,
273 ..
274 } = self;
275
276 if panics {
277 signals::enable_panic_handling();
278 }
279
280 crate::driver::block_on_panic(runner.as_ref(), async move {
281 let (system, _) = System::start(config);
282 if signals {
283 system.enable_signals();
284 }
285
286 let loc = current_location();
287 ntex_error::set_backtrace_start(loc.file(), loc.line() + 2);
288 fut.await
289 })
290 }
291
292 #[cfg(feature = "tokio")]
293 pub async fn run_local<F, R>(self, fut: F) -> R
295 where
296 F: Future<Output = R> + 'static,
297 R: 'static,
298 {
299 let SystemRunner { config, .. } = self;
300
301 let result = tok_io::task::LocalSet::new()
303 .run_until(async move {
304 _ = System::start(config);
305
306 let loc = current_location();
307 ntex_error::set_backtrace_start(loc.file(), loc.line() + 2);
308 fut.await
309 })
310 .await;
311
312 unsafe {
313 crate::remove_all_items();
314 }
315 result
316 }
317}
318
319#[track_caller]
320pub(crate) fn current_location() -> &'static panic::Location<'static> {
321 panic::Location::caller()
322}
323
324impl fmt::Debug for SystemRunner {
325 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326 f.debug_struct("SystemRunner")
327 .field("config", &self.config)
328 .finish()
329 }
330}