2018-12-10 04:55:40 +01:00
|
|
|
use std::cell::RefCell;
|
2019-11-25 16:49:11 +01:00
|
|
|
use std::future::Future;
|
2019-03-06 19:24:58 +01:00
|
|
|
use std::io;
|
2019-03-13 08:41:26 +01:00
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
2018-12-10 04:55:40 +01:00
|
|
|
|
2020-12-28 02:40:22 +01:00
|
|
|
use tokio::sync::mpsc::UnboundedSender;
|
2019-12-05 11:40:24 +01:00
|
|
|
use tokio::task::LocalSet;
|
2018-12-10 04:55:40 +01:00
|
|
|
|
|
|
|
use crate::arbiter::{Arbiter, SystemCommand};
|
2019-06-22 05:02:17 +02:00
|
|
|
use crate::builder::{Builder, SystemRunner};
|
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,
|
2018-12-10 04:55:40 +01:00
|
|
|
sys: UnboundedSender<SystemCommand>,
|
|
|
|
arbiter: Arbiter,
|
|
|
|
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(
|
|
|
|
sys: UnboundedSender<SystemCommand>,
|
|
|
|
arbiter: Arbiter,
|
|
|
|
stop_on_panic: bool,
|
|
|
|
) -> Self {
|
|
|
|
let sys = System {
|
|
|
|
sys,
|
|
|
|
arbiter,
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Build a new system with a customized tokio runtime.
|
|
|
|
///
|
|
|
|
/// This allows to customize the runtime. See struct level docs on
|
|
|
|
/// `Builder` for more information.
|
|
|
|
pub fn builder() -> Builder {
|
|
|
|
Builder::new()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::new_ret_no_self)]
|
|
|
|
/// Create new system.
|
|
|
|
///
|
|
|
|
/// This method panics if it can not create tokio runtime
|
|
|
|
pub fn new<T: Into<String>>(name: T) -> SystemRunner {
|
|
|
|
Self::builder().name(name).build()
|
|
|
|
}
|
|
|
|
|
2020-09-06 12:01:24 +02:00
|
|
|
/// Create new system using provided tokio `LocalSet`.
|
2019-05-23 20:34:47 +02:00
|
|
|
///
|
2019-05-24 17:29:52 +02:00
|
|
|
/// This method panics if it can not spawn system arbiter
|
2020-09-06 12:01:24 +02:00
|
|
|
///
|
|
|
|
/// Note: This method uses provided `LocalSet` to create a `System` future only.
|
|
|
|
/// All the [`Arbiter`]s will be started in separate threads using their own tokio `Runtime`s.
|
|
|
|
/// It means that using this method currently it is impossible to make `actix-rt` work in the
|
|
|
|
/// alternative `tokio` `Runtime`s (e.g. provided by [`tokio_compat`]).
|
|
|
|
///
|
|
|
|
/// [`tokio_compat`]: https://crates.io/crates/tokio-compat
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
2021-01-09 15:13:16 +01:00
|
|
|
/// ```ignore
|
2020-09-06 12:01:24 +02:00
|
|
|
/// use tokio::{runtime::Runtime, task::LocalSet};
|
|
|
|
/// use actix_rt::System;
|
|
|
|
/// use futures_util::future::try_join_all;
|
|
|
|
///
|
|
|
|
/// async fn run_application() {
|
|
|
|
/// let first_task = tokio::spawn(async {
|
|
|
|
/// // ...
|
|
|
|
/// # println!("One task");
|
|
|
|
/// # Ok::<(),()>(())
|
|
|
|
/// });
|
|
|
|
///
|
|
|
|
/// let second_task = tokio::spawn(async {
|
|
|
|
/// // ...
|
|
|
|
/// # println!("Another task");
|
|
|
|
/// # Ok::<(),()>(())
|
|
|
|
/// });
|
|
|
|
///
|
|
|
|
/// try_join_all(vec![first_task, second_task])
|
|
|
|
/// .await
|
|
|
|
/// .expect("Some of the futures finished unexpectedly");
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
///
|
2020-12-28 02:40:22 +01:00
|
|
|
/// let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
|
|
/// .worker_threads(2)
|
2020-09-06 12:01:24 +02:00
|
|
|
/// .enable_all()
|
|
|
|
/// .build()
|
|
|
|
/// .unwrap();
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// let actix_system_task = LocalSet::new();
|
|
|
|
/// let sys = System::run_in_tokio("actix-main-system", &actix_system_task);
|
|
|
|
/// actix_system_task.spawn_local(sys);
|
|
|
|
///
|
|
|
|
/// let rest_operations = run_application();
|
|
|
|
/// runtime.block_on(actix_system_task.run_until(rest_operations));
|
|
|
|
/// ```
|
2019-12-05 11:40:24 +01:00
|
|
|
pub fn run_in_tokio<T: Into<String>>(
|
2019-06-05 19:36:04 +02:00
|
|
|
name: T,
|
2019-12-05 11:40:24 +01:00
|
|
|
local: &LocalSet,
|
|
|
|
) -> impl Future<Output = io::Result<()>> {
|
2019-06-05 19:36:04 +02:00
|
|
|
Self::builder()
|
|
|
|
.name(name)
|
2019-12-05 11:40:24 +01:00
|
|
|
.build_async(local)
|
2019-06-05 19:36:04 +02:00
|
|
|
.run_nonblocking()
|
2019-05-23 20:34:47 +02:00
|
|
|
}
|
|
|
|
|
2020-09-06 12:01:24 +02:00
|
|
|
/// Consume the provided tokio Runtime and start the `System` in it.
|
|
|
|
/// This method will create a `LocalSet` object and occupy the current thread
|
|
|
|
/// for the created `System` exclusively. All the other asynchronous tasks that
|
|
|
|
/// should be executed as well must be aggregated into one future, provided as the last
|
|
|
|
/// argument to this method.
|
|
|
|
///
|
|
|
|
/// Note: This method uses provided `Runtime` to create a `System` future only.
|
|
|
|
/// All the [`Arbiter`]s will be started in separate threads using their own tokio `Runtime`s.
|
|
|
|
/// It means that using this method currently it is impossible to make `actix-rt` work in the
|
|
|
|
/// alternative `tokio` `Runtime`s (e.g. provided by `tokio_compat`).
|
|
|
|
///
|
|
|
|
/// [`tokio_compat`]: https://crates.io/crates/tokio-compat
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// - `name`: Name of the System
|
|
|
|
/// - `runtime`: A tokio Runtime to run the system in.
|
|
|
|
/// - `rest_operations`: A future to be executed in the runtime along with the System.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
2021-01-09 15:13:16 +01:00
|
|
|
/// ```ignore
|
2020-09-06 12:01:24 +02:00
|
|
|
/// use tokio::runtime::Runtime;
|
|
|
|
/// use actix_rt::System;
|
|
|
|
/// use futures_util::future::try_join_all;
|
|
|
|
///
|
|
|
|
/// async fn run_application() {
|
|
|
|
/// let first_task = tokio::spawn(async {
|
|
|
|
/// // ...
|
|
|
|
/// # println!("One task");
|
|
|
|
/// # Ok::<(),()>(())
|
|
|
|
/// });
|
|
|
|
///
|
|
|
|
/// let second_task = tokio::spawn(async {
|
|
|
|
/// // ...
|
|
|
|
/// # println!("Another task");
|
|
|
|
/// # Ok::<(),()>(())
|
|
|
|
/// });
|
|
|
|
///
|
|
|
|
/// try_join_all(vec![first_task, second_task])
|
|
|
|
/// .await
|
|
|
|
/// .expect("Some of the futures finished unexpectedly");
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
///
|
2020-12-28 02:40:22 +01:00
|
|
|
/// let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
|
|
/// .worker_threads(2)
|
2020-09-06 12:01:24 +02:00
|
|
|
/// .enable_all()
|
|
|
|
/// .build()
|
|
|
|
/// .unwrap();
|
|
|
|
///
|
|
|
|
/// let rest_operations = run_application();
|
|
|
|
/// System::attach_to_tokio("actix-main-system", runtime, rest_operations);
|
|
|
|
/// ```
|
|
|
|
pub fn attach_to_tokio<Fut, R>(
|
|
|
|
name: impl Into<String>,
|
2020-12-28 02:40:22 +01:00
|
|
|
runtime: tokio::runtime::Runtime,
|
2020-09-06 12:01:24 +02:00
|
|
|
rest_operations: Fut,
|
|
|
|
) -> R
|
|
|
|
where
|
|
|
|
Fut: std::future::Future<Output = R>,
|
|
|
|
{
|
|
|
|
let actix_system_task = LocalSet::new();
|
|
|
|
let sys = System::run_in_tokio(name.into(), &actix_system_task);
|
|
|
|
actix_system_task.spawn_local(sys);
|
|
|
|
|
|
|
|
runtime.block_on(actix_system_task.run_until(rest_operations))
|
|
|
|
}
|
|
|
|
|
2018-12-10 04:55:40 +01:00
|
|
|
/// 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"),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-04-05 00:05:08 +02:00
|
|
|
/// Check if current system is set, i.e., as already been 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"),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-03-12 06:51:17 +01:00
|
|
|
/// System id
|
|
|
|
pub fn id(&self) -> usize {
|
|
|
|
self.id
|
|
|
|
}
|
|
|
|
|
2018-12-10 04:55:40 +01:00
|
|
|
/// Stop the system
|
|
|
|
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) {
|
2020-12-28 02:40:22 +01:00
|
|
|
let _ = self.sys.send(SystemCommand::Exit(code));
|
2018-12-10 04:55:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn sys(&self) -> &UnboundedSender<SystemCommand> {
|
|
|
|
&self.sys
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return status of 'stop_on_panic' option which controls whether the System is stopped when an
|
|
|
|
/// uncaught panic is thrown from a worker thread.
|
|
|
|
pub fn stop_on_panic(&self) -> bool {
|
|
|
|
self.stop_on_panic
|
|
|
|
}
|
|
|
|
|
|
|
|
/// System arbiter
|
|
|
|
pub fn arbiter(&self) -> &Arbiter {
|
|
|
|
&self.arbiter
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This function will start tokio runtime and will finish once the
|
|
|
|
/// `System::stop()` message get called.
|
|
|
|
/// Function `f` get 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)
|
|
|
|
}
|
|
|
|
}
|