1use std::any::{Any, TypeId};
2use std::collections::VecDeque;
3use std::sync::{Arc, atomic::AtomicBool, atomic::AtomicUsize, atomic::Ordering};
4use std::time::{Duration, Instant};
5use std::{cell::RefCell, fmt, future::Future, pin::Pin};
6
7use async_channel::{Receiver, Sender, unbounded};
8use futures_timer::Delay;
9use parking_lot::{Mutex, RwLock};
10
11use crate::arbiter::Arbiter;
12use crate::pool::{PoolMetrics, ThreadPool};
13use crate::{BlockingResult, Builder, Handle, HashMap, Runner, SystemRunner};
14
15static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0);
16
17thread_local!(
18 static PINGS: RefCell<HashMap<Id, VecDeque<PingRecord>>> = RefCell::new(HashMap::default());
19);
20
21#[derive(Default)]
22struct Arbiters {
23 all: HashMap<Id, Arbiter>,
24 list: Vec<Arbiter>,
25}
26
27#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
29pub struct Id(pub(crate) usize);
30
31pub struct System(pub(crate) Arc<SystemInner>);
36
37pub(crate) struct SystemInner {
38 id: usize,
39 arbiter: Arbiter,
40 pub(crate) config: SystemConfig,
41 sender: Sender<SystemCommand>,
42 receiver: Receiver<SystemCommand>,
43 storage: RwLock<HashMap<TypeId, Box<dyn Any + Sync + Send>>>,
44 arbiters: Mutex<Arbiters>,
45 signals: AtomicBool,
46 pool: ThreadPool,
47}
48
49#[derive(Clone)]
51pub struct SystemConfig {
52 pub(super) name: String,
53 pub(super) stack_size: usize,
54 pub(super) ping_interval: usize,
55 pub(super) ping_threshold: usize,
56 pub(super) latency_interval: usize,
57 pub(super) pool_limit: usize,
58 pub(super) pool_recv_timeout: Duration,
59 pub(super) testing: bool,
60 pub(super) runner: Arc<dyn Runner>,
61}
62
63thread_local!(
64 static CURRENT: RefCell<Option<System>> = const { RefCell::new(None) };
65);
66
67impl Clone for System {
68 fn clone(&self) -> Self {
69 Self(self.0.clone())
70 }
71}
72
73impl SystemInner {
74 pub(crate) fn arbiters(&self) -> Vec<Arbiter> {
76 self.arbiters.lock().list.clone()
77 }
78}
79
80impl System {
81 pub(super) fn start(config: SystemConfig) -> (Self, oneshot::Receiver<i32>) {
83 let id = SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst);
84 let (sender, receiver) = unbounded();
85
86 let pool = ThreadPool::new(&config.name, config.pool_limit, config.pool_recv_timeout);
87 let (arbiter, controller) = Arbiter::new_system(id, config.name.clone());
88
89 let mut arbiters = Arbiters::default();
90 arbiters.all.insert(arbiter.id(), arbiter.clone());
91 arbiters.list.push(arbiter.clone());
92
93 let sys = System(Arc::new(SystemInner {
94 id,
95 config,
96 arbiter,
97 sender,
98 receiver,
99 pool,
100 arbiters: Mutex::new(arbiters),
101 storage: RwLock::new(HashMap::default()),
102 signals: AtomicBool::new(false),
103 }));
104 System::set_current(sys.clone());
105
106 let (stop_tx, stop) = oneshot::channel();
107
108 crate::spawn_detached(SystemSupport::new(&sys, stop_tx).run());
110 crate::spawn_detached(controller.run(sys.clone()));
111
112 (sys, stop)
113 }
114
115 pub fn build() -> Builder {
119 Builder::new()
120 }
121
122 #[allow(clippy::new_ret_no_self)]
123 pub fn new<R: Runner>(name: &str, runner: R) -> SystemRunner {
129 Self::build().name(name).build(runner)
130 }
131
132 #[allow(clippy::new_ret_no_self)]
133 pub fn with_config(name: &str, mut config: SystemConfig) -> SystemRunner {
139 name.clone_into(&mut config.name);
140 Self::build().name(name).build_with(config)
141 }
142
143 pub fn current() -> System {
149 CURRENT.with(|cell| match *cell.borrow() {
150 Some(ref sys) => sys.clone(),
151 None => panic!("System is not running"),
152 })
153 }
154
155 pub fn try_current() -> Option<System> {
157 CURRENT.with(|cell| cell.borrow().as_ref().map(Clone::clone))
158 }
159
160 #[doc(hidden)]
162 pub fn set_current(sys: System) {
163 CURRENT.with(|s| {
164 *s.borrow_mut() = Some(sys);
165 });
166 }
167
168 pub(crate) fn register_arbiter(&self, arb: Arbiter) {
169 CURRENT.with(|s| {
170 *s.borrow_mut() = Some(self.clone());
171 });
172 let mut arbiters = self.0.arbiters.lock();
173 arbiters.all.insert(arb.id(), arb.clone());
174 arbiters.list.push(arb);
175 }
176
177 pub(crate) fn unregister_arbiter(&self, id: Id) {
178 CURRENT.with(|s| {
179 *s.borrow_mut() = None;
180 });
181 let mut arbiters = self.0.arbiters.lock();
182 if let Some(hnd) = arbiters.all.remove(&id) {
183 for (idx, arb) in arbiters.list.iter().enumerate() {
184 if &hnd == arb {
185 arbiters.list.remove(idx);
186 break;
187 }
188 }
189 }
190 }
191
192 pub(super) fn remove_current() {
193 CURRENT.with(|cell| {
194 cell.borrow_mut().take();
195 });
196 }
197
198 pub(super) fn take_arbiters(&self) -> Vec<Arbiter> {
200 let mut arbiters = self.0.arbiters.lock();
201 arbiters.all.clear();
202 std::mem::take(&mut arbiters.list)
203 }
204
205 pub(super) fn stop_arbiters(&self) {
207 for arb in self.take_arbiters() {
208 arb.stop();
209 }
210 }
211
212 pub fn id(&self) -> Id {
214 Id(self.0.id)
215 }
216
217 pub fn name(&self) -> &str {
219 &self.0.config.name
220 }
221
222 pub fn stop(&self) {
224 self.stop_with_code(0);
225 }
226
227 pub fn stop_with_code(&self, code: i32) {
229 let _ = self.0.sender.try_send(SystemCommand::Exit(code));
230 }
231
232 pub fn signals(&self) -> bool {
234 self.0.signals.load(Ordering::Relaxed)
235 }
236
237 pub fn enable_signals(&self) {
242 if !self.signals() && crate::signals::start(self) {
243 self.0.signals.store(true, Ordering::Relaxed);
244 }
245 }
246
247 pub fn disable_signals(&self) {
249 if self.signals() {
250 crate::signals::stop(self);
251 self.0.signals.store(false, Ordering::Relaxed);
252 }
253 }
254
255 pub fn arbiter(&self) -> Arbiter {
261 self.0.arbiter.clone()
262 }
263
264 pub fn list_arbiters<F, R>(&self, f: F) -> R
271 where
272 F: FnOnce(&[Arbiter]) -> R,
273 {
274 f(&self.0.arbiters())
275 }
276
277 pub fn list_arbiter_pings<F>(mut f: F)
282 where
283 F: FnMut(&Arbiter, &mut VecDeque<PingRecord>),
284 {
285 PINGS.with(|pings| {
286 let mut p = pings.borrow_mut();
287 let sys = System::current();
288 let arbiters: Vec<_> = {
289 let arbiters = sys.0.arbiters.lock();
290 p.keys()
291 .filter_map(|id| arbiters.all.get(id).map(|arb| (*id, arb.clone())))
292 .collect()
293 };
294
295 for (id, arb) in &arbiters {
297 if let Some(recs) = p.get_mut(id) {
298 f(arb, recs);
299 }
300 }
301 });
302 }
303
304 #[doc(hidden)]
305 pub fn set_latency_callback<F>(f: F)
313 where
314 F: Fn(ntex_error::Backtrace, Option<&'static std::panic::Location<'static>>)
315 + Send
316 + Sync
317 + 'static,
318 {
319 crate::watchdog::set_latency_callback(Arc::new(f));
320 }
321
322 pub fn config(&self) -> SystemConfig {
324 self.0.config.clone()
325 }
326
327 #[inline]
328 pub fn handle(&self) -> Handle {
330 self.arbiter().handle().clone()
331 }
332
333 pub fn testing(&self) -> bool {
335 self.0.config.testing()
336 }
337
338 pub fn spawn_blocking<F, R>(&self, f: F) -> BlockingResult<R>
345 where
346 F: FnOnce() -> R + Send + 'static,
347 R: Send + 'static,
348 {
349 self.0.pool.execute(f)
350 }
351
352 pub fn pool_metrics(&self) -> PoolMetrics {
354 self.0.pool.metrics()
355 }
356
357 pub fn get_value<T>(&self, f: impl FnOnce() -> T) -> T
362 where
363 T: Clone + Send + Sync + 'static,
364 {
365 if let Some(boxed) = self.0.storage.read().get(&TypeId::of::<T>())
366 && let Some(val) = (&**boxed as &(dyn Any + 'static)).downcast_ref::<T>()
367 {
368 val.clone()
369 } else {
370 let val = f();
371 self.0
372 .storage
373 .write()
374 .insert(TypeId::of::<T>(), Box::new(val.clone()));
375 val
376 }
377 }
378}
379
380impl SystemConfig {
381 #[inline]
382 pub fn testing(&self) -> bool {
384 self.testing
385 }
386}
387
388impl fmt::Debug for System {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 f.debug_struct("System")
391 .field("id", &self.0.id)
392 .field("config", &self.0.config)
393 .field("signals", &self.signals())
394 .field("pool", &self.0.pool)
395 .finish()
396 }
397}
398
399impl fmt::Debug for SystemConfig {
400 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401 f.debug_struct("SystemConfig")
402 .field("name", &self.name)
403 .field("testing", &self.testing)
404 .field("stack_size", &self.stack_size)
405 .finish()
406 }
407}
408
409#[derive(Debug)]
410pub(super) enum SystemCommand {
411 Exit(i32),
412}
413
414#[derive(Debug)]
415struct SystemSupport {
416 sys: System,
417 stop: Option<oneshot::Sender<i32>>,
418 commands: Receiver<SystemCommand>,
419 watchdog: Option<Arc<AtomicBool>>,
420}
421
422impl Drop for SystemSupport {
423 fn drop(&mut self) {
424 self.stop_watchdog();
425 }
426}
427
428impl SystemSupport {
429 fn new(sys: &System, stop: oneshot::Sender<i32>) -> Self {
430 Self {
431 sys: sys.clone(),
432 stop: Some(stop),
433 commands: sys.0.receiver.clone(),
434 watchdog: None,
435 }
436 }
437
438 fn stop_watchdog(&mut self) {
439 if let Some(stop) = self.watchdog.take() {
440 stop.store(true, Ordering::Relaxed);
441 }
442 }
443
444 async fn run(mut self) {
445 crate::metrics::start_latency_sampler(
446 &self.sys.0.arbiter.0.latency,
447 self.sys.0.config.latency_interval,
448 );
449 self.watchdog = crate::watchdog::start(&self.sys);
450 if self.sys.0.config.ping_interval != 0 {
451 crate::spawn_detached(ping_arbiters(self.sys.clone()));
452 }
453
454 loop {
455 match self.commands.recv().await {
456 Ok(SystemCommand::Exit(code)) => {
457 log::debug!("Stopping system with {code} code");
458 self.stop_watchdog();
459 self.sys.stop_arbiters();
460
461 crate::arbiter::run_shutdown_callbacks();
462
463 if let Some(stop) = self.stop.take() {
465 let _ = stop.send(code);
466 }
467 }
468 Err(_) => {
469 log::debug!("System stopped");
470 return;
471 }
472 }
473 }
474 }
475}
476
477#[derive(Copy, Clone, Debug)]
478pub struct PingRecord {
479 pub start: Instant,
481 pub rtt: Option<Duration>,
483}
484
485async fn ping_arbiters(sys: System) {
486 let interval = Duration::from_millis(sys.0.config.ping_interval as u64);
487
488 loop {
489 Delay::new(interval).await;
491
492 let start = Instant::now();
493 let arbiters = sys.0.arbiters.lock();
494
495 PINGS.with(|pings| {
497 pings
498 .borrow_mut()
499 .retain(|id, _| arbiters.all.contains_key(id));
500 });
501
502 for arb in &arbiters.list {
503 let id = arb.id();
504 let fut = arb.handle().spawn(async move {
505 yield_to().await;
506 });
507
508 PINGS.with(|pings| {
509 let mut p = pings.borrow_mut();
510 let recs = p.entry(id).or_default();
511 recs.push_front(PingRecord { start, rtt: None });
512 recs.truncate(10);
513 });
514
515 crate::spawn_detached(async move {
516 if fut.await.is_ok() {
517 PINGS.with(|pings| {
518 if let Some(recs) = pings.borrow_mut().get_mut(&id)
520 && let Some(rec) = recs.iter_mut().find(|r| r.start == start)
521 {
522 rec.rtt = Some(start.elapsed());
523 }
524 });
525 }
526 });
527 }
528 }
529}
530
531async fn yield_to() {
532 use std::task::{Context, Poll};
533
534 struct Yield {
535 completed: bool,
536 }
537
538 impl Future for Yield {
539 type Output = ();
540
541 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
542 if self.completed {
543 return Poll::Ready(());
544 }
545 self.completed = true;
546 cx.waker().wake_by_ref();
547 Poll::Pending
548 }
549 }
550
551 Yield { completed: false }.await;
552}
553
554#[cfg(test)]
555mod ping_tests {
556 use super::*;
557 use crate::testing::TestRunner;
558
559 #[test]
560 fn ping_records_pruned() {
561 System::build()
562 .name("test")
563 .ping_interval(5)
564 .build(TestRunner)
565 .block_on(async {
566 let mut arb = Arbiter::new();
567 let id = arb.id();
568 let answered = || {
569 PINGS.with(|p| {
570 p.borrow()
571 .get(&id)
572 .is_some_and(|recs| recs.iter().any(|r| r.rtt.is_some()))
573 })
574 };
575 let has = || PINGS.with(|p| p.borrow().contains_key(&id));
576
577 for _ in 0..400 {
578 if answered() {
579 break;
580 }
581 Delay::new(Duration::from_millis(5)).await;
582 }
583 assert!(answered());
584
585 arb.stop();
586 arb.join().unwrap();
587 for _ in 0..400 {
588 if !has() {
589 break;
590 }
591 Delay::new(Duration::from_millis(5)).await;
592 }
593 assert!(!has());
594 });
595 }
596
597 #[test]
598 fn latency_sampling() {
599 System::build()
600 .name("test")
601 .latency_interval(5)
602 .build(TestRunner)
603 .block_on(async {
604 let mut arb = Arbiter::new();
605 let sampled = |arb: &Arbiter| arb.latency().unwrap().histogram.count() >= 2;
606 let sys_arb = System::current().arbiter();
607
608 for _ in 0..400 {
609 if sampled(&arb) && sampled(&sys_arb) {
610 break;
611 }
612 Delay::new(Duration::from_millis(5)).await;
613 }
614 assert!(sampled(&arb));
615 assert!(sampled(&sys_arb));
616
617 let m = arb.latency().unwrap();
618 assert_eq!(m.interval, Duration::from_millis(5));
619 assert!(m.last_sample.unwrap() <= Instant::now());
620
621 arb.stop();
622 arb.join().unwrap();
623 });
624
625 System::build()
626 .name("test")
627 .latency_interval(0)
628 .build(TestRunner)
629 .block_on(async {
630 assert_eq!(System::current().arbiter().latency(), None);
631 let mut arb = Arbiter::new();
632 assert_eq!(arb.latency(), None);
633 arb.stop();
634 arb.join().unwrap();
635 });
636 }
637}
638
639#[cfg(test)]
640mod api_tests {
641 use std::sync::atomic::{AtomicUsize, Ordering};
642
643 use super::*;
644 use crate::testing::TestRunner;
645
646 #[test]
647 #[should_panic(expected = "System is not running")]
648 fn current_without_system() {
649 assert!(System::try_current().is_none());
650 let _ = System::current();
651 }
652
653 #[test]
654 fn system_api() {
655 let config = System::new("base", TestRunner).block_on(async {
656 let sys = System::current();
657 assert_eq!(sys.name(), "base");
658 assert!(!sys.testing());
659 assert!(!sys.signals());
660 assert_eq!(System::try_current().unwrap().id(), sys.id());
661 assert_eq!(sys.arbiter(), Arbiter::current());
662 assert!(format!("{sys:?}").contains("base"));
663 assert!(format!("{:?}", sys.config()).contains("base"));
664
665 assert_eq!(sys.get_value(|| 1usize), 1);
667 assert_eq!(sys.get_value(|| 2usize), 1);
668
669 assert_eq!(sys.spawn_blocking(|| 10).await, Ok(10));
670
671 let cnt = Arc::new(AtomicUsize::new(0));
672 let cnt2 = cnt.clone();
673 sys.handle()
674 .spawn(async move {
675 cnt2.fetch_add(1, Ordering::Relaxed);
676 })
677 .await
678 .unwrap();
679 assert_eq!(cnt.load(Ordering::Relaxed), 1);
680 sys.list_arbiters(|arbs| assert_eq!(arbs, [sys.arbiter()]));
681 sys.config()
682 });
683 assert!(System::try_current().is_none());
684
685 System::with_config("copy", config).block_on(async {
686 let sys = System::current();
687 assert_eq!(sys.name(), "copy");
688 assert_eq!(Arbiter::current().name(), "copy");
689 });
690 }
691
692 #[test]
693 fn list_arbiter_pings() {
694 System::build()
695 .name("test")
696 .ping_interval(5)
697 .build(TestRunner)
698 .block_on(async {
699 let mut arb = Arbiter::new();
700 let visited = || {
701 let mut found = false;
702 System::list_arbiter_pings(|a, recs| {
703 if a == &arb && recs.iter().any(|r| r.rtt.is_some()) {
704 found = true;
705 }
706 });
707 found
708 };
709 for _ in 0..400 {
710 if visited() {
711 break;
712 }
713 Delay::new(Duration::from_millis(5)).await;
714 }
715 assert!(visited());
716
717 let mut visited = false;
719 System::list_arbiter_pings(|_, _| {
720 visited = true;
721 assert!(System::current().0.arbiters.try_lock().is_some());
722 });
723 assert!(visited);
724
725 arb.stop();
726 arb.join().unwrap();
727 });
728 }
729
730 #[test]
731 fn list_arbiters_callback_joins_worker() {
732 System::new("list-join", TestRunner).block_on(async {
733 let sys = System::current();
734 let worker = Arbiter::new();
735 sys.list_arbiters(|arbs| {
736 assert!(sys.0.arbiters.try_lock().is_some());
738 let mut arb = arbs.iter().find(|arb| **arb == worker).unwrap().clone();
739 arb.stop();
740 arb.join().unwrap();
741 sys.list_arbiters(|arbs| assert!(!arbs.contains(&worker)));
742 });
743 });
744 }
745}