use ayun_config::{
config::{Kind, UnhandledPanic},
traits::ConfigurationTrait,
};
use ayun_core::{
errors::ContainerError,
traits::{ErrorTrait, InstanceTrait},
Container, Error, Result,
};
pub struct Runtime {
inner: tokio::runtime::Runtime,
}
impl InstanceTrait for Runtime {
fn register(container: &Container) -> Result<Self, ContainerError>
where
Self: Sized,
{
let config = container
.resolve::<ayun_config::Config>()?
.get::<ayun_config::config::Runtime>("runtime")
.map_err(Error::wrap)?;
Ok(Self::try_from_config(config).map_err(Error::wrap)?)
}
}
impl Runtime {
fn new(inner: tokio::runtime::Runtime) -> Self {
Self { inner }
}
fn try_from_config(config: ayun_config::config::Runtime) -> Result<Self, Error> {
let mut builder = match config.kind {
Kind::CurrentThread => tokio::runtime::Builder::new_current_thread(),
Kind::MultiThread => tokio::runtime::Builder::new_multi_thread(),
Kind::MultiThreadAlt => tokio::runtime::Builder::new_multi_thread_alt(),
};
if let Some(num) = config.worker_threads {
builder.worker_threads(num);
}
if let Some(stack_size) = config.thread_stack_size {
builder.thread_stack_size(stack_size);
}
if let Some(time) = config.keep_alive {
builder.thread_keep_alive(std::time::Duration::from_millis(time));
}
if let Some(queue_interval) = config.global_queue_interval {
builder.global_queue_interval(queue_interval);
}
let behavior = match config.unhandled_panic {
UnhandledPanic::Ignore => tokio::runtime::UnhandledPanic::Ignore,
UnhandledPanic::ShutdownRuntime => tokio::runtime::UnhandledPanic::Ignore,
};
if config.metrics_poll_count_histogram_enable {
builder.enable_metrics_poll_count_histogram();
}
if config.disable_lifo_slot {
builder.disable_lifo_slot();
}
let runtime = builder
.enable_all()
.max_io_events_per_tick(config.nevents)
.max_blocking_threads(config.max_blocking_threads)
.thread_name(config.thread_name)
.event_interval(config.event_interval)
.unhandled_panic(behavior)
.build()?;
Ok(Self::new(runtime))
}
}
impl std::ops::Deref for Runtime {
type Target = tokio::runtime::Runtime;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
pub fn daemon(closure: ayun_core::Closure<Result<()>>) -> Result<()> {
closure()?;
let runtime = ayun_core::app().resolve::<Runtime>()?;
runtime.block_on(shutdown());
Ok(())
}
pub async fn shutdown() {
let ctrl_c = async {
tokio::signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("fail to install the terminate signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
tracing::warn!("signal received, starting graceful shutdown");
}