2021-01-26 10:46:14 +01:00
|
|
|
use std::{
|
|
|
|
cell::RefCell,
|
2021-01-29 05:08:14 +01:00
|
|
|
collections::HashMap,
|
|
|
|
future::Future,
|
2021-01-26 10:46:14 +01:00
|
|
|
io,
|
2021-01-29 05:08:14 +01:00
|
|
|
pin::Pin,
|
2021-01-26 10:46:14 +01:00
|
|
|
sync::atomic::{AtomicUsize, Ordering},
|
2021-01-29 05:08:14 +01:00
|
|
|
task::{Context, Poll},
|
2021-01-26 10:46:14 +01:00
|
|
|
};
|
2018-12-10 04:55:40 +01:00
|
|
|
|
2021-01-29 05:08:14 +01:00
|
|
|
use futures_core::ready;
|
|
|
|
use tokio::sync::{mpsc, oneshot};
|
2018-12-10 04:55:40 +01:00
|
|
|
|
2021-01-26 10:46:14 +01:00
|
|
|
use crate::{
|
|
|
|
builder::{Builder, SystemRunner},
|
2021-01-29 05:08:14 +01:00
|
|
|
worker::Worker,
|
2021-01-26 10:46:14 +01:00
|
|
|
};
|
2018-12-10 04:55:40 +01:00
|
|
|
|
2019-03-13 08:41:26 +01:00
|
|
|
static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0);
|
2019-03-12 06:51:17 +01:00
|
|
|
|
2018-12-10 04:55:40 +01:00
|
|
|
/// System is a runtime manager.
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct System {
|
2019-03-12 06:51:17 +01:00
|
|
|
id: usize,
|
2021-01-29 05:08:14 +01:00
|
|
|
tx: mpsc::UnboundedSender<SystemCommand>,
|
|
|
|
worker: Worker,
|
2018-12-10 04:55:40 +01:00
|
|
|
stop_on_panic: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
thread_local!(
|
|
|
|
static CURRENT: RefCell<Option<System>> = RefCell::new(None);
|
|
|
|
);
|
|
|
|
|
|
|
|
impl System {
|
|
|
|
/// Constructs new system and sets it as current
|
|
|
|
pub(crate) fn construct(
|
2021-01-29 05:08:14 +01:00
|
|
|
sys: mpsc::UnboundedSender<SystemCommand>,
|
|
|
|
worker: Worker,
|
2018-12-10 04:55:40 +01:00
|
|
|
stop_on_panic: bool,
|
|
|
|
) -> Self {
|
|
|
|
let sys = System {
|
2021-01-29 03:21:06 +01:00
|
|
|
tx: sys,
|
2021-01-29 05:08:14 +01:00
|
|
|
worker,
|
2018-12-10 04:55:40 +01:00
|
|
|
stop_on_panic,
|
2019-03-12 06:51:17 +01:00
|
|
|
id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst),
|
2018-12-10 04:55:40 +01:00
|
|
|
};
|
|
|
|
System::set_current(sys.clone());
|
|
|
|
sys
|
|
|
|
}
|
|
|
|
|
2021-01-29 05:08:14 +01:00
|
|
|
/// Build a new system with a customized Tokio runtime.
|
2018-12-10 04:55:40 +01:00
|
|
|
///
|
2021-01-26 10:46:14 +01:00
|
|
|
/// This allows to customize the runtime. See [`Builder`] for more information.
|
2018-12-10 04:55:40 +01:00
|
|
|
pub fn builder() -> Builder {
|
|
|
|
Builder::new()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create new system.
|
|
|
|
///
|
2021-01-29 05:08:14 +01:00
|
|
|
/// This method panics if it can not create Tokio runtime
|
2021-01-26 10:46:14 +01:00
|
|
|
#[allow(clippy::new_ret_no_self)]
|
2021-01-29 03:21:06 +01:00
|
|
|
pub fn new(name: impl Into<String>) -> SystemRunner {
|
2018-12-10 04:55:40 +01:00
|
|
|
Self::builder().name(name).build()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get current running system.
|
|
|
|
pub fn current() -> System {
|
|
|
|
CURRENT.with(|cell| match *cell.borrow() {
|
|
|
|
Some(ref sys) => sys.clone(),
|
|
|
|
None => panic!("System is not running"),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-01-26 10:46:14 +01:00
|
|
|
/// Check if current system has started.
|
2020-02-25 06:55:02 +01:00
|
|
|
pub fn is_set() -> bool {
|
2018-12-10 04:55:40 +01:00
|
|
|
CURRENT.with(|cell| cell.borrow().is_some())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set current running system.
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub fn set_current(sys: System) {
|
|
|
|
CURRENT.with(|s| {
|
|
|
|
*s.borrow_mut() = Some(sys);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Execute function with system reference.
|
|
|
|
pub fn with_current<F, R>(f: F) -> R
|
|
|
|
where
|
|
|
|
F: FnOnce(&System) -> R,
|
|
|
|
{
|
|
|
|
CURRENT.with(|cell| match *cell.borrow() {
|
|
|
|
Some(ref sys) => f(sys),
|
|
|
|
None => panic!("System is not running"),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-01-26 10:46:14 +01:00
|
|
|
/// Numeric system ID.
|
2019-03-12 06:51:17 +01:00
|
|
|
pub fn id(&self) -> usize {
|
|
|
|
self.id
|
|
|
|
}
|
|
|
|
|
2021-01-26 10:46:14 +01:00
|
|
|
/// Stop the system (with code 0).
|
2018-12-10 04:55:40 +01:00
|
|
|
pub fn stop(&self) {
|
|
|
|
self.stop_with_code(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Stop the system with a particular exit code.
|
|
|
|
pub fn stop_with_code(&self, code: i32) {
|
2021-01-29 03:21:06 +01:00
|
|
|
let _ = self.tx.send(SystemCommand::Exit(code));
|
2018-12-10 04:55:40 +01:00
|
|
|
}
|
|
|
|
|
2021-01-29 05:08:14 +01:00
|
|
|
pub(crate) fn tx(&self) -> &mpsc::UnboundedSender<SystemCommand> {
|
2021-01-29 03:21:06 +01:00
|
|
|
&self.tx
|
2018-12-10 04:55:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return status of 'stop_on_panic' option which controls whether the System is stopped when an
|
|
|
|
/// uncaught panic is thrown from a worker thread.
|
2021-01-26 10:46:14 +01:00
|
|
|
pub(crate) fn stop_on_panic(&self) -> bool {
|
2018-12-10 04:55:40 +01:00
|
|
|
self.stop_on_panic
|
|
|
|
}
|
|
|
|
|
2021-01-26 10:46:14 +01:00
|
|
|
/// Get shared reference to system arbiter.
|
2021-01-29 05:08:14 +01:00
|
|
|
pub fn arbiter(&self) -> &Worker {
|
|
|
|
&self.worker
|
2018-12-10 04:55:40 +01:00
|
|
|
}
|
|
|
|
|
2021-01-29 05:08:14 +01:00
|
|
|
/// This function will start Tokio runtime and will finish once the `System::stop()` message
|
|
|
|
/// is called. Function `f` is called within Tokio runtime context.
|
2019-03-06 19:24:58 +01:00
|
|
|
pub fn run<F>(f: F) -> io::Result<()>
|
2018-12-10 04:55:40 +01:00
|
|
|
where
|
2020-12-27 00:26:02 +01:00
|
|
|
F: FnOnce(),
|
2018-12-10 04:55:40 +01:00
|
|
|
{
|
|
|
|
Self::builder().run(f)
|
|
|
|
}
|
|
|
|
}
|
2021-01-29 05:08:14 +01:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) enum SystemCommand {
|
|
|
|
Exit(i32),
|
|
|
|
RegisterArbiter(usize, Worker),
|
|
|
|
DeregisterArbiter(usize),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) struct SystemWorker {
|
|
|
|
stop: Option<oneshot::Sender<i32>>,
|
|
|
|
commands: mpsc::UnboundedReceiver<SystemCommand>,
|
|
|
|
workers: HashMap<usize, Worker>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SystemWorker {
|
|
|
|
pub(crate) fn new(
|
|
|
|
commands: mpsc::UnboundedReceiver<SystemCommand>,
|
|
|
|
stop: oneshot::Sender<i32>,
|
|
|
|
) -> Self {
|
|
|
|
SystemWorker {
|
|
|
|
commands,
|
|
|
|
stop: Some(stop),
|
|
|
|
workers: HashMap::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Future for SystemWorker {
|
|
|
|
type Output = ();
|
|
|
|
|
|
|
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
|
|
// process all items currently buffered in channel
|
|
|
|
loop {
|
|
|
|
match ready!(Pin::new(&mut self.commands).poll_recv(cx)) {
|
|
|
|
// channel closed; no more messages can be received
|
|
|
|
None => return Poll::Ready(()),
|
|
|
|
|
|
|
|
// process system command
|
|
|
|
Some(cmd) => match cmd {
|
|
|
|
SystemCommand::Exit(code) => {
|
|
|
|
// stop arbiters
|
|
|
|
for arb in self.workers.values() {
|
|
|
|
arb.stop();
|
|
|
|
}
|
|
|
|
// stop event loop
|
|
|
|
if let Some(stop) = self.stop.take() {
|
|
|
|
let _ = stop.send(code);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SystemCommand::RegisterArbiter(name, hnd) => {
|
|
|
|
self.workers.insert(name, hnd);
|
|
|
|
}
|
|
|
|
SystemCommand::DeregisterArbiter(name) => {
|
|
|
|
self.workers.remove(&name);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|