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, panic, pin::Pin, rc::Rc};
6
7use async_channel::{Receiver, Sender, unbounded};
8use futures_timer::Delay;
9use parking_lot::{Mutex, RwLock};
10
11use crate::arbiter::Arbiter;
12use crate::pool::ThreadPool;
13use crate::{BlockingResult, Builder, Handle, HashMap, HashSet, 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(Arc<SystemInner>);
36
37struct SystemInner {
38 id: usize,
39 arbiter: Arbiter,
40 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 #[allow(dead_code)]
56 pub(super) ping_threshold: 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 System {
74 pub(super) fn start(config: SystemConfig) -> (Self, oneshot::Receiver<i32>) {
76 let id = SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst);
77 let (sender, receiver) = unbounded();
78
79 let pool = ThreadPool::new(&config.name, config.pool_limit, config.pool_recv_timeout);
80 let (arbiter, controller) = Arbiter::new_system(id, config.name.clone());
81
82 let mut arbiters = Arbiters::default();
83 arbiters.all.insert(arbiter.id(), arbiter.clone());
84 arbiters.list.push(arbiter.clone());
85
86 let sys = System(Arc::new(SystemInner {
87 id,
88 config,
89 arbiter,
90 sender,
91 receiver,
92 pool,
93 arbiters: Mutex::new(arbiters),
94 storage: RwLock::new(HashMap::default()),
95 signals: AtomicBool::new(false),
96 }));
97 System::set_current(sys.clone());
98
99 let (stop_tx, stop) = oneshot::channel();
100
101 crate::spawn(SystemSupport::new(&sys, stop_tx).run());
103 crate::spawn(controller.run(sys.clone()));
104
105 (sys, stop)
106 }
107
108 pub fn build() -> Builder {
112 Builder::new()
113 }
114
115 #[allow(clippy::new_ret_no_self)]
116 pub fn new<R: Runner>(name: &str, runner: R) -> SystemRunner {
122 Self::build().name(name).build(runner)
123 }
124
125 #[allow(clippy::new_ret_no_self)]
126 pub fn with_config(name: &str, config: SystemConfig) -> SystemRunner {
132 Self::build().name(name).build_with(config)
133 }
134
135 pub fn current() -> System {
141 CURRENT.with(|cell| match *cell.borrow() {
142 Some(ref sys) => sys.clone(),
143 None => panic!("System is not running"),
144 })
145 }
146
147 pub fn try_current() -> Option<System> {
149 CURRENT.with(|cell| cell.borrow().as_ref().map(Clone::clone))
150 }
151
152 #[doc(hidden)]
154 pub fn set_current(sys: System) {
155 CURRENT.with(|s| {
156 *s.borrow_mut() = Some(sys);
157 });
158 }
159
160 pub(crate) fn register_arbiter(&self, arb: Arbiter) {
161 CURRENT.with(|s| {
162 *s.borrow_mut() = Some(self.clone());
163 });
164 let mut arbiters = self.0.arbiters.lock();
165 arbiters.all.insert(arb.id(), arb.clone());
166 arbiters.list.push(arb);
167 }
168
169 pub(crate) fn unregister_arbiter(&self, id: Id) {
170 CURRENT.with(|s| {
171 *s.borrow_mut() = None;
172 });
173 let mut arbiters = self.0.arbiters.lock();
174 if let Some(hnd) = arbiters.all.remove(&id) {
175 for (idx, arb) in arbiters.list.iter().enumerate() {
176 if &hnd == arb {
177 arbiters.list.remove(idx);
178 break;
179 }
180 }
181 }
182 }
183
184 pub(super) fn remove_current() {
185 CURRENT.with(|cell| {
186 cell.borrow_mut().take();
187 });
188 }
189
190 pub fn id(&self) -> Id {
192 Id(self.0.id)
193 }
194
195 pub fn name(&self) -> &str {
197 &self.0.config.name
198 }
199
200 pub fn stop(&self) {
202 self.stop_with_code(0);
203 }
204
205 pub fn stop_with_code(&self, code: i32) {
207 let _ = self.0.sender.try_send(SystemCommand::Exit(code));
208 }
209
210 #[doc(hidden)]
211 #[deprecated(since = "3.17.0")]
212 pub fn stop_on_panic(&self) -> bool {
217 false
218 }
219
220 pub fn signals(&self) -> bool {
222 self.0.signals.load(Ordering::Relaxed)
223 }
224
225 pub fn enable_signals(&self) {
227 if !self.signals() {
228 crate::signals::start(self);
229 self.0.signals.store(true, Ordering::Relaxed);
230 }
231 }
232
233 pub fn disable_signals(&self) {
235 if self.signals() {
236 crate::signals::stop(self);
237 self.0.signals.store(false, Ordering::Relaxed);
238 }
239 }
240
241 pub fn arbiter(&self) -> Arbiter {
247 self.0.arbiter.clone()
248 }
249
250 pub fn list_arbiters<F, R>(&self, f: F) -> R
255 where
256 F: FnOnce(&[Arbiter]) -> R,
257 {
258 f(&self.0.arbiters.lock().list)
259 }
260
261 pub fn list_arbiter_pings<F>(mut f: F)
266 where
267 F: FnMut(&Arbiter, &mut VecDeque<PingRecord>),
268 {
269 PINGS.with(|pings| {
270 let mut p = pings.borrow_mut();
271 let sys = System::current();
272 let arbiters = sys.0.arbiters.lock();
273
274 for (id, recs) in &mut *p {
275 if let Some(arb) = arbiters.all.get(id) {
276 f(arb, recs);
277 }
278 }
279 });
280 }
281
282 #[cfg(target_os = "linux")]
283 #[doc(hidden)]
284 pub fn set_latency_callback<F: Fn(ntex_error::Backtrace) + 'static>(f: F) {
291 unsafe {
292 ARB_CB = Some(Box::new(f));
293 }
294 }
295
296 pub fn config(&self) -> SystemConfig {
298 self.0.config.clone()
299 }
300
301 #[inline]
302 pub fn handle(&self) -> Handle {
304 self.arbiter().handle().clone()
305 }
306
307 pub fn testing(&self) -> bool {
309 self.0.config.testing()
310 }
311
312 pub fn spawn_blocking<F, R>(&self, f: F) -> BlockingResult<R>
319 where
320 F: FnOnce() -> R + Send + 'static,
321 R: Send + 'static,
322 {
323 self.0.pool.execute(f)
324 }
325
326 pub fn get_value<T>(&self, f: impl FnOnce() -> T) -> T
331 where
332 T: Clone + Send + Sync + 'static,
333 {
334 if let Some(boxed) = self.0.storage.read().get(&TypeId::of::<T>())
335 && let Some(val) = (&**boxed as &(dyn Any + 'static)).downcast_ref::<T>()
336 {
337 val.clone()
338 } else {
339 let val = f();
340 self.0
341 .storage
342 .write()
343 .insert(TypeId::of::<T>(), Box::new(val.clone()));
344 val
345 }
346 }
347}
348
349impl SystemConfig {
350 #[inline]
351 pub fn testing(&self) -> bool {
353 self.testing
354 }
355}
356
357impl fmt::Debug for System {
358 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359 f.debug_struct("System")
360 .field("id", &self.0.id)
361 .field("config", &self.0.config)
362 .field("signals", &self.signals())
363 .field("pool", &self.0.pool)
364 .finish()
365 }
366}
367
368impl fmt::Debug for SystemConfig {
369 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370 f.debug_struct("SystemConfig")
371 .field("name", &self.name)
372 .field("testing", &self.testing)
373 .field("stack_size", &self.stack_size)
374 .finish()
375 }
376}
377
378#[derive(Debug)]
379pub(super) enum SystemCommand {
380 Exit(i32),
381}
382
383#[derive(Debug)]
384struct SystemSupport {
385 sys: System,
386 stop: Option<oneshot::Sender<i32>>,
387 commands: Receiver<SystemCommand>,
388}
389
390impl SystemSupport {
391 fn new(sys: &System, stop: oneshot::Sender<i32>) -> Self {
392 Self {
393 sys: sys.clone(),
394 stop: Some(stop),
395 commands: sys.0.receiver.clone(),
396 }
397 }
398
399 async fn run(mut self) {
400 if self.sys.0.config.ping_interval != 0 {
401 crate::spawn(ping_arbiters(self.sys.clone()));
402 }
403
404 loop {
405 match self.commands.recv().await {
406 Ok(SystemCommand::Exit(code)) => {
407 log::debug!("Stopping system with {code} code");
408
409 let mut arbiters = self.sys.0.arbiters.lock();
411 for arb in arbiters.list.drain(..) {
412 arb.stop();
413 }
414 arbiters.all.clear();
415
416 if let Some(stop) = self.stop.take() {
418 let _ = stop.send(code);
419 }
420 }
421 Err(_) => {
422 log::debug!("System stopped");
423 return;
424 }
425 }
426 }
427 }
428}
429
430#[derive(Copy, Clone, Debug)]
431pub struct PingRecord {
432 pub start: Instant,
434 pub rtt: Option<Duration>,
436}
437
438async fn ping_arbiters(sys: System) {
439 let arbs = Rc::new(RefCell::new(HashSet::default()));
440 let interval = Duration::from_millis(sys.0.config.ping_interval as u64);
441 #[cfg(target_os = "linux")]
442 let threshold = Duration::from_millis(sys.0.config.ping_threshold as u64);
443
444 loop {
445 Delay::new(interval).await;
447
448 {
450 arbs.borrow_mut().clear();
451
452 let start = Instant::now();
453 let arbiters = sys.0.arbiters.lock();
454
455 for arb in &arbiters.list {
456 let id = arb.id();
457 let arbs = arbs.clone();
458 let fut = arb.handle().spawn(async move {
459 yield_to().await;
460 });
461
462 PINGS.with(|pings| {
464 let mut p = pings.borrow_mut();
465 let recs = p.entry(arb.id()).or_default();
466 recs.push_front(PingRecord { start, rtt: None });
467 recs.truncate(10);
468 });
469
470 crate::spawn(async move {
471 if fut.await.is_ok() {
472 arbs.borrow_mut().insert(id);
473
474 PINGS.with(|pings| {
475 if let Some(recs) = pings.borrow_mut().get_mut(&id)
476 && let Some(rec) = recs.front_mut()
477 {
478 rec.rtt = Some(start.elapsed());
479 }
480 });
481 }
482 });
483 }
484 }
485
486 #[cfg(target_os = "linux")]
488 {
489 const SPIN: Duration = Duration::from_micros(100);
490
491 Delay::new(threshold).await;
493
494 let mut no_pongs = Vec::new();
495 {
496 for arb in &sys.0.arbiters.lock().list {
497 let pong = arbs.borrow_mut().remove(&arb.id());
498 if !pong {
499 no_pongs.push(arb.clone());
500 }
501 }
502 }
503
504 if !crate::signals::is_enabled() {
505 continue;
506 }
507
508 for arb in no_pongs {
509 log::error!("Arbiter {}({:?}) did not return pong", arb.name(), arb.id());
511
512 *CAPTURED.lock() = None;
514 EXPECTED_TID.store(arb.tid(), Ordering::Release);
515 let result = unsafe {
516 libc::syscall(libc::SYS_tgkill, libc::getpid(), arb.tid(), libc::SIGUSR2)
517 };
518
519 if result == -1 {
520 log::error!(
521 "Unsable to send SIGUSR2 to arbiter {}({:?}): {}",
522 arb.name(),
523 arb.id(),
524 std::io::Error::last_os_error()
525 );
526 } else {
527 for _ in 0..1000 {
529 Delay::new(SPIN).await;
530 if let Some(bt) = CAPTURED.lock().take() {
531 let bt = ntex_error::Backtrace::from(bt);
532 #[allow(static_mut_refs)]
533 if let Some(f) = unsafe { ARB_CB.as_ref() } {
534 f(bt);
535 } else {
536 bt.resolver().resolve();
537 log::error!(
538 "Worker does not returned pong within {interval:?} time.\n{bt:?}"
539 );
540 }
541 break;
542 }
543 }
544 }
545 }
546 }
547 }
548}
549
550async fn yield_to() {
551 use std::task::{Context, Poll};
552
553 struct Yield {
554 completed: bool,
555 }
556
557 impl Future for Yield {
558 type Output = ();
559
560 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
561 if self.completed {
562 return Poll::Ready(());
563 }
564 self.completed = true;
565 cx.waker().wake_by_ref();
566 Poll::Pending
567 }
568 }
569
570 Yield { completed: false }.await;
571}
572
573#[cfg(target_os = "linux")]
574static mut ARB_CB: Option<Box<dyn Fn(ntex_error::Backtrace)>> = None;
575
576#[cfg(target_os = "linux")]
577static EXPECTED_TID: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
578#[cfg(target_os = "linux")]
579static CAPTURED: Mutex<Option<ntex_error::BacktraceRaw>> = Mutex::new(None);
580
581#[track_caller]
582#[cfg(target_family = "unix")]
583pub(crate) fn sig_usr2() {
584 #[cfg(target_os = "linux")]
585 #[allow(clippy::cast_possible_truncation)]
586 {
587 let tid = unsafe { libc::syscall(libc::SYS_gettid) } as i32;
588 if EXPECTED_TID.load(Ordering::Acquire) == tid {
589 let bt = ntex_error::BacktraceRaw::new(panic::Location::caller());
592 *CAPTURED.lock() = Some(bt);
593 }
594 }
595}