use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, OnceLock};
use crate::litebox::LiteBox;
use crate::metrics::RuntimeMetrics;
use crate::runtime::backend::RuntimeBackend;
use crate::runtime::images::ImageBackend;
use crate::runtime::options::{BoxArchive, BoxOptions, BoxliteOptions};
use crate::runtime::rt_impl::{LocalRuntime, RuntimeImpl};
use crate::runtime::signal_handler::install_signal_handler;
use crate::runtime::types::BoxInfo;
use boxlite_shared::errors::{BoxliteError, BoxliteResult};
#[cfg(feature = "rest")]
use crate::rest::runtime::RestRuntime;
static DEFAULT_RUNTIME: OnceLock<BoxliteRuntime> = OnceLock::new();
static ATEXIT_INSTALLED: AtomicBool = AtomicBool::new(false);
extern "C" fn shutdown_on_exit() {
if let Some(rt) = DEFAULT_RUNTIME.get() {
rt.backend.shutdown_sync();
}
}
#[derive(Clone)]
pub struct BoxliteRuntime {
backend: Arc<dyn RuntimeBackend>,
image_backend: Option<Arc<dyn ImageBackend>>,
}
impl BoxliteRuntime {
pub fn new(options: BoxliteOptions) -> BoxliteResult<Self> {
let local = LocalRuntime(RuntimeImpl::new(options)?);
let backend_arc = Arc::new(local);
let image_backend = Arc::clone(&backend_arc) as Arc<dyn ImageBackend>;
Ok(Self {
backend: backend_arc,
image_backend: Some(image_backend),
})
}
#[cfg(feature = "rest")]
pub fn rest(config: crate::rest::options::BoxliteRestOptions) -> BoxliteResult<Self> {
let rest_runtime = RestRuntime::new(&config)?;
Ok(Self {
backend: Arc::new(rest_runtime),
image_backend: None, })
}
pub fn with_defaults() -> BoxliteResult<Self> {
Self::new(BoxliteOptions::default())
}
pub fn default_runtime() -> &'static Self {
let rt = DEFAULT_RUNTIME.get_or_init(|| {
Self::with_defaults()
.unwrap_or_else(|e| panic!("Failed to initialize BoxliteRuntime:\n\n{e}"))
});
if ATEXIT_INSTALLED
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
{
unsafe {
libc::atexit(shutdown_on_exit);
}
}
let backend = rt.backend.clone();
install_signal_handler(move || async move {
let _ = backend.shutdown(None).await;
});
rt
}
pub fn try_default_runtime() -> Option<&'static Self> {
DEFAULT_RUNTIME.get()
}
pub fn init_default_runtime(options: BoxliteOptions) -> BoxliteResult<()> {
let runtime = Self::new(options)?;
DEFAULT_RUNTIME
.set(runtime)
.map_err(|_| BoxliteError::Internal(
"Default runtime already initialized. Call init_default_runtime() before any use of default_runtime().".into()
))
}
pub async fn create(
&self,
options: BoxOptions,
name: Option<String>,
) -> BoxliteResult<LiteBox> {
self.backend.create(options, name).await
}
pub async fn get_or_create(
&self,
options: BoxOptions,
name: Option<String>,
) -> BoxliteResult<(LiteBox, bool)> {
self.backend.get_or_create(options, name).await
}
pub async fn get(&self, id_or_name: &str) -> BoxliteResult<Option<LiteBox>> {
self.backend.get(id_or_name).await
}
pub async fn get_info(&self, id_or_name: &str) -> BoxliteResult<Option<BoxInfo>> {
self.backend.get_info(id_or_name).await
}
pub async fn list_info(&self) -> BoxliteResult<Vec<BoxInfo>> {
self.backend.list_info().await
}
pub async fn exists(&self, id_or_name: &str) -> BoxliteResult<bool> {
self.backend.exists(id_or_name).await
}
pub async fn metrics(&self) -> BoxliteResult<RuntimeMetrics> {
self.backend.metrics().await
}
pub async fn remove(&self, id_or_name: &str, force: bool) -> BoxliteResult<()> {
self.backend.remove(id_or_name, force).await
}
pub async fn import_box(
&self,
archive: BoxArchive,
name: Option<String>,
) -> BoxliteResult<LiteBox> {
self.backend.import_box(archive, name).await
}
pub async fn shutdown(&self, timeout: Option<i32>) -> BoxliteResult<()> {
self.backend.shutdown(timeout).await
}
pub fn images(&self) -> BoxliteResult<crate::runtime::ImageHandle> {
match &self.image_backend {
Some(manager) => Ok(crate::runtime::ImageHandle::new(Arc::clone(manager))),
None => Err(BoxliteError::Unsupported(
"Image operations not supported over REST API".to_string(),
)),
}
}
}
impl std::fmt::Debug for BoxliteRuntime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BoxliteRuntime").finish_non_exhaustive()
}
}
const _: () = {
const fn assert_send_sync<T: Send + Sync>() {}
let _ = assert_send_sync::<BoxliteRuntime>;
};