use crate::{oneshot, LiftReturn, RustCallStatus};
pub type ForeignFutureHandle = u64;
pub type ForeignFutureCallbackData = *mut ();
pub type ForeignFutureCallback<FfiType> =
extern "C" fn(oneshot_handle: u64, ForeignFutureResult<FfiType>);
#[repr(C)]
pub struct ForeignFutureResult<T> {
return_value: T,
call_status: RustCallStatus,
}
#[repr(C)]
pub struct ForeignFuture {
pub handle: ForeignFutureHandle,
pub free: extern "C" fn(handle: ForeignFutureHandle),
}
impl Drop for ForeignFuture {
fn drop(&mut self) {
(self.free)(self.handle)
}
}
unsafe impl Send for ForeignFuture {}
pub async fn foreign_async_call<F, T, UT>(call_scaffolding_function: F) -> T
where
F: FnOnce(ForeignFutureCallback<T::ReturnType>, u64) -> ForeignFuture,
T: LiftReturn<UT>,
{
let (sender, receiver) = oneshot::channel::<ForeignFutureResult<T::ReturnType>>();
let _foreign_future =
call_scaffolding_function(foreign_future_complete::<T, UT>, sender.into_raw() as u64);
let result = receiver.await;
T::lift_foreign_return(result.return_value, result.call_status)
}
pub extern "C" fn foreign_future_complete<T: LiftReturn<UT>, UT>(
oneshot_handle: u64,
result: ForeignFutureResult<T::ReturnType>,
) {
let channel = unsafe { oneshot::Sender::from_raw(oneshot_handle as *mut ()) };
channel.send(result);
}
#[cfg(test)]
mod test {
use super::*;
use crate::{Lower, RustBuffer};
use once_cell::sync::OnceCell;
use std::{
future::Future,
pin::Pin,
sync::{
atomic::{AtomicU32, Ordering},
Arc,
},
task::{Context, Poll, Wake},
};
struct MockForeignFuture {
freed: Arc<AtomicU32>,
callback_info: Arc<OnceCell<(ForeignFutureCallback<RustBuffer>, u64)>>,
rust_future: Option<Pin<Box<dyn Future<Output = String>>>>,
}
impl MockForeignFuture {
fn new() -> Self {
let callback_info = Arc::new(OnceCell::new());
let freed = Arc::new(AtomicU32::new(0));
let rust_future: Pin<Box<dyn Future<Output = String>>> = {
let callback_info = callback_info.clone();
let freed = freed.clone();
Box::pin(foreign_async_call::<_, String, crate::UniFfiTag>(
move |callback, data| {
callback_info.set((callback, data)).unwrap();
ForeignFuture {
handle: Arc::into_raw(freed) as *mut () as u64,
free: Self::free,
}
},
))
};
let rust_future = Some(rust_future);
let mut mock_foreign_future = Self {
freed,
callback_info,
rust_future,
};
let _ = mock_foreign_future.poll();
mock_foreign_future
}
fn poll(&mut self) -> Poll<String> {
let waker = Arc::new(NoopWaker).into();
let mut context = Context::from_waker(&waker);
self.rust_future
.as_mut()
.unwrap()
.as_mut()
.poll(&mut context)
}
fn complete_success(&self, value: String) {
let (callback, data) = self.callback_info.get().unwrap();
callback(
*data,
ForeignFutureResult {
return_value: <String as Lower<crate::UniFfiTag>>::lower(value),
call_status: RustCallStatus::new(),
},
);
}
fn complete_error(&self, error_message: String) {
let (callback, data) = self.callback_info.get().unwrap();
callback(
*data,
ForeignFutureResult {
return_value: RustBuffer::default(),
call_status: RustCallStatus::error(error_message),
},
);
}
fn drop_future(&mut self) {
self.rust_future = None
}
fn free_count(&self) -> u32 {
self.freed.load(Ordering::Relaxed)
}
extern "C" fn free(handle: u64) {
let flag = unsafe { Arc::from_raw(handle as *mut AtomicU32) };
flag.fetch_add(1, Ordering::Relaxed);
}
}
struct NoopWaker;
impl Wake for NoopWaker {
fn wake(self: Arc<Self>) {}
}
#[test]
fn test_foreign_future() {
let mut mock_foreign_future = MockForeignFuture::new();
assert_eq!(mock_foreign_future.poll(), Poll::Pending);
mock_foreign_future.complete_success("It worked!".to_owned());
assert_eq!(
mock_foreign_future.poll(),
Poll::Ready("It worked!".to_owned())
);
assert_eq!(mock_foreign_future.free_count(), 1);
}
#[test]
#[should_panic]
fn test_foreign_future_error() {
let mut mock_foreign_future = MockForeignFuture::new();
assert_eq!(mock_foreign_future.poll(), Poll::Pending);
mock_foreign_future.complete_error("It Failed!".to_owned());
let _ = mock_foreign_future.poll();
}
#[test]
fn test_drop_after_complete() {
let mut mock_foreign_future = MockForeignFuture::new();
mock_foreign_future.complete_success("It worked!".to_owned());
assert_eq!(mock_foreign_future.free_count(), 0);
assert_eq!(
mock_foreign_future.poll(),
Poll::Ready("It worked!".to_owned())
);
mock_foreign_future.drop_future();
assert_eq!(mock_foreign_future.free_count(), 1);
}
#[test]
fn test_drop_before_complete() {
let mut mock_foreign_future = MockForeignFuture::new();
mock_foreign_future.complete_success("It worked!".to_owned());
assert_eq!(mock_foreign_future.free_count(), 0);
mock_foreign_future.drop_future();
assert_eq!(mock_foreign_future.free_count(), 1);
}
}