Orchid-base uses task-local context.
Everything else is broken at the moment.
This commit is contained in:
@@ -3,31 +3,23 @@ use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::{BitAnd, Deref};
|
||||
use std::pin::{Pin, pin};
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll;
|
||||
use std::thread::panicking;
|
||||
|
||||
use async_fn_stream::stream;
|
||||
use bound::Bound;
|
||||
use derive_destructure::destructure;
|
||||
use dyn_clone::{DynClone, clone_box};
|
||||
use futures::channel::mpsc::{self, Receiver, Sender, channel};
|
||||
use futures::channel::oneshot;
|
||||
use futures::future::{LocalBoxFuture, select_all};
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::lock::{Mutex, MutexGuard};
|
||||
use futures::{AsyncRead, AsyncWrite, SinkExt, Stream, StreamExt, stream, stream_select};
|
||||
use hashbrown::HashMap;
|
||||
use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request, UnderRoot, enc_vec};
|
||||
use some_executor::task_local;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::clone;
|
||||
use crate::logging::Logger;
|
||||
use orchid_api_traits::{Decode, Encode, Request, UnderRoot};
|
||||
|
||||
#[must_use = "Receipts indicate that a required action has been performed within a function. \
|
||||
Most likely this should be returned somewhere."]
|
||||
pub struct Receipt<'a>(PhantomData<&'a mut ()>);
|
||||
|
||||
/// Write guard to outbound for the purpose of serializing a request. Only one
|
||||
@@ -98,11 +90,11 @@ impl<T: Client + ?Sized> ClientExt for T {}
|
||||
/// A form of [Evidence] that doesn't require the value to be kept around
|
||||
pub struct Witness<T>(PhantomData<T>);
|
||||
impl<T> Witness<T> {
|
||||
fn of(t: &T) -> Self { Self(PhantomData) }
|
||||
pub fn of(_: &T) -> Self { Self(PhantomData) }
|
||||
}
|
||||
impl<T> Copy for Witness<T> {}
|
||||
impl<T> Clone for Witness<T> {
|
||||
fn clone(&self) -> Self { Self(PhantomData) }
|
||||
fn clone(&self) -> Self { *self }
|
||||
}
|
||||
|
||||
/// A proxy for the type of a value either previously saved into a [Witness] or
|
||||
@@ -172,12 +164,6 @@ impl<'a> NotifReader<'a> {
|
||||
pub async fn release(self) {}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct IO {
|
||||
i: IoLock<dyn AsyncRead>,
|
||||
o: IoLock<dyn AsyncWrite>,
|
||||
}
|
||||
|
||||
struct ReplySub {
|
||||
id: u64,
|
||||
ack: oneshot::Sender<()>,
|
||||
@@ -192,9 +178,7 @@ struct IoClient {
|
||||
notif_tid: TypeId,
|
||||
}
|
||||
impl IoClient {
|
||||
pub async fn new<Req: 'static, Not: 'static>(
|
||||
output: IoLock<dyn AsyncWrite>,
|
||||
) -> (Receiver<ReplySub>, Self) {
|
||||
fn new<Req: 'static, Not: 'static>(output: IoLock<dyn AsyncWrite>) -> (Receiver<ReplySub>, Self) {
|
||||
let (req, rep) = mpsc::channel(0);
|
||||
(rep, Self {
|
||||
output,
|
||||
@@ -227,8 +211,8 @@ impl Client for IoClient {
|
||||
};
|
||||
let (cb, reply) = oneshot::channel();
|
||||
let (ack, got_ack) = oneshot::channel();
|
||||
self.subscribe.as_ref().clone().send(ReplySub { id, ack, cb }).await;
|
||||
got_ack.await;
|
||||
self.subscribe.as_ref().clone().send(ReplySub { id, ack, cb }).await.unwrap();
|
||||
got_ack.await.unwrap();
|
||||
let mut w = self.lock_out().await;
|
||||
id.encode(w.as_mut()).await;
|
||||
Box::new(IoReqWriter { reply, w }) as Box<dyn ReqWriter>
|
||||
@@ -273,430 +257,176 @@ impl MsgWriter for IoNotifWriter {
|
||||
|
||||
pub struct CommCtx {
|
||||
quit: Sender<()>,
|
||||
client: Rc<IoClient>,
|
||||
}
|
||||
|
||||
impl CommCtx {
|
||||
pub async fn quit(self) { self.quit.clone().send(()).await.expect("quit channel dropped"); }
|
||||
pub fn client(&self) -> Rc<dyn Client> { self.client.clone() as Rc<dyn Client> }
|
||||
}
|
||||
|
||||
/// This function will exit only when one of the callbacks calls
|
||||
/// [CommCtx::quit].
|
||||
pub async fn io_comm<Req: 'static, Not: 'static>(
|
||||
/// Establish bidirectional request-notification communication over a duplex
|
||||
/// channel. The returned [IoClient] can be used for notifications immediately,
|
||||
/// but requests can only be received while the future is running. The future
|
||||
/// will only resolve when [CommCtx::quit] is called.
|
||||
pub fn io_comm<Req: 'static, Not: 'static>(
|
||||
o: Rc<Mutex<Pin<Box<dyn AsyncWrite>>>>,
|
||||
i: Mutex<Pin<Box<dyn AsyncRead>>>,
|
||||
notif: impl for<'a> AsyncFn(&mut CommCtx, NotifReader<'a>),
|
||||
req: impl for<'a> AsyncFn(&mut CommCtx, ReqReader<'a>),
|
||||
) {
|
||||
req: impl for<'a> AsyncFn(&mut CommCtx, ReqReader<'a>) -> Receipt<'a>,
|
||||
) -> (impl Client, impl Future<Output = ()>) {
|
||||
let i = Rc::new(i);
|
||||
let (onsub, client) = IoClient::new::<Req, Not>(o.clone()).await;
|
||||
let client = Rc::new(client);
|
||||
let (exit, onexit) = channel(1);
|
||||
enum Event {
|
||||
Input(u64),
|
||||
Sub(ReplySub),
|
||||
Exit,
|
||||
}
|
||||
let exiting = RefCell::new(false);
|
||||
let input_stream = stream(async |mut h| {
|
||||
loop {
|
||||
let id = u64::decode(i.lock().await.as_mut()).await;
|
||||
h.emit(Event::Input(id)).await;
|
||||
let (onsub, client) = IoClient::new::<Req, Not>(o.clone());
|
||||
(client, async move {
|
||||
let (exit, onexit) = channel(1);
|
||||
enum Event {
|
||||
Input(u64),
|
||||
Sub(ReplySub),
|
||||
Exit,
|
||||
}
|
||||
});
|
||||
let pending_reqs = RefCell::new(VecDeque::<LocalBoxFuture<()>>::new());
|
||||
// this stream will never yield a value
|
||||
let mut fork_stream = pin!(
|
||||
stream::poll_fn(|cx| {
|
||||
let mut reqs_g = pending_reqs.borrow_mut();
|
||||
reqs_g.retain_mut(|req| match req.as_mut().poll(cx) {
|
||||
Poll::Pending => true,
|
||||
Poll::Ready(()) => false,
|
||||
});
|
||||
if *exiting.borrow() { Poll::Ready(None) } else { Poll::Pending }
|
||||
})
|
||||
.fuse()
|
||||
);
|
||||
{
|
||||
let mut shared = pin!(stream_select!(
|
||||
pin!(input_stream) as Pin<&mut dyn Stream<Item = Event>>,
|
||||
onsub.map(Event::Sub),
|
||||
fork_stream.as_mut(),
|
||||
onexit.map(|()| Event::Exit),
|
||||
));
|
||||
let exiting = RefCell::new(false);
|
||||
let input_stream = stream(async |mut h| {
|
||||
loop {
|
||||
let id = u64::decode(i.lock().await.as_mut()).await;
|
||||
h.emit(Event::Input(id)).await;
|
||||
}
|
||||
});
|
||||
let pending_reqs = RefCell::new(VecDeque::<LocalBoxFuture<()>>::new());
|
||||
// this stream will never yield a value
|
||||
let mut fork_stream = pin!(
|
||||
stream::poll_fn(|cx| {
|
||||
let mut reqs_g = pending_reqs.borrow_mut();
|
||||
reqs_g.retain_mut(|req| match req.as_mut().poll(cx) {
|
||||
Poll::Pending => true,
|
||||
Poll::Ready(()) => false,
|
||||
});
|
||||
if *exiting.borrow() { Poll::Ready(None) } else { Poll::Pending }
|
||||
})
|
||||
.fuse()
|
||||
);
|
||||
let mut pending_replies = HashMap::new();
|
||||
while let Some(next) = shared.next().await {
|
||||
match next {
|
||||
Event::Exit => {
|
||||
*exiting.borrow_mut() = true;
|
||||
break;
|
||||
},
|
||||
Event::Sub(ReplySub { id, ack, cb }) => {
|
||||
pending_replies.insert(id, cb);
|
||||
ack.send(());
|
||||
},
|
||||
Event::Input(id) if id == 0 => {
|
||||
let (i, notif, exit, client) = (i.clone(), ¬if, exit.clone(), client.clone());
|
||||
pending_reqs.borrow_mut().push_back(Box::pin(async move {
|
||||
let g = i.lock().await;
|
||||
notif(&mut CommCtx { client, quit: exit.clone() }, NotifReader { read: g }).await
|
||||
}));
|
||||
},
|
||||
// id.msb == 0 is a request, !id where id.msb == 1 is the equivalent response
|
||||
Event::Input(id) =>
|
||||
if (id & (1 << (u64::BITS - 1))) == 0 {
|
||||
let (i, o, req, exit, client) =
|
||||
(i.clone(), o.clone(), &req, exit.clone(), client.clone());
|
||||
{
|
||||
let mut shared = pin!(stream_select!(
|
||||
pin!(input_stream) as Pin<&mut dyn Stream<Item = Event>>,
|
||||
onsub.map(Event::Sub),
|
||||
fork_stream.as_mut(),
|
||||
onexit.map(|()| Event::Exit),
|
||||
));
|
||||
while let Some(next) = shared.next().await {
|
||||
match next {
|
||||
Event::Exit => {
|
||||
*exiting.borrow_mut() = true;
|
||||
break;
|
||||
},
|
||||
Event::Sub(ReplySub { id, ack, cb }) => {
|
||||
pending_replies.insert(id, cb);
|
||||
ack.send(()).unwrap();
|
||||
},
|
||||
Event::Input(0) => {
|
||||
let (i, notif, exit) = (i.clone(), ¬if, exit.clone());
|
||||
pending_reqs.borrow_mut().push_back(Box::pin(async move {
|
||||
let g = i.lock().await;
|
||||
req(&mut CommCtx { client, quit: exit.clone() }, ReqReader {
|
||||
id,
|
||||
read: g,
|
||||
write: &*o,
|
||||
})
|
||||
.await
|
||||
}) as LocalBoxFuture<()>);
|
||||
} else {
|
||||
let cb = pending_replies.remove(&!id).expect("Reply to unrecognized request");
|
||||
let _ = cb.send(Bound::async_new(i.clone(), |i| i.lock()).await);
|
||||
notif(&mut CommCtx { quit: exit.clone() }, NotifReader { read: g }).await
|
||||
}));
|
||||
},
|
||||
// id.msb == 0 is a request, !id where id.msb == 1 is the equivalent response
|
||||
Event::Input(id) =>
|
||||
if (id & (1 << (u64::BITS - 1))) == 0 {
|
||||
let (i, o, req, exit) = (i.clone(), o.clone(), &req, exit.clone());
|
||||
pending_reqs.borrow_mut().push_back(Box::pin(async move {
|
||||
let g = i.lock().await;
|
||||
let _ =
|
||||
req(&mut CommCtx { quit: exit.clone() }, ReqReader { id, read: g, write: &o })
|
||||
.await;
|
||||
}) as LocalBoxFuture<()>);
|
||||
} else {
|
||||
let cb = pending_replies.remove(&!id).expect("Reply to unrecognized request");
|
||||
let _ = cb.send(Bound::async_new(i.clone(), |i| i.lock()).await);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fork_stream.as_mut().count().await;
|
||||
}
|
||||
|
||||
trait_set! {
|
||||
pub trait SendFn<T: MsgSet> =
|
||||
for<'a> FnMut(&'a [u8], ReqNot<T>) -> LocalBoxFuture<'a, ()>
|
||||
+ DynClone + 'static;
|
||||
pub trait ReqFn<T: MsgSet> =
|
||||
for<'a> FnMut(ReqReader<'a, T>, <T::In as Channel>::Req)
|
||||
-> LocalBoxFuture<'a, Receipt<'a>>
|
||||
+ DynClone + 'static;
|
||||
pub trait NotifFn<T: MsgSet> =
|
||||
FnMut(<T::In as Channel>::Notif, ReqNot<T>) -> LocalBoxFuture<'static, ()>
|
||||
+ DynClone + 'static;
|
||||
}
|
||||
|
||||
fn get_id(message: &[u8]) -> (u64, &[u8]) {
|
||||
(u64::from_be_bytes(message[..8].to_vec().try_into().unwrap()), &message[8..])
|
||||
}
|
||||
|
||||
pub trait ReqHandlish {
|
||||
fn defer(&self, cb: impl Future<Output = ()> + 'static)
|
||||
where Self: Sized {
|
||||
self.defer_objsafe(Box::pin(cb));
|
||||
}
|
||||
fn defer_objsafe(&self, val: Pin<Box<dyn Future<Output = ()>>>);
|
||||
}
|
||||
impl ReqHandlish for &'_ dyn ReqHandlish {
|
||||
fn defer_objsafe(&self, val: Pin<Box<dyn Future<Output = ()>>>) { (**self).defer_objsafe(val) }
|
||||
}
|
||||
|
||||
type LocalAsyncFnOnceBox = Box<dyn FnOnce(Vec<u8>) -> LocalBoxFuture<'static, ()>>;
|
||||
|
||||
#[derive(destructure)]
|
||||
pub struct RequestHandle<'a, MS: MsgSet> {
|
||||
defer: RefCell<Vec<Pin<Box<dyn Future<Output = ()>>>>>,
|
||||
_reqlt: PhantomData<&'a mut ()>,
|
||||
parent: ReqNot<MS>,
|
||||
raw_reply: RefCell<Option<LocalAsyncFnOnceBox>>,
|
||||
}
|
||||
impl<'a, MS: MsgSet + 'static> ReqReader<'a, MS> {
|
||||
pub fn new(parent: ReqNot<MS>, raw_reply: impl AsyncFnOnce(Vec<u8>) + 'static) -> Self {
|
||||
Self {
|
||||
defer: RefCell::default(),
|
||||
_reqlt: PhantomData,
|
||||
parent,
|
||||
raw_reply: RefCell::new(Some(Box::new(|v| Box::pin(raw_reply(v))))),
|
||||
}
|
||||
}
|
||||
pub fn reqnot(&self) -> ReqNot<MS> { self.parent.clone() }
|
||||
pub async fn handle<U: Request>(&self, _: &U, rep: &U::Response) -> Receipt<'a> {
|
||||
self.respond(rep).await
|
||||
}
|
||||
pub fn will_handle_as<U: Request>(&self, _: &U) -> ReqTypToken<U> { ReqTypToken(PhantomData) }
|
||||
pub async fn handle_as<U: Request>(&self, _: ReqTypToken<U>, rep: &U::Response) -> Receipt<'a> {
|
||||
self.respond(rep).await
|
||||
}
|
||||
pub async fn respond(&self, response: &impl Encode) -> Receipt<'a> {
|
||||
let replier = self.raw_reply.borrow_mut().take().expect("Already responded to request");
|
||||
let buf = enc_vec(response).await;
|
||||
(replier)(buf).await;
|
||||
let deferred = mem::take(&mut *self.defer.borrow_mut());
|
||||
for item in deferred {
|
||||
item.await
|
||||
}
|
||||
Receipt(PhantomData)
|
||||
}
|
||||
}
|
||||
impl<MS: MsgSet> ReqHandlish for ReqReader<'_, MS> {
|
||||
fn defer_objsafe(&self, val: Pin<Box<dyn Future<Output = ()>>>) {
|
||||
self.defer.borrow_mut().push(val)
|
||||
}
|
||||
}
|
||||
impl<MS: MsgSet> Drop for ReqReader<'_, MS> {
|
||||
fn drop(&mut self) {
|
||||
if !panicking() {
|
||||
debug_assert!(self.raw_reply.borrow().is_none(), "Request dropped without response")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReqTypToken<T>(PhantomData<T>);
|
||||
|
||||
pub struct ReqNotData<T: MsgSet> {
|
||||
id: u64,
|
||||
send: Box<dyn SendFn<T>>,
|
||||
notif: Box<dyn NotifFn<T>>,
|
||||
req: Box<dyn ReqFn<T>>,
|
||||
responses: HashMap<u64, mpsc::Sender<Vec<u8>>>,
|
||||
}
|
||||
|
||||
/// Wraps a raw message buffer to save on copying.
|
||||
/// Dereferences to the tail of the message buffer, cutting off the ID
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RawReply(Vec<u8>);
|
||||
impl Deref for RawReply {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &Self::Target { get_id(&self.0[..]).1 }
|
||||
}
|
||||
|
||||
pub struct ReqNot<T: MsgSet>(Arc<Mutex<ReqNotData<T>>>, Logger);
|
||||
impl<T: MsgSet> ReqNot<T> {
|
||||
pub fn new(
|
||||
logger: Logger,
|
||||
send: impl SendFn<T>,
|
||||
notif: impl NotifFn<T>,
|
||||
req: impl ReqFn<T>,
|
||||
) -> Self {
|
||||
Self(
|
||||
Arc::new(Mutex::new(ReqNotData {
|
||||
id: 1,
|
||||
send: Box::new(send),
|
||||
notif: Box::new(notif),
|
||||
req: Box::new(req),
|
||||
responses: HashMap::new(),
|
||||
})),
|
||||
logger,
|
||||
)
|
||||
}
|
||||
|
||||
/// Can be called from a polling thread or dispatched in any other way
|
||||
pub async fn receive(&self, message: &[u8]) {
|
||||
let mut g = self.0.lock().await;
|
||||
let (id, payload) = get_id(message);
|
||||
if id == 0 {
|
||||
let mut notif_cb = clone_box(&*g.notif);
|
||||
mem::drop(g);
|
||||
let notif_val = <T::In as Channel>::Notif::decode(Pin::new(&mut &payload[..])).await;
|
||||
notif_cb(notif_val, self.clone()).await
|
||||
} else if 0 < id.bitand(1 << 63) {
|
||||
let mut sender = g.responses.remove(&!id).expect("Received response for invalid message");
|
||||
let _ = sender.send(message.to_vec()).await;
|
||||
} else {
|
||||
let message = <T::In as Channel>::Req::decode(Pin::new(&mut &payload[..])).await;
|
||||
let mut req_cb = clone_box(&*g.req);
|
||||
mem::drop(g);
|
||||
let rn = self.clone();
|
||||
let rn2 = self.clone();
|
||||
req_cb(
|
||||
ReqReader::new(rn, async move |vec| {
|
||||
let mut buf = (!id).to_be_bytes().to_vec();
|
||||
buf.extend(vec);
|
||||
let mut send = clone_box(&*rn2.0.lock().await.send);
|
||||
(send)(&buf, rn2.clone()).await;
|
||||
}),
|
||||
message,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn notify<N: Coding + Into<<T::Out as Channel>::Notif>>(&self, notif: N) {
|
||||
let mut send = clone_box(&*self.0.lock().await.send);
|
||||
let mut buf = vec![0; 8];
|
||||
let msg: <T::Out as Channel>::Notif = notif.into();
|
||||
msg.encode(Pin::new(&mut buf)).await;
|
||||
send(&buf, self.clone()).await
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DynRequester {
|
||||
type Transfer;
|
||||
fn logger(&self) -> &Logger;
|
||||
/// Encode and send a request, then receive the response buffer.
|
||||
fn raw_request(&self, data: Self::Transfer) -> LocalBoxFuture<'_, RawReply>;
|
||||
}
|
||||
|
||||
pub struct MappedRequester<'a, T: 'a>(Box<dyn Fn(T) -> LocalBoxFuture<'a, RawReply> + 'a>, Logger);
|
||||
impl<'a, T> MappedRequester<'a, T> {
|
||||
fn new<U: DynRequester + 'a, F: Fn(T) -> U::Transfer + 'a>(
|
||||
req: U,
|
||||
cb: F,
|
||||
logger: Logger,
|
||||
) -> Self {
|
||||
let req_arc = Arc::new(req);
|
||||
let cb_arc = Arc::new(cb);
|
||||
MappedRequester(
|
||||
Box::new(move |t| {
|
||||
Box::pin(clone!(req_arc, cb_arc; async move { req_arc.raw_request(cb_arc(t)).await}))
|
||||
}),
|
||||
logger,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DynRequester for MappedRequester<'_, T> {
|
||||
type Transfer = T;
|
||||
fn logger(&self) -> &Logger { &self.1 }
|
||||
fn raw_request(&self, data: Self::Transfer) -> LocalBoxFuture<'_, RawReply> { self.0(data) }
|
||||
}
|
||||
|
||||
impl<T: MsgSet> DynRequester for ReqNot<T> {
|
||||
type Transfer = <T::Out as Channel>::Req;
|
||||
fn logger(&self) -> &Logger { &self.1 }
|
||||
fn raw_request(&self, req: Self::Transfer) -> LocalBoxFuture<'_, RawReply> {
|
||||
Box::pin(async move {
|
||||
let mut g = self.0.lock().await;
|
||||
let id = g.id;
|
||||
g.id += 1;
|
||||
let mut buf = id.to_be_bytes().to_vec();
|
||||
req.encode(Pin::new(&mut buf)).await;
|
||||
let (send, mut recv) = mpsc::channel(1);
|
||||
g.responses.insert(id, send);
|
||||
let mut send = clone_box(&*g.send);
|
||||
mem::drop(g);
|
||||
let rn = self.clone();
|
||||
send(&buf, rn).await;
|
||||
let items = recv.next().await;
|
||||
RawReply(items.unwrap())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Requester: DynRequester {
|
||||
#[must_use = "These types are subject to change with protocol versions. \
|
||||
If you don't want to use the return value, At a minimum, force the type."]
|
||||
fn request<R: Request + Into<Self::Transfer>>(
|
||||
&self,
|
||||
data: R,
|
||||
) -> impl Future<Output = R::Response>;
|
||||
fn map<'a, U>(self, cb: impl Fn(U) -> Self::Transfer + 'a) -> MappedRequester<'a, U>
|
||||
where Self: Sized + 'a {
|
||||
let logger = self.logger().clone();
|
||||
MappedRequester::new(self, cb, logger)
|
||||
}
|
||||
}
|
||||
|
||||
impl<This: DynRequester + ?Sized> Requester for This {
|
||||
async fn request<R: Request + Into<Self::Transfer>>(&self, data: R) -> R::Response {
|
||||
let req = format!("{data:?}");
|
||||
let rep = R::Response::decode(Pin::new(&mut &self.raw_request(data.into()).await[..])).await;
|
||||
let req_str = req.to_string();
|
||||
if !req_str.starts_with("AtomPrint") && !req_str.starts_with("ExtAtomPrint") {
|
||||
writeln!(self.logger(), "Request {req} got response {rep:?}");
|
||||
}
|
||||
rep
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MsgSet> Clone for ReqNot<T> {
|
||||
fn clone(&self) -> Self { Self(self.0.clone(), self.1.clone()) }
|
||||
fork_stream.as_mut().count().await;
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::FutureExt;
|
||||
use futures::join;
|
||||
use futures::lock::Mutex;
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_api_traits::{Channel, Request};
|
||||
use never::Never;
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_api_traits::Request;
|
||||
use test_executors::spin_on;
|
||||
use unsync_pipe::pipe;
|
||||
|
||||
use super::{MsgSet, ReqNot};
|
||||
use crate::logging::Logger;
|
||||
use crate::reqnot::Requester as _;
|
||||
use crate::{api, clone};
|
||||
use crate::reqnot::{ClientExt, NotifReader, io_comm};
|
||||
|
||||
#[derive(Clone, Debug, Coding, PartialEq)]
|
||||
pub struct TestReq(u8);
|
||||
impl Request for TestReq {
|
||||
type Response = u8;
|
||||
}
|
||||
|
||||
pub struct TestChan;
|
||||
impl Channel for TestChan {
|
||||
type Notif = u8;
|
||||
type Req = TestReq;
|
||||
}
|
||||
|
||||
pub struct TestMsgSet;
|
||||
impl MsgSet for TestMsgSet {
|
||||
type In = TestChan;
|
||||
type Out = TestChan;
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, Coding, Hierarchy)]
|
||||
#[extendable]
|
||||
struct TestNotif(u64);
|
||||
|
||||
#[test]
|
||||
fn notification() {
|
||||
spin_on(async {
|
||||
let logger = Logger::new(api::LogStrategy::StdErr);
|
||||
let received = Arc::new(Mutex::new(None));
|
||||
let receiver = ReqNot::<TestMsgSet>::new(
|
||||
logger.clone(),
|
||||
|_, _| panic!("Should not send anything"),
|
||||
clone!(received; move |notif, _| clone!(received; async move {
|
||||
*received.lock().await = Some(notif);
|
||||
}.boxed_local())),
|
||||
|_, _| panic!("Not receiving a request"),
|
||||
let (in1, out2) = pipe(1024);
|
||||
let (in2, out1) = pipe(1024);
|
||||
let received = RefCell::new(None);
|
||||
let (_, run_receiver) = io_comm::<Never, u64>(
|
||||
Rc::new(Mutex::new(Box::pin(in2))),
|
||||
Mutex::new(Box::pin(out2)),
|
||||
async |_, notif: NotifReader| {
|
||||
*received.borrow_mut() = Some(notif.read::<TestNotif>().await)
|
||||
},
|
||||
async |_, _| panic!("Should receive notif, not request"),
|
||||
);
|
||||
let sender = ReqNot::<TestMsgSet>::new(
|
||||
logger,
|
||||
clone!(receiver; move |d, _| clone!(receiver; Box::pin(async move {
|
||||
receiver.receive(d).await
|
||||
}))),
|
||||
|_, _| panic!("Should not receive notif"),
|
||||
|_, _| panic!("Should not receive request"),
|
||||
let (sender, _) = io_comm::<Never, Never>(
|
||||
Rc::new(Mutex::new(Box::pin(in1))),
|
||||
Mutex::new(Box::pin(out1)),
|
||||
async |_, _| panic!("Should not receive notif"),
|
||||
async |_, _| panic!("Should not receive request"),
|
||||
);
|
||||
sender.notify(3).await;
|
||||
assert_eq!(*received.lock().await, Some(3));
|
||||
sender.notify(4).await;
|
||||
assert_eq!(*received.lock().await, Some(4));
|
||||
join!(run_receiver, async {
|
||||
sender.notify(TestNotif(3)).await;
|
||||
assert_eq!(*received.borrow(), Some(TestNotif(3)));
|
||||
sender.notify(TestNotif(4)).await;
|
||||
assert_eq!(*received.borrow(), Some(TestNotif(4)));
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extendable]
|
||||
struct DummyRequest(u64);
|
||||
impl Request for DummyRequest {
|
||||
type Response = u64;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request() {
|
||||
spin_on(async {
|
||||
let logger = Logger::new(api::LogStrategy::StdErr);
|
||||
let receiver = Rc::new(Mutex::<Option<ReqNot<TestMsgSet>>>::new(None));
|
||||
let sender = Rc::new(ReqNot::<TestMsgSet>::new(
|
||||
logger.clone(),
|
||||
clone!(receiver; move |d, _| clone!(receiver; Box::pin(async move {
|
||||
receiver.lock().await.as_ref().unwrap().receive(d).await
|
||||
}))),
|
||||
|_, _| panic!("Should not receive notif"),
|
||||
|_, _| panic!("Should not receive request"),
|
||||
));
|
||||
*receiver.lock().await = Some(ReqNot::new(
|
||||
logger,
|
||||
clone!(sender; move |d, _| clone!(sender; Box::pin(async move {
|
||||
sender.receive(d).await
|
||||
}))),
|
||||
|_, _| panic!("Not receiving notifs"),
|
||||
|hand, req| {
|
||||
Box::pin(async move {
|
||||
assert_eq!(req, TestReq(5));
|
||||
hand.respond(&6u8).await
|
||||
})
|
||||
let (in1, out2) = pipe(1024);
|
||||
let (in2, out1) = pipe(1024);
|
||||
let (_, run_server) = io_comm::<Never, Never>(
|
||||
Rc::new(Mutex::new(Box::pin(in2))),
|
||||
Mutex::new(Box::pin(out2)),
|
||||
async |_, _| panic!("No notifs expected"),
|
||||
async |_, mut req| {
|
||||
let val = req.read_req::<DummyRequest>().await;
|
||||
req.reply(&val, &(val.0 + 1)).await
|
||||
},
|
||||
));
|
||||
let response = sender.request(TestReq(5)).await;
|
||||
assert_eq!(response, 6);
|
||||
);
|
||||
let (client, run_client) = io_comm::<DummyRequest, Never>(
|
||||
Rc::new(Mutex::new(Box::pin(in1))),
|
||||
Mutex::new(Box::pin(out1)),
|
||||
async |_, _| panic!("Not expecting ingress notif"),
|
||||
async |_, _| panic!("Not expecting ingress req"),
|
||||
);
|
||||
join!(run_server, run_client, async {
|
||||
let response = client.request(DummyRequest(5)).await;
|
||||
assert_eq!(response, 6);
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user