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 latency_interval: usize,
21 signals: bool,
23 panics: bool,
25 pool_limit: usize,
27 pool_recv_timeout: time::Duration,
28 testing: bool,
30}
31
32impl Builder {
33 pub(super) fn new() -> Self {
34 Builder {
35 name: "ntex".into(),
36 stack_size: 0,
37 ping_interval: 2000,
38 ping_threshold: 1000,
39 latency_interval: 100,
40 signals: false,
41 panics: false,
42 testing: false,
43 pool_limit: 256,
44 pool_recv_timeout: time::Duration::from_mins(1),
45 }
46 }
47
48 #[must_use]
49 pub fn name<N: AsRef<str>>(mut self, name: N) -> Self {
51 self.name = name.as_ref().into();
52 self
53 }
54
55 #[must_use]
56 pub fn signals(mut self, eanbled: bool) -> Self {
60 self.signals = eanbled;
61 self
62 }
63
64 #[must_use]
65 pub fn panic_handling(mut self, eanbled: bool) -> Self {
72 self.panics = eanbled;
73 self
74 }
75
76 #[doc(hidden)]
77 #[must_use]
78 pub fn disable_signals(mut self) -> Self {
82 self.signals = false;
83 self
84 }
85
86 #[doc(hidden)]
87 #[must_use]
88 pub fn enable_signals(mut self) -> Self {
92 self.signals = true;
93 self
94 }
95
96 #[must_use]
97 pub fn stack_size(mut self, size: usize) -> Self {
99 self.stack_size = size;
100 self
101 }
102
103 #[must_use]
104 pub fn ping_interval(mut self, interval: usize) -> Self {
109 self.ping_interval = interval;
110 self
111 }
112
113 #[must_use]
114 pub fn ping_threshold(mut self, interval: usize) -> Self {
124 self.ping_threshold = interval;
125 self
126 }
127
128 #[must_use]
129 pub fn latency_interval(mut self, interval: usize) -> Self {
137 self.latency_interval = interval;
138 self
139 }
140
141 #[must_use]
142 pub fn thread_pool_limit(mut self, value: usize) -> Self {
146 self.pool_limit = value;
147 self
148 }
149
150 #[must_use]
151 pub fn testing(mut self) -> Self {
155 self.testing = true;
156 self.signals = false;
157 self.panics = false;
158 self
159 }
160
161 #[must_use]
162 pub fn thread_pool_recv_timeout<T>(mut self, timeout: T) -> Self
166 where
167 time::Duration: From<T>,
168 {
169 self.pool_recv_timeout = timeout.into();
170 self
171 }
172
173 pub fn build<R: Runner>(self, runner: R) -> SystemRunner {
179 let config = SystemConfig {
180 name: self.name.clone(),
181 testing: self.testing,
182 stack_size: self.stack_size,
183 ping_interval: self.ping_interval,
184 ping_threshold: self.ping_threshold,
185 latency_interval: self.latency_interval,
186 pool_limit: self.pool_limit,
187 pool_recv_timeout: self.pool_recv_timeout,
188 runner: Arc::new(runner),
189 };
190 self.build_with(config)
191 }
192
193 pub fn build_with(self, config: SystemConfig) -> SystemRunner {
199 let runner = config.runner.clone();
200
201 SystemRunner {
203 config,
204 runner,
205 signals: self.signals,
206 panics: self.panics,
207 _t: PhantomData,
208 }
209 }
210}
211
212#[must_use = "SystemRunner must be run"]
214pub struct SystemRunner {
215 config: SystemConfig,
216 runner: Arc<dyn Runner>,
217 signals: bool,
218 panics: bool,
219 _t: PhantomData<Rc<()>>,
220}
221
222impl SystemRunner {
223 pub fn run_until_stop(self) -> io::Result<()> {
225 self.run(|| Ok(()))
226 }
227
228 pub fn run<F>(self, f: F) -> io::Result<()>
230 where
231 F: FnOnce() -> io::Result<()> + 'static,
232 {
233 log::info!("Starting {:?} system", self.config.name);
234
235 let SystemRunner {
236 config,
237 runner,
238 signals,
239 panics,
240 ..
241 } = self;
242
243 if panics {
244 signals::enable_panic_handling();
245 }
246
247 crate::driver::block_on_panic(runner.as_ref(), async move {
249 let (system, stop) = System::start(config);
250 let _guard = SystemGuard(system.clone());
251 if signals {
252 system.enable_signals();
253 }
254
255 f()?;
256
257 let result = stop.await;
258
259 match result {
260 Ok(code) => {
261 if code != 0 {
262 Err(io::Error::other(format!("Non-zero exit code: {code}")))
263 } else {
264 Ok(())
265 }
266 }
267 Err(_) => Err(io::Error::other("Closed")),
268 }
269 })
270 }
271
272 #[allow(clippy::missing_panics_doc)]
273 pub fn block_on<F, R>(self, fut: F) -> R
275 where
276 F: Future<Output = R> + 'static,
277 R: 'static,
278 {
279 let SystemRunner {
280 config,
281 runner,
282 signals,
283 panics,
284 ..
285 } = self;
286
287 if panics {
288 signals::enable_panic_handling();
289 }
290
291 crate::driver::block_on_panic(runner.as_ref(), async move {
292 let (system, _) = System::start(config);
293 let _guard = SystemGuard(system.clone());
294 if signals {
295 system.enable_signals();
296 }
297
298 let loc = current_location();
299 ntex_error::set_backtrace_start(loc.file(), loc.line() + 2);
300 fut.await
301 })
302 }
303
304 #[cfg(feature = "tokio")]
305 pub async fn run_local<F, R>(self, fut: F) -> R
307 where
308 F: Future<Output = R> + 'static,
309 R: 'static,
310 {
311 let SystemRunner { config, .. } = self;
312
313 let result = tok_io::task::LocalSet::new()
315 .run_until(async move {
316 let (system, _) = System::start(config);
317 let _guard = SystemGuard(system);
318
319 let loc = current_location();
320 ntex_error::set_backtrace_start(loc.file(), loc.line() + 2);
321 fut.await
322 })
323 .await;
324
325 crate::arbiter::run_shutdown_callbacks();
326 unsafe {
327 crate::remove_all_items();
328 }
329 if let Some(arb) = crate::Arbiter::try_current() {
330 arb.finalize();
331 }
332 result
333 }
334}
335
336struct SystemGuard(System);
339
340impl Drop for SystemGuard {
341 fn drop(&mut self) {
342 self.0.disable_signals();
343
344 if std::thread::panicking() {
345 let list = self.0.take_arbiters();
348 if !list.is_empty() {
349 let stopped = std::thread::Builder::new()
350 .spawn(move || list.into_iter().for_each(|arb| arb.stop()))
351 .map(std::thread::JoinHandle::join);
352 if let Ok(Err(payload)) = stopped {
353 std::mem::forget(payload);
354 crate::abort_on_second_panic();
355 }
356 }
357 } else {
358 self.0.stop_arbiters();
359 }
360 }
361}
362
363#[track_caller]
364pub(crate) fn current_location() -> &'static panic::Location<'static> {
365 panic::Location::caller()
366}
367
368impl fmt::Debug for SystemRunner {
369 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370 f.debug_struct("SystemRunner")
371 .field("config", &self.config)
372 .finish()
373 }
374}
375
376#[cfg(test)]
377mod tests {
378 use std::sync::atomic::{AtomicUsize, Ordering};
379 use std::{cell::Cell, cell::RefCell, time::Duration};
380
381 use super::*;
382 use crate::{Arbiter, testing::TestRunner};
383
384 #[test]
385 fn builder_options() {
386 let builder = System::build()
387 .name("opts")
388 .signals(false)
389 .panic_handling(false)
390 .enable_signals()
391 .disable_signals()
392 .stack_size(2 * 1024 * 1024)
393 .ping_interval(0)
394 .ping_threshold(10)
395 .thread_pool_limit(1)
396 .thread_pool_recv_timeout(Duration::from_millis(100))
397 .testing();
398 assert!(format!("{builder:?}").contains("opts"));
399
400 let runner = builder.build(TestRunner);
401 assert!(format!("{runner:?}").contains("opts"));
402 runner.block_on(async {
403 let sys = System::current();
404 assert_eq!(sys.name(), "opts");
405 assert!(sys.testing());
406 assert!(!sys.signals());
407
408 let mut arb = Arbiter::new();
410 arb.stop();
411 arb.join().unwrap();
412
413 assert_eq!(sys.spawn_blocking(|| 1).await, Ok(1));
414 });
415 }
416
417 #[test]
418 fn run_exit_codes() {
419 let err = System::new("test", TestRunner)
421 .run(|| Err(io::Error::other("init failed")))
422 .unwrap_err();
423 assert_eq!(err.to_string(), "init failed");
424
425 System::new("test", TestRunner)
426 .run(|| {
427 System::current().stop();
428 Ok(())
429 })
430 .unwrap();
431
432 let called = Rc::new(Cell::new(false));
434 let arb = Rc::new(RefCell::new(None));
435 let (called2, arb2) = (called.clone(), arb.clone());
436 let err = System::new("test", TestRunner)
437 .run(move || {
438 Arbiter::on_shutdown(move || called2.set(true));
439 *arb2.borrow_mut() = Some(Arbiter::new());
440 System::current().stop_with_code(3);
441 Ok(())
442 })
443 .unwrap_err();
444 assert_eq!(err.to_string(), "Non-zero exit code: 3");
445 assert!(called.get());
446
447 let mut arb = arb.borrow_mut().take().unwrap();
448 arb.join().unwrap();
449 assert!(!arb.is_running());
450 }
451
452 #[test]
453 fn exit_stops_child_arbiters() {
454 fn assert_stopped(arb: &Rc<RefCell<Option<Arbiter>>>) {
455 let arb = arb.borrow_mut().take().unwrap();
456 let mut arb2 = arb.clone();
457 let (tx, rx) = std::sync::mpsc::channel();
458 std::thread::spawn(move || {
459 let _ = tx.send(arb2.join().is_ok());
460 });
461 let stopped = rx.recv_timeout(Duration::from_secs(5));
462 arb.stop();
463 assert_eq!(stopped, Ok(true), "child arbiter is still running");
464 }
465
466 let arb = Rc::new(RefCell::new(None));
468 let arb2 = arb.clone();
469 let err = System::new("test", TestRunner)
470 .run(move || {
471 *arb2.borrow_mut() = Some(Arbiter::new());
472 Err(io::Error::other("init failed"))
473 })
474 .unwrap_err();
475 assert_eq!(err.to_string(), "init failed");
476 assert_stopped(&arb);
477
478 let arb2 = arb.clone();
480 System::new("test", TestRunner).block_on(async move {
481 *arb2.borrow_mut() = Some(Arbiter::new());
482 });
483 assert_stopped(&arb);
484
485 let arb2 = arb.clone();
487 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
488 System::new("test", TestRunner).block_on(async move {
489 *arb2.borrow_mut() = Some(Arbiter::new());
490 panic!("root future panic");
491 });
492 }));
493 assert!(result.is_err());
494 assert_stopped(&arb);
495 }
496
497 #[test]
498 fn primary_arbiter_finalized() {
499 fn with_on_stop(calls: &Arc<AtomicUsize>) -> Arbiter {
500 let calls = calls.clone();
501 Arbiter::current().on_stop(move || {
502 calls.fetch_add(1, Ordering::Relaxed);
503 })
504 }
505
506 let calls = Arc::new(AtomicUsize::new(0));
508 let calls2 = calls.clone();
509 let arb = System::new("test", TestRunner).block_on(async move { with_on_stop(&calls2) });
510 assert!(!arb.is_running());
511 assert_eq!(calls.load(Ordering::Relaxed), 1);
512
513 let calls = Arc::new(AtomicUsize::new(0));
515 let (calls2, slot) = (calls.clone(), Rc::new(RefCell::new(None)));
516 let slot2 = slot.clone();
517 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
518 System::new("test", TestRunner).block_on(async move {
519 *slot2.borrow_mut() = Some(with_on_stop(&calls2));
520 panic!("root future panic");
521 });
522 }));
523 assert!(result.is_err());
524 assert!(!slot.borrow().as_ref().unwrap().is_running());
525 assert_eq!(calls.load(Ordering::Relaxed), 1);
526
527 let calls = Arc::new(AtomicUsize::new(0));
529 let (calls2, slot) = (calls.clone(), Rc::new(RefCell::new(None)));
530 let slot2 = slot.clone();
531 System::new("test", TestRunner)
532 .run(move || {
533 *slot2.borrow_mut() = Some(with_on_stop(&calls2));
534 System::current().stop();
535 Ok(())
536 })
537 .unwrap();
538 assert!(!slot.borrow().as_ref().unwrap().is_running());
539 assert_eq!(calls.load(Ordering::Relaxed), 1);
540 }
541
542 #[cfg(feature = "tokio")]
543 #[test]
544 fn run_local_finalizes_primary_arbiter() {
545 let calls = Arc::new(AtomicUsize::new(0));
546 let calls2 = calls.clone();
547 let rt = tok_io::runtime::Builder::new_current_thread()
548 .enable_all()
549 .build()
550 .unwrap();
551 let arb = rt.block_on(System::new("test", TestRunner).run_local(async move {
552 Arbiter::current().on_stop(move || {
553 calls2.fetch_add(1, Ordering::Relaxed);
554 })
555 }));
556 assert!(!arb.is_running());
557 assert_eq!(calls.load(Ordering::Relaxed), 1);
558 }
559}