#[cfg(feature = "tokio")]
use std::future::Future;
use crate::{
error::Result,
server::{run_parent, Server},
};
#[cfg(feature = "tokio")]
use tokio::runtime::Builder;
pub const DEFAULT_NUM_PROCESSES: u32 = 8;
pub struct Prefork<Res>
where
Res: 'static,
{
resource: Res,
num_processes: u32,
child_init: Box<dyn Fn(u32, Res)>,
}
impl<Res> Prefork<Res>
where
Res: 'static,
{
pub fn from_resource(resource: Res) -> Self {
Self {
resource,
num_processes: DEFAULT_NUM_PROCESSES,
child_init: Box::new(Self::empty_init),
}
}
#[must_use]
pub fn with_num_processes(mut self, num_processes: u32) -> Self {
self.num_processes = num_processes;
self
}
#[cfg(feature = "tokio")]
#[must_use]
pub fn with_tokio<FAsync, Fut>(mut self, child_init: FAsync) -> Self
where
FAsync: Fn(u32, Res) -> Fut + 'static,
Fut: Future<Output = ()>,
{
self.child_init = Self::start_tokio(child_init);
self
}
#[must_use]
pub fn with_init<FChild>(mut self, child_init: FChild) -> Self
where
FChild: Fn(u32, Res) + 'static,
{
self.child_init = Box::new(child_init);
self
}
pub fn fork(self) -> Result<bool> {
let mut server = Server::from_resource(self.resource, self.child_init, self.num_processes);
let Some(pids) = server.prefork()? else {
return Ok(false);
};
if !pids.is_empty() {
run_parent(pids)?;
}
Ok(true)
}
pub fn server(self) -> Server<Res> {
Server::from_resource(self.resource, self.child_init, self.num_processes)
}
#[cfg(feature = "tokio")]
fn start_tokio<FAsync, Fut>(child_init: FAsync) -> Box<dyn Fn(u32, Res)>
where
FAsync: Fn(u32, Res) -> Fut + 'static,
Fut: Future<Output = ()>,
{
Box::new(move |child_num: u32, resource: Res| {
let runtime = Builder::new_current_thread()
.enable_io()
.enable_time()
.build()
.expect("cannot create runtime");
runtime.block_on(async {
(child_init)(child_num, resource).await;
});
})
}
#[allow(clippy::needless_pass_by_value)]
fn empty_init(_child_num: u32, _res: Res) {
}
}