use std::sync::{atomic::AtomicUsize, atomic::Ordering, Arc};
use std::{cell::RefCell, fmt, future::Future, pin::Pin, rc::Rc};
use async_channel::Sender;
use super::arbiter::{Arbiter, SystemCommand};
use super::builder::{Builder, SystemRunner};
static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0);
#[derive(Clone, Debug)]
pub struct System {
id: usize,
sys: Sender<SystemCommand>,
arbiter: Arbiter,
config: SystemConfig,
}
#[derive(Clone)]
pub(super) struct SystemConfig {
pub(super) stack_size: usize,
pub(super) stop_on_panic: bool,
pub(super) block_on:
Option<Arc<dyn Fn(Pin<Box<dyn Future<Output = ()>>>) + Sync + Send>>,
}
thread_local!(
static CURRENT: RefCell<Option<System>> = const { RefCell::new(None) };
);
impl System {
pub(super) fn construct(
sys: Sender<SystemCommand>,
arbiter: Arbiter,
config: SystemConfig,
) -> Self {
let sys = System {
sys,
config,
arbiter,
id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst),
};
System::set_current(sys.clone());
sys
}
pub fn build() -> Builder {
Builder::new()
}
#[allow(clippy::new_ret_no_self)]
pub fn new(name: &str) -> SystemRunner {
Self::build().name(name).finish()
}
pub fn current() -> System {
CURRENT.with(|cell| match *cell.borrow() {
Some(ref sys) => sys.clone(),
None => panic!("System is not running"),
})
}
#[doc(hidden)]
pub fn set_current(sys: System) {
CURRENT.with(|s| {
*s.borrow_mut() = Some(sys);
})
}
pub fn id(&self) -> usize {
self.id
}
pub fn stop(&self) {
self.stop_with_code(0)
}
pub fn stop_with_code(&self, code: i32) {
let _ = self.sys.try_send(SystemCommand::Exit(code));
}
pub fn stop_on_panic(&self) -> bool {
self.config.stop_on_panic
}
pub fn arbiter(&self) -> &Arbiter {
&self.arbiter
}
pub(super) fn sys(&self) -> &Sender<SystemCommand> {
&self.sys
}
pub(super) fn config(&self) -> SystemConfig {
self.config.clone()
}
}
impl SystemConfig {
#[inline]
pub(super) fn block_on<F, R>(&self, fut: F) -> R
where
F: Future<Output = R> + 'static,
R: 'static,
{
let result = Rc::new(RefCell::new(None));
let result_inner = result.clone();
if let Some(block_on) = &self.block_on {
(*block_on)(Box::pin(async move {
let r = fut.await;
*result_inner.borrow_mut() = Some(r);
}));
} else {
crate::block_on(Box::pin(async move {
let r = fut.await;
*result_inner.borrow_mut() = Some(r);
}));
}
let res = result.borrow_mut().take().unwrap();
res
}
}
impl fmt::Debug for SystemConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SystemConfig")
.field("stack_size", &self.stack_size)
.field("stop_on_panic", &self.stop_on_panic)
.finish()
}
}