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>);
33
34struct SystemInner {
35 id: usize,
36 arbiter: Arbiter,
37 config: SystemConfig,
38 sender: Sender<SystemCommand>,
39 receiver: Receiver<SystemCommand>,
40 storage: RwLock<HashMap<TypeId, Box<dyn Any + Sync + Send>>>,
41 arbiters: Mutex<Arbiters>,
42 signals: AtomicBool,
43 pool: ThreadPool,
44}
45
46#[derive(Clone)]
47pub struct SystemConfig {
48 pub(super) name: String,
49 pub(super) stack_size: usize,
50 pub(super) ping_interval: usize,
51 #[allow(dead_code)]
52 pub(super) ping_threshold: usize,
53 pub(super) pool_limit: usize,
54 pub(super) pool_recv_timeout: Duration,
55 pub(super) testing: bool,
56 pub(super) runner: Arc<dyn Runner>,
57}
58
59thread_local!(
60 static CURRENT: RefCell<Option<System>> = const { RefCell::new(None) };
61);
62
63impl Clone for System {
64 fn clone(&self) -> Self {
65 Self(self.0.clone())
66 }
67}
68
69impl System {
70 pub(super) fn start(config: SystemConfig) -> (Self, oneshot::Receiver<i32>) {
72 let id = SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst);
73 let (sender, receiver) = unbounded();
74
75 let pool = ThreadPool::new(&config.name, config.pool_limit, config.pool_recv_timeout);
76 let (arbiter, controller) = Arbiter::new_system(id, config.name.clone());
77
78 let mut arbiters = Arbiters::default();
79 arbiters.all.insert(arbiter.id(), arbiter.clone());
80 arbiters.list.push(arbiter.clone());
81
82 let sys = System(Arc::new(SystemInner {
83 id,
84 config,
85 arbiter,
86 sender,
87 receiver,
88 pool,
89 arbiters: Mutex::new(arbiters),
90 storage: RwLock::new(HashMap::default()),
91 signals: AtomicBool::new(false),
92 }));
93 System::set_current(sys.clone());
94
95 let (stop_tx, stop) = oneshot::channel();
96
97 crate::spawn(SystemSupport::new(&sys, stop_tx).run());
99 crate::spawn(controller.run(sys.clone()));
100
101 (sys, stop)
102 }
103
104 pub fn build() -> Builder {
109 Builder::new()
110 }
111
112 #[allow(clippy::new_ret_no_self)]
113 pub fn new<R: Runner>(name: &str, runner: R) -> SystemRunner {
117 Self::build().name(name).build(runner)
118 }
119
120 #[allow(clippy::new_ret_no_self)]
121 pub fn with_config(name: &str, config: SystemConfig) -> SystemRunner {
125 Self::build().name(name).build_with(config)
126 }
127
128 pub fn current() -> System {
134 CURRENT.with(|cell| match *cell.borrow() {
135 Some(ref sys) => sys.clone(),
136 None => panic!("System is not running"),
137 })
138 }
139
140 pub fn try_current() -> Option<System> {
142 CURRENT.with(|cell| cell.borrow().as_ref().map(Clone::clone))
143 }
144
145 #[doc(hidden)]
147 pub fn set_current(sys: System) {
148 CURRENT.with(|s| {
149 *s.borrow_mut() = Some(sys);
150 });
151 }
152
153 pub(crate) fn register_arbiter(&self, arb: Arbiter) {
154 CURRENT.with(|s| {
155 *s.borrow_mut() = Some(self.clone());
156 });
157 let mut arbiters = self.0.arbiters.lock();
158 arbiters.all.insert(arb.id(), arb.clone());
159 arbiters.list.push(arb);
160 }
161
162 pub(crate) fn unregister_arbiter(&self, id: Id) {
163 CURRENT.with(|s| {
164 *s.borrow_mut() = None;
165 });
166 let mut arbiters = self.0.arbiters.lock();
167 if let Some(hnd) = arbiters.all.remove(&id) {
168 for (idx, arb) in arbiters.list.iter().enumerate() {
169 if &hnd == arb {
170 arbiters.list.remove(idx);
171 break;
172 }
173 }
174 }
175 }
176
177 pub(super) fn remove_current() {
178 CURRENT.with(|cell| {
179 cell.borrow_mut().take();
180 });
181 }
182
183 pub fn id(&self) -> Id {
185 Id(self.0.id)
186 }
187
188 pub fn name(&self) -> &str {
190 &self.0.config.name
191 }
192
193 pub fn stop(&self) {
195 self.stop_with_code(0);
196 }
197
198 pub fn stop_with_code(&self, code: i32) {
200 let _ = self.0.sender.try_send(SystemCommand::Exit(code));
201 }
202
203 #[doc(hidden)]
204 #[deprecated(since = "3.17.0")]
205 pub fn stop_on_panic(&self) -> bool {
210 false
211 }
212
213 pub fn signals(&self) -> bool {
215 self.0.signals.load(Ordering::Relaxed)
216 }
217
218 pub fn enable_signals(&self) {
220 if !self.signals() {
221 crate::signals::start(self);
222 self.0.signals.store(true, Ordering::Relaxed);
223 }
224 }
225
226 pub fn disable_signals(&self) {
228 if self.signals() {
229 crate::signals::stop(self);
230 self.0.signals.store(false, Ordering::Relaxed);
231 }
232 }
233
234 pub fn arbiter(&self) -> Arbiter {
240 self.0.arbiter.clone()
241 }
242
243 pub fn list_arbiters<F, R>(&self, f: F) -> R
248 where
249 F: FnOnce(&[Arbiter]) -> R,
250 {
251 f(&self.0.arbiters.lock().list)
252 }
253
254 pub fn list_arbiter_pings<F>(mut f: F)
259 where
260 F: FnMut(&Arbiter, &mut VecDeque<PingRecord>),
261 {
262 PINGS.with(|pings| {
263 let mut p = pings.borrow_mut();
264 let sys = System::current();
265 let arbiters = sys.0.arbiters.lock();
266
267 for (id, recs) in &mut *p {
268 if let Some(arb) = arbiters.all.get(id) {
269 f(arb, recs);
270 }
271 }
272 });
273 }
274
275 #[cfg(target_os = "linux")]
276 #[doc(hidden)]
277 pub fn set_latency_callback<F: Fn(ntex_error::Backtrace) + 'static>(f: F) {
284 unsafe {
285 ARB_CB = Some(Box::new(f));
286 }
287 }
288
289 pub fn config(&self) -> SystemConfig {
291 self.0.config.clone()
292 }
293
294 #[inline]
295 pub fn handle(&self) -> Handle {
297 self.arbiter().handle().clone()
298 }
299
300 pub fn testing(&self) -> bool {
302 self.0.config.testing()
303 }
304
305 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 .finish()
366 }
367}
368
369#[derive(Debug)]
370pub(super) enum SystemCommand {
371 Exit(i32),
372}
373
374#[derive(Debug)]
375struct SystemSupport {
376 sys: System,
377 stop: Option<oneshot::Sender<i32>>,
378 commands: Receiver<SystemCommand>,
379}
380
381impl SystemSupport {
382 fn new(sys: &System, stop: oneshot::Sender<i32>) -> Self {
383 Self {
384 sys: sys.clone(),
385 stop: Some(stop),
386 commands: sys.0.receiver.clone(),
387 }
388 }
389
390 async fn run(mut self) {
391 if self.sys.0.config.ping_interval != 0 {
392 crate::spawn(ping_arbiters(self.sys.clone()));
393 }
394
395 loop {
396 match self.commands.recv().await {
397 Ok(SystemCommand::Exit(code)) => {
398 log::debug!("Stopping system with {code} code");
399
400 let mut arbiters = self.sys.0.arbiters.lock();
402 for arb in arbiters.list.drain(..) {
403 arb.stop();
404 }
405 arbiters.all.clear();
406
407 if let Some(stop) = self.stop.take() {
409 let _ = stop.send(code);
410 }
411 }
412 Err(_) => {
413 log::debug!("System stopped");
414 return;
415 }
416 }
417 }
418 }
419}
420
421#[derive(Copy, Clone, Debug)]
422pub struct PingRecord {
423 pub start: Instant,
425 pub rtt: Option<Duration>,
427}
428
429async fn ping_arbiters(sys: System) {
430 let arbs = Rc::new(RefCell::new(HashSet::default()));
431 let interval = Duration::from_millis(sys.0.config.ping_interval as u64);
432 #[cfg(target_os = "linux")]
433 let threshold = Duration::from_millis(sys.0.config.ping_threshold as u64);
434
435 loop {
436 Delay::new(interval).await;
438
439 {
441 arbs.borrow_mut().clear();
442
443 let start = Instant::now();
444 let arbiters = sys.0.arbiters.lock();
445
446 for arb in &arbiters.list {
447 let id = arb.id();
448 let arbs = arbs.clone();
449 let fut = arb.handle().spawn(async move {
450 yield_to().await;
451 });
452
453 PINGS.with(|pings| {
455 let mut p = pings.borrow_mut();
456 let recs = p.entry(arb.id()).or_default();
457 recs.push_front(PingRecord { start, rtt: None });
458 recs.truncate(10);
459 });
460
461 crate::spawn(async move {
462 if fut.await.is_ok() {
463 arbs.borrow_mut().insert(id);
464
465 PINGS.with(|pings| {
466 if let Some(recs) = pings.borrow_mut().get_mut(&id)
467 && let Some(rec) = recs.front_mut()
468 {
469 rec.rtt = Some(start.elapsed());
470 }
471 });
472 }
473 });
474 }
475 }
476
477 #[cfg(target_os = "linux")]
479 {
480 const SPIN: Duration = Duration::from_micros(100);
481
482 Delay::new(threshold).await;
484
485 let mut no_pongs = Vec::new();
486 {
487 for arb in &sys.0.arbiters.lock().list {
488 let pong = arbs.borrow_mut().remove(&arb.id());
489 if !pong {
490 no_pongs.push(arb.clone());
491 }
492 }
493 }
494
495 if !crate::signals::is_enabled() {
496 continue;
497 }
498
499 for arb in no_pongs {
500 log::error!("Arbiter {}({:?}) did not return pong", arb.name(), arb.id());
502
503 *CAPTURED.lock() = None;
505 EXPECTED_TID.store(arb.tid(), Ordering::Release);
506 let result = unsafe {
507 libc::syscall(libc::SYS_tgkill, libc::getpid(), arb.tid(), libc::SIGUSR2)
508 };
509
510 if result == -1 {
511 log::error!(
512 "Unsable to send SIGUSR2 to arbiter {}({:?}): {}",
513 arb.name(),
514 arb.id(),
515 std::io::Error::last_os_error()
516 );
517 } else {
518 for _ in 0..1000 {
520 Delay::new(SPIN).await;
521 if let Some(bt) = CAPTURED.lock().take() {
522 let bt = ntex_error::Backtrace::from(bt);
523 #[allow(static_mut_refs)]
524 if let Some(f) = unsafe { ARB_CB.as_ref() } {
525 f(bt);
526 } else {
527 bt.resolver().resolve();
528 log::error!(
529 "Worker does not returned pong within {interval:?} time.\n{bt:?}"
530 );
531 }
532 break;
533 }
534 }
535 }
536 }
537 }
538 }
539}
540
541async fn yield_to() {
542 use std::task::{Context, Poll};
543
544 struct Yield {
545 completed: bool,
546 }
547
548 impl Future for Yield {
549 type Output = ();
550
551 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
552 if self.completed {
553 return Poll::Ready(());
554 }
555 self.completed = true;
556 cx.waker().wake_by_ref();
557 Poll::Pending
558 }
559 }
560
561 Yield { completed: false }.await;
562}
563
564#[cfg(target_os = "linux")]
565static mut ARB_CB: Option<Box<dyn Fn(ntex_error::Backtrace)>> = None;
566
567#[cfg(target_os = "linux")]
568static EXPECTED_TID: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
569#[cfg(target_os = "linux")]
570static CAPTURED: Mutex<Option<ntex_error::BacktraceRaw>> = Mutex::new(None);
571
572#[track_caller]
573#[cfg(target_family = "unix")]
574pub(crate) fn sig_usr2() {
575 #[cfg(target_os = "linux")]
576 #[allow(clippy::cast_possible_truncation)]
577 {
578 let tid = unsafe { libc::syscall(libc::SYS_gettid) } as i32;
579 if EXPECTED_TID.load(Ordering::Acquire) == tid {
580 let bt = ntex_error::BacktraceRaw::new(panic::Location::caller());
583 *CAPTURED.lock() = Some(bt);
584 }
585 }
586}