use std::future::Future;
use std::io;
use std::sync::{Arc, OnceLock};
use thiserror::Error;
use tokio::runtime::{Handle, Runtime};
use tokio::task::JoinHandle;
static GLOBAL_EXECUTOR: OnceLock<RongExecutor> = OnceLock::new();
#[derive(Clone)]
pub struct RongExecutor {
runtime: Arc<Runtime>,
}
impl std::fmt::Debug for RongExecutor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RongExecutor").finish_non_exhaustive()
}
}
#[derive(Debug, Clone)]
pub struct RongExecutorBuilder {
threads: usize,
thread_name: String,
}
#[derive(Debug, Error)]
pub enum RongExecutorBuildError {
#[error("executor threads must be greater than 0")]
InvalidThreads,
#[error("failed to build executor: {0}")]
Build(#[from] io::Error),
}
#[derive(Debug, Error)]
pub enum InstallGlobalExecutorError {
#[error("global RongExecutor is already installed")]
AlreadyInstalled,
}
impl Default for RongExecutorBuilder {
fn default() -> Self {
Self {
threads: std::thread::available_parallelism()
.map(|count| count.get())
.map(|count| count.min(4))
.unwrap_or(1),
thread_name: "rong-host".to_string(),
}
}
}
impl RongExecutorBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn threads(mut self, threads: usize) -> Self {
self.threads = threads;
self
}
pub fn thread_name(mut self, name: impl Into<String>) -> Self {
self.thread_name = name.into();
self
}
pub fn build(self) -> Result<RongExecutor, RongExecutorBuildError> {
if self.threads == 0 {
return Err(RongExecutorBuildError::InvalidThreads);
}
let runtime = tokio::runtime::Builder::new_multi_thread()
.worker_threads(self.threads)
.thread_name(self.thread_name)
.enable_all()
.build()?;
Ok(RongExecutor {
runtime: Arc::new(runtime),
})
}
}
impl RongExecutor {
pub fn builder() -> RongExecutorBuilder {
RongExecutorBuilder::new()
}
pub fn global() -> Self {
GLOBAL_EXECUTOR
.get_or_init(|| {
RongExecutor::builder()
.build()
.expect("failed to build global RongExecutor")
})
.clone()
}
pub fn install_global(self) -> Result<(), InstallGlobalExecutorError> {
GLOBAL_EXECUTOR
.set(self)
.map_err(|_| InstallGlobalExecutorError::AlreadyInstalled)
}
pub fn handle(&self) -> Handle {
self.runtime.handle().clone()
}
pub fn spawn<F, T>(&self, future: F) -> JoinHandle<T>
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static,
{
self.runtime.spawn(future)
}
pub fn spawn_blocking<F, T>(&self, func: F) -> JoinHandle<T>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
self.runtime.spawn_blocking(func)
}
}