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>>> =
19 RefCell::new(HashMap::default());
20);
21
22#[derive(Default)]
23struct Arbiters {
24 all: HashMap<Id, Arbiter>,
25 list: Vec<Arbiter>,
26}
27
28#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
30pub struct Id(pub(crate) usize);
31
32pub struct System(Arc<SystemInner>);
34
35struct SystemInner {
36 id: usize,
37 arbiter: Arbiter,
38 config: SystemConfig,
39 sender: Sender<SystemCommand>,
40 receiver: Receiver<SystemCommand>,
41 storage: RwLock<HashMap<TypeId, Box<dyn Any + Sync + Send>>>,
42 arbiters: Mutex<Arbiters>,
43 signals: AtomicBool,
44 pool: ThreadPool,
45}
46
47#[derive(Clone)]
48pub struct SystemConfig {
49 pub(super) name: String,
50 pub(super) stack_size: usize,
51 pub(super) stop_on_panic: bool,
52 pub(super) ping_interval: usize,
53 #[allow(dead_code)]
54 pub(super) ping_threshold: usize,
55 pub(super) pool_limit: usize,
56 pub(super) pool_recv_timeout: Duration,
57 pub(super) testing: bool,
58 pub(super) runner: Arc<dyn Runner>,
59}
60
61thread_local!(
62 static CURRENT: RefCell<Option<System>> = const { RefCell::new(None) };
63);
64
65impl Clone for System {
66 fn clone(&self) -> Self {
67 Self(self.0.clone())
68 }
69}
70
71impl System {
72 pub(super) fn start(config: SystemConfig) -> (Self, oneshot::Receiver<i32>) {
74 let id = SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst);
75 let (sender, receiver) = unbounded();
76
77 let pool =
78 ThreadPool::new(&config.name, config.pool_limit, config.pool_recv_timeout);
79 let (arbiter, controller) = Arbiter::new_system(id, config.name.clone());
80
81 let mut arbiters = Arbiters::default();
82 arbiters.all.insert(arbiter.id(), arbiter.clone());
83 arbiters.list.push(arbiter.clone());
84
85 let sys = System(Arc::new(SystemInner {
86 id,
87 config,
88 arbiter,
89 sender,
90 receiver,
91 pool,
92 arbiters: Mutex::new(arbiters),
93 storage: RwLock::new(HashMap::default()),
94 signals: AtomicBool::new(false),
95 }));
96 System::set_current(sys.clone());
97
98 let (stop_tx, stop) = oneshot::channel();
99
100 crate::spawn(SystemSupport::new(&sys, stop_tx).run());
102 crate::spawn(controller.run(sys.clone()));
103
104 (sys, stop)
105 }
106
107 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 {
120 Self::build().name(name).build(runner)
121 }
122
123 #[allow(clippy::new_ret_no_self)]
124 pub fn with_config(name: &str, config: SystemConfig) -> SystemRunner {
128 Self::build().name(name).build_with(config)
129 }
130
131 pub fn current() -> System {
137 CURRENT.with(|cell| match *cell.borrow() {
138 Some(ref sys) => sys.clone(),
139 None => panic!("System is not running"),
140 })
141 }
142
143 pub fn try_current() -> Option<System> {
145 CURRENT.with(|cell| cell.borrow().as_ref().map(Clone::clone))
146 }
147
148 #[doc(hidden)]
150 pub fn set_current(sys: System) {
151 CURRENT.with(|s| {
152 *s.borrow_mut() = Some(sys);
153 });
154 }
155
156 pub(crate) fn register_arbiter(&self, arb: Arbiter) {
157 CURRENT.with(|s| {
158 *s.borrow_mut() = Some(self.clone());
159 });
160 let mut arbiters = self.0.arbiters.lock();
161 arbiters.all.insert(arb.id(), arb.clone());
162 arbiters.list.push(arb);
163 }
164
165 pub(crate) fn unregister_arbiter(&self, id: Id) {
166 CURRENT.with(|s| {
167 *s.borrow_mut() = None;
168 });
169 let mut arbiters = self.0.arbiters.lock();
170 if let Some(hnd) = arbiters.all.remove(&id) {
171 for (idx, arb) in arbiters.list.iter().enumerate() {
172 if &hnd == arb {
173 arbiters.list.remove(idx);
174 break;
175 }
176 }
177 }
178 }
179
180 pub(super) fn remove_current() {
181 CURRENT.with(|cell| {
182 cell.borrow_mut().take();
183 });
184 }
185
186 pub fn id(&self) -> Id {
188 Id(self.0.id)
189 }
190
191 pub fn name(&self) -> &str {
193 &self.0.config.name
194 }
195
196 pub fn stop(&self) {
198 self.stop_with_code(0);
199 }
200
201 pub fn stop_with_code(&self, code: i32) {
203 let _ = self.0.sender.try_send(SystemCommand::Exit(code));
204 }
205
206 pub fn stop_on_panic(&self) -> bool {
211 self.0.config.stop_on_panic
212 }
213
214 pub fn signals(&self) -> bool {
216 self.0.signals.load(Ordering::Relaxed)
217 }
218
219 pub fn enable_signals(&self) {
221 if !self.signals() {
222 crate::signals::start(self);
223 self.0.signals.store(true, Ordering::Relaxed);
224 }
225 }
226
227 pub fn disable_signals(&self) {
229 if self.signals() {
230 crate::signals::stop(self);
231 self.0.signals.store(false, Ordering::Relaxed);
232 }
233 }
234
235 pub fn arbiter(&self) -> Arbiter {
241 self.0.arbiter.clone()
242 }
243
244 pub fn list_arbiters<F, R>(&self, f: F) -> R
249 where
250 F: FnOnce(&[Arbiter]) -> R,
251 {
252 f(&self.0.arbiters.lock().list)
253 }
254
255 pub fn list_arbiter_pings<F>(mut f: F)
260 where
261 F: FnMut(&Arbiter, &mut VecDeque<PingRecord>),
262 {
263 PINGS.with(|pings| {
264 let mut p = pings.borrow_mut();
265 let sys = System::current();
266 let arbiters = sys.0.arbiters.lock();
267
268 for (id, recs) in &mut *p {
269 if let Some(arb) = arbiters.all.get(id) {
270 f(arb, recs);
271 }
272 }
273 });
274 }
275
276 #[cfg(target_os = "linux")]
277 #[doc(hidden)]
278 pub fn set_latency_callback<F: Fn(ntex_error::Backtrace) + 'static>(f: F) {
285 unsafe {
286 ARB_CB = Some(Box::new(f));
287 }
288 }
289
290 pub fn config(&self) -> SystemConfig {
292 self.0.config.clone()
293 }
294
295 #[inline]
296 pub fn handle(&self) -> Handle {
298 self.arbiter().handle().clone()
299 }
300
301 pub fn testing(&self) -> bool {
303 self.0.config.testing()
304 }
305
306 pub fn spawn_blocking<F, R>(&self, f: F) -> BlockingResult<R>
310 where
311 F: FnOnce() -> R + Send + 'static,
312 R: Send + 'static,
313 {
314 self.0.pool.execute(f)
315 }
316
317 pub fn get_value<T>(&self, f: impl FnOnce() -> T) -> T
322 where
323 T: Clone + Send + Sync + 'static,
324 {
325 if let Some(boxed) = self.0.storage.read().get(&TypeId::of::<T>())
326 && let Some(val) = (&**boxed as &(dyn Any + 'static)).downcast_ref::<T>()
327 {
328 val.clone()
329 } else {
330 let val = f();
331 self.0
332 .storage
333 .write()
334 .insert(TypeId::of::<T>(), Box::new(val.clone()));
335 val
336 }
337 }
338}
339
340impl SystemConfig {
341 #[inline]
342 pub fn testing(&self) -> bool {
344 self.testing
345 }
346}
347
348impl fmt::Debug for System {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 f.debug_struct("System")
351 .field("id", &self.0.id)
352 .field("config", &self.0.config)
353 .field("signals", &self.signals())
354 .field("pool", &self.0.pool)
355 .finish()
356 }
357}
358
359impl fmt::Debug for SystemConfig {
360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361 f.debug_struct("SystemConfig")
362 .field("name", &self.name)
363 .field("testing", &self.testing)
364 .field("stack_size", &self.stack_size)
365 .field("stop_on_panic", &self.stop_on_panic)
366 .finish()
367 }
368}
369
370#[derive(Debug)]
371pub(super) enum SystemCommand {
372 Exit(i32),
373}
374
375#[derive(Debug)]
376struct SystemSupport {
377 sys: System,
378 stop: Option<oneshot::Sender<i32>>,
379 commands: Receiver<SystemCommand>,
380}
381
382impl SystemSupport {
383 fn new(sys: &System, stop: oneshot::Sender<i32>) -> Self {
384 Self {
385 sys: sys.clone(),
386 stop: Some(stop),
387 commands: sys.0.receiver.clone(),
388 }
389 }
390
391 async fn run(mut self) {
392 if self.sys.0.config.ping_interval != 0 {
393 crate::spawn(ping_arbiters(self.sys.clone()));
394 }
395
396 loop {
397 match self.commands.recv().await {
398 Ok(SystemCommand::Exit(code)) => {
399 log::debug!("Stopping system with {code} code");
400
401 let mut arbiters = self.sys.0.arbiters.lock();
403 for arb in arbiters.list.drain(..) {
404 arb.stop();
405 }
406 arbiters.all.clear();
407
408 if let Some(stop) = self.stop.take() {
410 let _ = stop.send(code);
411 }
412 }
413 Err(_) => {
414 log::debug!("System stopped");
415 return;
416 }
417 }
418 }
419 }
420}
421
422#[derive(Copy, Clone, Debug)]
423pub struct PingRecord {
424 pub start: Instant,
426 pub rtt: Option<Duration>,
428}
429
430async fn ping_arbiters(sys: System) {
431 let pings = Rc::new(RefCell::new(HashSet::default()));
432 let interval = Duration::from_millis(sys.0.config.ping_interval as u64);
433 #[cfg(target_os = "linux")]
434 let threshold = Duration::from_millis(sys.0.config.ping_threshold as u64);
435
436 loop {
437 Delay::new(interval).await;
439
440 {
442 pings.borrow_mut().clear();
443
444 let start = Instant::now();
445 let arbiters = sys.0.arbiters.lock();
446
447 for arb in &arbiters.list {
448 let id = arb.id();
449 let pings = pings.clone();
450 let fut = arb.handle().spawn(async move {
451 yield_to().await;
452 });
453
454 PINGS.with(|pings| {
456 let mut p = pings.borrow_mut();
457 let recs = p.entry(arb.id()).or_default();
458 recs.push_front(PingRecord { start, rtt: None });
459 recs.truncate(10);
460 });
461
462 crate::spawn(async move {
463 if fut.await.is_ok() {
464 pings.borrow_mut().insert(id);
465
466 PINGS.with(|pings| {
467 pings
468 .borrow_mut()
469 .get_mut(&id)
470 .unwrap()
471 .front_mut()
472 .unwrap()
473 .rtt = Some(start.elapsed());
474 });
475 }
476 });
477 }
478 }
479
480 #[cfg(target_os = "linux")]
482 {
483 const SPIN: Duration = Duration::from_micros(100);
484
485 Delay::new(threshold).await;
487
488 let mut no_pongs = Vec::new();
489 {
490 for arb in &sys.0.arbiters.lock().list {
491 let pong = pings.borrow_mut().remove(&arb.id());
492 if !pong {
493 no_pongs.push(arb.clone());
494 }
495 }
496 }
497
498 if !crate::signals::is_enabled() {
499 continue;
500 }
501
502 for arb in no_pongs {
503 log::error!("Arbiter {}({:?}) did not return pong", arb.name(), arb.id());
505
506 *CAPTURED.lock() = None;
508 EXPECTED_TID.store(arb.tid(), Ordering::Release);
509 let result = unsafe {
510 libc::syscall(
511 libc::SYS_tgkill,
512 libc::getpid(),
513 arb.tid(),
514 libc::SIGUSR2,
515 )
516 };
517
518 if result == -1 {
519 log::error!(
520 "Unsable to send SIGUSR2 to arbiter {}({:?}): {}",
521 arb.name(),
522 arb.id(),
523 std::io::Error::last_os_error()
524 );
525 } else {
526 for _ in 0..1000 {
528 Delay::new(SPIN).await;
529 if let Some(bt) = CAPTURED.lock().take() {
530 let bt = ntex_error::Backtrace::from(bt);
531 #[allow(static_mut_refs)]
532 if let Some(f) = unsafe { ARB_CB.as_ref() } {
533 f(bt);
534 } else {
535 bt.resolver().resolve();
536 log::error!(
537 "Worker does not returned pong within {interval:?} time.\n{bt:?}"
538 );
539 }
540 break;
541 }
542 }
543 }
544 }
545 }
546 }
547}
548
549async fn yield_to() {
550 use std::task::{Context, Poll};
551
552 struct Yield {
553 completed: bool,
554 }
555
556 impl Future for Yield {
557 type Output = ();
558
559 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
560 if self.completed {
561 return Poll::Ready(());
562 }
563 self.completed = true;
564 cx.waker().wake_by_ref();
565 Poll::Pending
566 }
567 }
568
569 Yield { completed: false }.await;
570}
571
572#[cfg(target_os = "linux")]
573static mut ARB_CB: Option<Box<dyn Fn(ntex_error::Backtrace)>> = None;
574
575#[cfg(target_os = "linux")]
576static EXPECTED_TID: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
577#[cfg(target_os = "linux")]
578static CAPTURED: Mutex<Option<ntex_error::BacktraceRaw>> = Mutex::new(None);
579
580#[track_caller]
581#[cfg(target_family = "unix")]
582pub(crate) fn sig_usr2() {
583 #[cfg(target_os = "linux")]
584 #[allow(clippy::cast_possible_truncation)]
585 {
586 let tid = unsafe { libc::syscall(libc::SYS_gettid) } as i32;
587 if EXPECTED_TID.load(Ordering::Acquire) == tid {
588 let bt = ntex_error::BacktraceRaw::new(panic::Location::caller());
591 *CAPTURED.lock() = Some(bt);
592 }
593 }
594}