use std::{future::Future, io, pin::Pin, sync::Arc, thread};
pub const DEFAULT_CLIENT_IO_THREADS: usize = 2;
pub fn default_daemon_io_threads() -> usize {
num_cpus::get().max(2)
}
pub type BoxFuture = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
pub type BoxIoFuture = Pin<Box<dyn Future<Output = io::Result<()>> + Send + 'static>>;
pub type IoTask = Pin<Box<dyn Future<Output = io::Result<()>> + Send + 'static>>;
pub trait Spawn: Send + Sync {
fn spawn_detached(&self, future: BoxFuture);
fn spawn_io(&self, future: BoxIoFuture) -> IoTask;
}
pub type Spawner = Arc<dyn Spawn>;
pub struct Runtime {
executor: Arc<async_executor::Executor<'static>>,
shutdown: Arc<event_listener::Event>,
threads: Vec<thread::JoinHandle<()>>,
}
impl Runtime {
pub fn new(threads: usize) -> Self {
let executor = Arc::new(async_executor::Executor::new());
let shutdown = Arc::new(event_listener::Event::new());
let threads = (0..threads.max(1))
.map(|i| {
let executor = executor.clone();
let shutdown = shutdown.clone();
thread::Builder::new()
.name(format!("laburnum-rt-{i}"))
.spawn(move || {
async_io::block_on(executor.run(shutdown.listen()));
})
.expect("spawn laburnum runtime thread")
})
.collect();
Self {
executor,
shutdown,
threads,
}
}
pub fn spawner(&self) -> Spawner {
Arc::new(ExecutorSpawner::new(self.executor.clone()))
}
pub fn spawn<T: Send + 'static>(
&self,
future: impl Future<Output = T> + Send + 'static,
) -> async_executor::Task<T> {
self.executor.spawn(future)
}
}
impl Drop for Runtime {
fn drop(&mut self) {
self.shutdown.notify(usize::MAX);
for handle in self.threads.drain(..) {
let _ = handle.join();
}
}
}
pub struct ExecutorSpawner {
executor: Arc<async_executor::Executor<'static>>,
}
impl ExecutorSpawner {
pub fn new(executor: Arc<async_executor::Executor<'static>>) -> Self {
Self { executor }
}
}
impl Spawn for ExecutorSpawner {
fn spawn_detached(&self, future: BoxFuture) {
self.executor.spawn(future).detach();
}
fn spawn_io(&self, future: BoxIoFuture) -> IoTask {
Box::pin(self.executor.spawn(future))
}
}
#[cfg(feature = "test")]
pub fn inert_spawner() -> Spawner {
Arc::new(ExecutorSpawner::new(Arc::new(async_executor::Executor::new())))
}