mod store;
mod ui;
use std::{
iter,
sync::mpsc::{self, Receiver, Sender, TryRecvError},
thread,
time::Duration,
};
use rand_chacha::ChaCha8Rng;
use rand_core::SeedableRng as _;
use crate::{
backend::{BackendId, CoreOnly, Dispatch},
pipe::{ServiceEndpoint, TrussedChannel, TrussedResponder},
platform,
service::Service,
types::CoreContext,
ClientImplementation,
};
pub use store::{StorageConfig, Store, StoreConfig};
pub use ui::UserInterface;
pub type Client<'a, D = CoreOnly> = ClientImplementation<'a, Syscall, D>;
pub fn with_platform<R, F>(mut store: StoreConfig, f: F) -> R
where
F: FnOnce(Platform<'_>) -> R,
{
store.with_store(|store| {
let platform = Platform {
rng: ChaCha8Rng::from_seed([42u8; 32]),
store,
ui: UserInterface::new(),
};
f(platform)
})
}
pub fn with_client<R, F>(store: StoreConfig, client_id: &str, f: F) -> R
where
F: FnOnce(Client) -> R,
{
with_platform(store, |platform| platform.run_client(client_id, f))
}
pub fn with_clients<R, F, const N: usize>(store: StoreConfig, client_ids: [&str; N], f: F) -> R
where
F: FnOnce([Client; N]) -> R,
{
with_platform(store, |platform| platform.run_clients(client_ids, f))
}
pub struct Syscall(Sender<()>);
impl platform::Syscall for Syscall {
fn syscall(&mut self) {
self.0.send(()).unwrap();
}
}
pub struct Runner<'a, I: 'static, C> {
syscall_tx: Sender<()>,
syscall_rx: Receiver<()>,
eps: Vec<ServiceEndpoint<'a, I, C>>,
}
impl<'a, I: 'static, C> Runner<'a, I, C> {
pub fn new() -> Self {
let (syscall_tx, syscall_rx) = mpsc::channel();
Self {
syscall_tx,
syscall_rx,
eps: Vec::new(),
}
}
pub fn syscall(&self) -> Syscall {
Syscall(self.syscall_tx.clone())
}
pub fn run<P, D, F, R>(self, platform: P, dispatch: D, f: F) -> R
where
P: platform::Platform,
D: Dispatch<Context = C, BackendId = I>,
C: Send + Sync,
I: Send + Sync,
F: FnOnce() -> R,
{
let mut service = Service::with_dispatch(platform, dispatch);
thread::scope(|s| {
let (stop_tx, stop_rx) = mpsc::channel();
s.spawn(move || {
let mut eps = self.eps;
while stop_rx.try_recv() == Err(TryRecvError::Empty) {
if self.syscall_rx.try_recv().is_ok() {
service.process(&mut eps);
}
thread::sleep(Duration::from_millis(1));
}
});
let result = f();
stop_tx.send(()).unwrap();
result
})
}
}
impl<'a, I: 'static, C: Default> Runner<'a, I, C> {
pub fn add_endpoint(
&mut self,
responder: TrussedResponder<'a>,
client_id: &str,
backends: &'static [BackendId<I>],
) {
let context = CoreContext::new(client_id.try_into().unwrap());
self.eps
.push(ServiceEndpoint::new(responder, context, backends));
}
}
impl<I: 'static, C> Default for Runner<'_, I, C> {
fn default() -> Self {
Self::new()
}
}
pub struct Platform<'a> {
rng: ChaCha8Rng,
store: store::Store<'a>,
ui: UserInterface,
}
impl Platform<'_> {
pub fn run_client<R>(self, client_id: &str, test: impl FnOnce(Client) -> R) -> R {
self.run_client_with_backends(client_id, CoreOnly, &[], test)
}
pub fn run_client_with_backends<R, D: Dispatch>(
self,
client_id: &str,
dispatch: D,
backends: &'static [BackendId<D::BackendId>],
test: impl FnOnce(Client<'_, D>) -> R,
) -> R
where
D::Context: Send + Sync,
D::BackendId: Send + Sync,
{
let channel = TrussedChannel::new();
let mut runner = Runner::new();
let (requester, responder) = channel.split().unwrap();
runner.add_endpoint(responder, client_id, backends);
let client = Client::new(requester, runner.syscall(), None);
runner.run(self, dispatch, || test(client))
}
pub fn run_clients<R, const N: usize>(
self,
client_ids: [&str; N],
test: impl FnOnce([Client; N]) -> R,
) -> R {
let channels = [const { TrussedChannel::new() }; N];
let mut runner = Runner::new();
let clients: Vec<_> = iter::zip(client_ids, &channels)
.map(|(id, channel)| {
let (requester, responder) = channel.split().unwrap();
runner.add_endpoint(responder, id, &[]);
Client::new(requester, runner.syscall(), None)
})
.collect();
runner.run(self, CoreOnly, || test(clients.try_into().ok().unwrap()))
}
pub fn run_clients_with_backends<R, D: Dispatch, const N: usize>(
self,
client_ids: [(&str, &'static [BackendId<D::BackendId>]); N],
dispatch: D,
test: impl FnOnce([Client<'_, D>; N]) -> R,
) -> R
where
D::Context: Send + Sync,
D::BackendId: Send + Sync,
{
let channels = [const { TrussedChannel::new() }; N];
let mut runner = Runner::new();
let clients: Vec<_> = iter::zip(client_ids, &channels)
.map(|((id, backends), channel)| {
let (requester, responder) = channel.split().unwrap();
runner.add_endpoint(responder, id, backends);
Client::new(requester, runner.syscall(), None)
})
.collect();
runner.run(self, dispatch, || test(clients.try_into().ok().unwrap()))
}
}
impl<'a> platform::Platform for Platform<'a> {
type R = ChaCha8Rng;
type S = store::Store<'a>;
type UI = UserInterface;
fn user_interface(&mut self) -> &mut Self::UI {
&mut self.ui
}
fn rng(&mut self) -> &mut Self::R {
&mut self.rng
}
fn store(&self) -> Self::S {
self.store
}
}