use core::future::Future;
use crate::{
EndpointAddr, InterfaceBuilder, LocalSpawner, PacketSender, RoleSetup, RuntimeHandle,
WorkerContext, context_map::ModrpcContextTag,
};
pub trait InterfaceSchema {
fn new(ib: &mut InterfaceBuilder) -> Self;
}
pub trait InterfaceRole: 'static {
type Interface: InterfaceSchema + Send + Sync;
type Config: mproto::Owned;
type Init: mproto::Owned;
type Stubs;
type Hooks: Clone + 'static;
fn setup_worker(
i: &Self::Interface,
ii: &mut RoleSetup,
config: &Self::Config,
init: &Self::Init,
) -> (Self::Stubs, Self::Hooks);
}
pub trait RoleStartFn<Role: InterfaceRole>: FnOnce(RoleWorkerContext<Role>) {}
impl<F, Role> RoleStartFn<Role> for F
where
Role: InterfaceRole,
F: FnOnce(RoleWorkerContext<Role>),
{
}
pub struct RoleWorkerContext<'a, Role: InterfaceRole> {
pub rt: &'a RuntimeHandle,
pub setup: &'a mut RoleSetup<'a>,
pub stubs: Role::Stubs,
pub hooks: &'a Role::Hooks,
pub config: &'a Role::Config,
pub init: &'a Role::Init,
}
impl<Role: InterfaceRole> RoleWorkerContext<'_, Role> {
pub fn raw_spawner(&self) -> &LocalSpawner {
&self.role_spawner().spawner
}
pub fn role_spawner(&self) -> &RoleSpawner {
&self.setup.role_spawner()
}
pub fn role_shutdown_signal(&self) -> &bab::SignalTree {
self.setup.role_shutdown_signal()
}
}
#[derive(Clone)]
pub struct RoleSpawner {
spawner: LocalSpawner,
interval_fn: for<'a> fn(
core::time::Duration,
&'a mut dyn FnMut(),
) -> core::pin::Pin<Box<dyn Future<Output = ()> + 'a>>,
worker_shutdown_signal: bab::SignalTree,
role_shutdown_signal: bab::SignalTree,
}
impl RoleSpawner {
pub fn raw_spawner(&self) -> &LocalSpawner {
&self.spawner
}
pub fn spawn(&self, future: impl core::future::Future<Output = ()> + 'static) {
let worker_shutdown_signal = self.worker_shutdown_signal.clone();
let role_shutdown_signal = self.role_shutdown_signal.clone();
self.spawner
.spawn(async move {
futures_lite::future::or(
future,
futures_lite::future::or(
role_shutdown_signal.wait(),
worker_shutdown_signal.wait(),
),
)
.await;
})
.unwrap();
}
pub fn on_shutdown(&self, future: impl core::future::Future<Output = ()> + 'static) {
let worker_shutdown_signal = self.worker_shutdown_signal.clone();
let role_shutdown_signal = self.role_shutdown_signal.clone();
self.spawner
.spawn(async move {
futures_lite::future::or(
role_shutdown_signal.wait(),
worker_shutdown_signal.wait(),
)
.await;
future.await;
})
.unwrap();
}
pub fn spawn_interval_loop<'a>(
&self,
interval_duration: core::time::Duration,
mut f: impl FnMut() + 'static,
) {
let interval_fn = self.interval_fn;
self.spawn(async move {
(interval_fn)(interval_duration, &mut f).await;
});
}
}
pub fn run_role_worker<Role>(
rt: &RuntimeHandle,
plane_id: u32,
role_id: u64,
spawner: LocalSpawner,
worker_cx: &WorkerContext,
worker_shutdown_signal: bab::SignalTree,
role_shutdown_signal: bab::SignalTree,
endpoint_addr: EndpointAddr,
packet_sender: PacketSender,
config: &<Role as InterfaceRole>::Config,
init: &<Role as InterfaceRole>::Init,
start_fn: impl RoleStartFn<Role>,
) -> <Role as InterfaceRole>::Hooks
where
Role: InterfaceRole,
<Role as InterfaceRole>::Init: mproto::Encode,
{
let role_spawner = RoleSpawner {
spawner: spawner,
interval_fn: worker_cx.interval_fn,
worker_shutdown_signal,
role_shutdown_signal: role_shutdown_signal.clone(),
};
role_spawner.on_shutdown({
let pp = worker_cx.pp.clone();
let locals = worker_cx.locals.clone();
async move {
pp.remove_plane(plane_id);
locals
.borrow_mut()
.shutdown_tag(ModrpcContextTag::Role(role_id));
}
});
let mut ib = InterfaceBuilder::new();
let interface = Role::Interface::new(&mut ib);
let role_handle = {
let endpoint_addr = endpoint_addr.clone();
let role_name = core::any::type_name::<Role>()
.split("<")
.next()
.unwrap()
.rsplit("::")
.next()
.unwrap()
.strip_suffix("Role")
.unwrap_or_else(|| core::any::type_name::<Role>());
let mut setup = RoleSetup::new(
role_name,
role_spawner.clone(),
worker_cx,
packet_sender,
plane_id,
role_id,
endpoint_addr,
role_shutdown_signal.clone(),
);
let (stubs, role_handle) = Role::setup_worker(&interface, &mut setup, config, init);
start_fn(RoleWorkerContext {
rt,
setup: &mut setup,
stubs,
hooks: &role_handle,
config,
init,
});
role_handle
};
role_handle
}