Introduced dylib extension format, cleared up shutdown sequence
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
use std::mem;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
use orchid_api::binary::{FutureContextVT, FutureVT, OwnedWakerVT, UnitPoll};
|
||||
use orchid_api::binary::{FutureBin, FutureContextBin, OwnedWakerBin, UnitPoll};
|
||||
|
||||
type WideBox = Box<dyn Future<Output = ()>>;
|
||||
|
||||
static OWNED_VTABLE: RawWakerVTable = RawWakerVTable::new(
|
||||
|data| {
|
||||
let data = unsafe { Rc::<OwnedWakerVT>::from_raw(data as *const _) };
|
||||
let data = unsafe { Rc::<OwnedWakerBin>::from_raw(data as *const _) };
|
||||
let val = RawWaker::new(Rc::into_raw(data.clone()) as *const (), &OWNED_VTABLE);
|
||||
// Clone must create a duplicate of the Rc, so it has to be un-leaked, cloned,
|
||||
// then leaked again.
|
||||
@@ -18,30 +19,32 @@ static OWNED_VTABLE: RawWakerVTable = RawWakerVTable::new(
|
||||
|data| {
|
||||
// Wake must awaken the task and then clean up the state, so the waker must be
|
||||
// un-leaked
|
||||
let data = unsafe { Rc::<OwnedWakerVT>::from_raw(data as *const _) };
|
||||
let data = unsafe { Rc::<OwnedWakerBin>::from_raw(data as *const _) };
|
||||
(data.wake)(data.data);
|
||||
mem::drop(data);
|
||||
},
|
||||
|data| {
|
||||
// Wake-by-ref must awaken the task while preserving the future, so the Rc is
|
||||
// untouched
|
||||
let data = unsafe { (data as *const OwnedWakerVT).as_ref() }.unwrap();
|
||||
let data = unsafe { (data as *const OwnedWakerBin).as_ref() }.unwrap();
|
||||
(data.wake_ref)(data.data);
|
||||
},
|
||||
|data| {
|
||||
// Drop must clean up the state, so the waker must be un-leaked
|
||||
let data = unsafe { Rc::<OwnedWakerVT>::from_raw(data as *const _) };
|
||||
let data = unsafe { Rc::<OwnedWakerBin>::from_raw(data as *const _) };
|
||||
(data.drop)(data.data);
|
||||
mem::drop(data);
|
||||
},
|
||||
);
|
||||
|
||||
struct BorrowedWakerData<'a> {
|
||||
go_around: &'a mut bool,
|
||||
cx: FutureContextVT,
|
||||
cx: FutureContextBin,
|
||||
}
|
||||
static BORROWED_VTABLE: RawWakerVTable = RawWakerVTable::new(
|
||||
|data| {
|
||||
let data = unsafe { (data as *mut BorrowedWakerData).as_mut() }.unwrap();
|
||||
let owned_data = Rc::<OwnedWakerVT>::new((data.cx.waker)(data.cx.data));
|
||||
let owned_data = Rc::<OwnedWakerBin>::new((data.cx.waker)(data.cx.data));
|
||||
RawWaker::new(Rc::into_raw(owned_data) as *const (), &OWNED_VTABLE)
|
||||
},
|
||||
|data| *unsafe { (data as *mut BorrowedWakerData).as_mut() }.unwrap().go_around = true,
|
||||
@@ -51,13 +54,13 @@ static BORROWED_VTABLE: RawWakerVTable = RawWakerVTable::new(
|
||||
|
||||
/// Convert a future to a binary-compatible format that can be sent across
|
||||
/// dynamic library boundaries
|
||||
pub fn future_to_vt<Fut: Future<Output = ()> + 'static>(fut: Fut) -> FutureVT {
|
||||
pub fn future_to_vt<Fut: Future<Output = ()> + 'static>(fut: Fut) -> FutureBin {
|
||||
let wide_box = Box::new(fut) as WideBox;
|
||||
let data = Box::into_raw(Box::new(wide_box));
|
||||
extern "C" fn drop(raw: *const ()) {
|
||||
std::mem::drop(unsafe { Box::<WideBox>::from_raw(raw as *mut _) })
|
||||
mem::drop(unsafe { Box::<WideBox>::from_raw(raw as *mut _) })
|
||||
}
|
||||
extern "C" fn poll(raw: *const (), cx: FutureContextVT) -> UnitPoll {
|
||||
extern "C" fn poll(raw: *const (), cx: FutureContextBin) -> UnitPoll {
|
||||
let mut this = unsafe { Pin::new_unchecked(&mut **(raw as *mut WideBox).as_mut().unwrap()) };
|
||||
loop {
|
||||
let mut go_around = false;
|
||||
@@ -77,22 +80,22 @@ pub fn future_to_vt<Fut: Future<Output = ()> + 'static>(fut: Fut) -> FutureVT {
|
||||
}
|
||||
}
|
||||
}
|
||||
FutureVT { data: data as *const _, drop, poll }
|
||||
FutureBin { data: data as *const _, drop, poll }
|
||||
}
|
||||
|
||||
struct VirtualFuture {
|
||||
vt: FutureVT,
|
||||
vt: FutureBin,
|
||||
}
|
||||
impl Unpin for VirtualFuture {}
|
||||
impl Future for VirtualFuture {
|
||||
type Output = ();
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
extern "C" fn waker(raw: *const ()) -> OwnedWakerVT {
|
||||
extern "C" fn waker(raw: *const ()) -> OwnedWakerBin {
|
||||
let waker = unsafe { (raw as *mut Context).as_mut() }.unwrap().waker().clone();
|
||||
let data = Box::into_raw(Box::<Waker>::new(waker)) as *const ();
|
||||
return OwnedWakerVT { data, drop, wake, wake_ref };
|
||||
return OwnedWakerBin { data, drop, wake, wake_ref };
|
||||
extern "C" fn drop(raw: *const ()) {
|
||||
std::mem::drop(unsafe { Box::<Waker>::from_raw(raw as *mut Waker) })
|
||||
mem::drop(unsafe { Box::<Waker>::from_raw(raw as *mut Waker) })
|
||||
}
|
||||
extern "C" fn wake(raw: *const ()) {
|
||||
unsafe { Box::<Waker>::from_raw(raw as *mut Waker) }.wake();
|
||||
@@ -101,7 +104,7 @@ impl Future for VirtualFuture {
|
||||
unsafe { (raw as *mut Waker).as_mut() }.unwrap().wake_by_ref();
|
||||
}
|
||||
}
|
||||
let cx = FutureContextVT { data: cx as *mut Context as *const (), waker };
|
||||
let cx = FutureContextBin { data: cx as *mut Context as *const (), waker };
|
||||
let result = (self.vt.poll)(self.vt.data, cx);
|
||||
match result {
|
||||
UnitPoll::Pending => Poll::Pending,
|
||||
@@ -115,4 +118,4 @@ impl Drop for VirtualFuture {
|
||||
|
||||
/// Receive a future sent across dynamic library boundaries and convert it into
|
||||
/// an owned object
|
||||
pub fn vt_to_future(vt: FutureVT) -> impl Future<Output = ()> { VirtualFuture { vt } }
|
||||
pub fn vt_to_future(vt: FutureBin) -> impl Future<Output = ()> { VirtualFuture { vt } }
|
||||
|
||||
@@ -299,10 +299,15 @@ impl<'a> MsgWriter<'a> for IoNotifWriter {
|
||||
|
||||
pub struct CommCtx {
|
||||
exit: Sender<()>,
|
||||
o: Rc<Mutex<Pin<Box<dyn AsyncWrite>>>>,
|
||||
}
|
||||
|
||||
impl CommCtx {
|
||||
pub async fn exit(self) { self.exit.clone().send(()).await.expect("quit channel dropped"); }
|
||||
pub async fn exit(self) -> io::Result<()> {
|
||||
self.o.lock().await.as_mut().close().await?;
|
||||
self.exit.clone().send(()).await.expect("quit channel dropped");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Establish bidirectional request-notification communication over a duplex
|
||||
@@ -313,13 +318,14 @@ impl CommCtx {
|
||||
/// check that the correct message families are sent in the correct directions
|
||||
/// across the channel.
|
||||
pub fn io_comm(
|
||||
o: Rc<Mutex<Pin<Box<dyn AsyncWrite>>>>,
|
||||
i: Mutex<Pin<Box<dyn AsyncRead>>>,
|
||||
o: Pin<Box<dyn AsyncWrite>>,
|
||||
i: Pin<Box<dyn AsyncRead>>,
|
||||
) -> (impl Client + 'static, CommCtx, IoCommServer) {
|
||||
let i = Rc::new(i);
|
||||
let i = Rc::new(Mutex::new(i));
|
||||
let o = Rc::new(Mutex::new(o));
|
||||
let (onsub, client) = IoClient::new(o.clone());
|
||||
let (exit, onexit) = channel(1);
|
||||
(client, CommCtx { exit }, IoCommServer { o, i, onsub, onexit })
|
||||
(client, CommCtx { exit, o: o.clone() }, IoCommServer { o, i, onsub, onexit })
|
||||
}
|
||||
pub struct IoCommServer {
|
||||
o: Rc<Mutex<Pin<Box<dyn AsyncWrite>>>>,
|
||||
@@ -345,15 +351,12 @@ impl IoCommServer {
|
||||
let mut g = Bound::async_new(i.clone(), async |i| i.lock().await).await;
|
||||
match u64::decode(g.as_mut()).await {
|
||||
Ok(id) => h.emit(Event::Input(id, g)).await,
|
||||
Err(e)
|
||||
if matches!(
|
||||
e.kind(),
|
||||
io::ErrorKind::BrokenPipe
|
||||
| io::ErrorKind::ConnectionAborted
|
||||
| io::ErrorKind::UnexpectedEof
|
||||
) =>
|
||||
h.emit(Event::Exit).await,
|
||||
Err(e) => return Err(e),
|
||||
Err(e) => match e.kind() {
|
||||
io::ErrorKind::BrokenPipe
|
||||
| io::ErrorKind::ConnectionAborted
|
||||
| io::ErrorKind::UnexpectedEof => h.emit(Event::Exit).await,
|
||||
_ => return Err(e),
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -419,10 +422,8 @@ impl IoCommServer {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use futures::lock::Mutex;
|
||||
use futures::{SinkExt, StreamExt, join};
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_api_traits::Request;
|
||||
@@ -444,9 +445,8 @@ mod test {
|
||||
let (in1, out2) = pipe(1024);
|
||||
let (in2, out1) = pipe(1024);
|
||||
let (received, mut on_receive) = mpsc::channel(2);
|
||||
let (_, recv_ctx, recv_srv) =
|
||||
io_comm(Rc::new(Mutex::new(Box::pin(in2))), Mutex::new(Box::pin(out2)));
|
||||
let (sender, ..) = io_comm(Rc::new(Mutex::new(Box::pin(in1))), Mutex::new(Box::pin(out1)));
|
||||
let (_, recv_ctx, recv_srv) = io_comm(Box::pin(in2), Box::pin(out2));
|
||||
let (sender, ..) = io_comm(Box::pin(in1), Box::pin(out1));
|
||||
join!(
|
||||
async {
|
||||
recv_srv
|
||||
@@ -465,7 +465,7 @@ mod test {
|
||||
assert_eq!(on_receive.next().await, Some(TestNotif(3)));
|
||||
sender.notify(TestNotif(4)).await.unwrap();
|
||||
assert_eq!(on_receive.next().await, Some(TestNotif(4)));
|
||||
recv_ctx.exit().await;
|
||||
recv_ctx.exit().await.unwrap();
|
||||
}
|
||||
);
|
||||
}))
|
||||
@@ -484,10 +484,8 @@ mod test {
|
||||
spin_on(with_logger(logger, async {
|
||||
let (in1, out2) = pipe(1024);
|
||||
let (in2, out1) = pipe(1024);
|
||||
let (_, srv_ctx, srv) =
|
||||
io_comm(Rc::new(Mutex::new(Box::pin(in2))), Mutex::new(Box::pin(out2)));
|
||||
let (client, client_ctx, client_srv) =
|
||||
io_comm(Rc::new(Mutex::new(Box::pin(in1))), Mutex::new(Box::pin(out1)));
|
||||
let (_, srv_ctx, srv) = io_comm(Box::pin(in2), Box::pin(out2));
|
||||
let (client, client_ctx, client_srv) = io_comm(Box::pin(in1), Box::pin(out1));
|
||||
join!(
|
||||
async {
|
||||
srv
|
||||
@@ -513,8 +511,8 @@ mod test {
|
||||
async {
|
||||
let response = client.request(DummyRequest(5)).await.unwrap();
|
||||
assert_eq!(response, 6);
|
||||
srv_ctx.exit().await;
|
||||
client_ctx.exit().await;
|
||||
srv_ctx.exit().await.unwrap();
|
||||
client_ctx.exit().await.unwrap();
|
||||
}
|
||||
);
|
||||
}))
|
||||
@@ -527,9 +525,8 @@ mod test {
|
||||
let (input1, output1) = pipe(1024);
|
||||
let (input2, output2) = pipe(1024);
|
||||
let (reply_client, reply_context, reply_server) =
|
||||
io_comm(Rc::new(Mutex::new(Box::pin(input1))), Mutex::new(Box::pin(output2)));
|
||||
let (req_client, req_context, req_server) =
|
||||
io_comm(Rc::new(Mutex::new(Box::pin(input2))), Mutex::new(Box::pin(output1)));
|
||||
io_comm(Box::pin(input1), Box::pin(output2));
|
||||
let (req_client, req_context, req_server) = io_comm(Box::pin(input2), Box::pin(output1));
|
||||
let reply_context = RefCell::new(Some(reply_context));
|
||||
let (exit, onexit) = futures::channel::oneshot::channel::<()>();
|
||||
join!(
|
||||
@@ -539,7 +536,7 @@ mod test {
|
||||
async |hand| {
|
||||
let _notif = hand.read::<TestNotif>().await.unwrap();
|
||||
let context = reply_context.borrow_mut().take().unwrap();
|
||||
context.exit().await;
|
||||
context.exit().await?;
|
||||
Ok(())
|
||||
},
|
||||
async |mut hand| {
|
||||
|
||||
Reference in New Issue
Block a user