1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 //! This module defines a Rust Future that wraps an async foreign function call.
7 //! The general idea is to create a [oneshot::Channel], hand the sender to the foreign side, and
8 //! await the receiver side on the Rust side.
10 //! The foreign side should:
11 //! * Input a [ForeignFutureCallback] and a `u64` handle in their scaffolding function.
12 //! This is the sender, converted to a raw pointer, and an extern "C" function that sends the result.
13 //! * Return a [ForeignFuture], which represents the foreign task object corresponding to the async function.
14 //! * Call the [ForeignFutureCallback] when the async function completes with:
15 //! * The `u64` handle initially passed in
16 //! * The `ForeignFutureResult` for the call
17 //! * Wait for the [ForeignFutureHandle::free] function to be called to free the task object.
18 //! If this is called before the task completes, then the task will be cancelled.
20 use crate::{LiftReturn, RustCallStatus, UnexpectedUniFFICallbackError};
22 /// Handle for a foreign future
23 pub type ForeignFutureHandle = u64;
25 /// Handle for a callback data associated with a foreign future.
26 pub type ForeignFutureCallbackData = *mut ();
28 /// Callback that's passed to a foreign async functions.
30 /// See `LiftReturn` trait for how this is implemented.
31 pub type ForeignFutureCallback<FfiType> =
32 extern "C" fn(oneshot_handle: u64, ForeignFutureResult<FfiType>);
34 /// C struct that represents the result of a foreign future
36 pub struct ForeignFutureResult<T> {
37 // Note: for void returns, T is `()`, which isn't directly representable with C since it's a ZST.
38 // Foreign code should treat that case as if there was no `return_value` field.
40 call_status: RustCallStatus,
43 /// Perform a call to a foreign async method
45 /// C struct that represents the foreign future.
47 /// This is what's returned by the async scaffolding functions.
49 pub struct ForeignFuture {
50 pub handle: ForeignFutureHandle,
51 pub free: extern "C" fn(handle: ForeignFutureHandle),
54 impl Drop for ForeignFuture {
56 (self.free)(self.handle)
60 unsafe impl Send for ForeignFuture {}
62 pub async fn foreign_async_call<F, T, UT>(call_scaffolding_function: F) -> T
64 F: FnOnce(ForeignFutureCallback<T::ReturnType>, u64) -> ForeignFuture,
67 let (sender, receiver) = oneshot::channel::<ForeignFutureResult<T::ReturnType>>();
68 // Keep the ForeignFuture around, even though we don't ever use it.
69 // The important thing is that the ForeignFuture will be dropped when this Future is.
71 call_scaffolding_function(foreign_future_complete::<T, UT>, sender.into_raw() as u64);
72 match receiver.await {
73 Ok(result) => T::lift_foreign_return(result.return_value, result.call_status),
75 // This shouldn't happen in practice, but we can do our best to recover
76 T::handle_callback_unexpected_error(UnexpectedUniFFICallbackError::new(format!(
77 "Error awaiting foreign future: {e}"
83 pub extern "C" fn foreign_future_complete<T: LiftReturn<UT>, UT>(
85 result: ForeignFutureResult<T::ReturnType>,
87 let channel = unsafe { oneshot::Sender::from_raw(oneshot_handle as *mut ()) };
88 // Ignore errors in send.
90 // Error means the receiver was already dropped which will happen when the future is cancelled.
91 let _ = channel.send(result);
97 use crate::{Lower, RustBuffer};
98 use once_cell::sync::OnceCell;
103 atomic::{AtomicU32, Ordering},
106 task::{Context, Poll, Wake},
109 struct MockForeignFuture {
110 freed: Arc<AtomicU32>,
111 callback_info: Arc<OnceCell<(ForeignFutureCallback<RustBuffer>, u64)>>,
112 rust_future: Option<Pin<Box<dyn Future<Output = String>>>>,
115 impl MockForeignFuture {
117 let callback_info = Arc::new(OnceCell::new());
118 let freed = Arc::new(AtomicU32::new(0));
120 let rust_future: Pin<Box<dyn Future<Output = String>>> = {
121 let callback_info = callback_info.clone();
122 let freed = freed.clone();
123 Box::pin(foreign_async_call::<_, String, crate::UniFfiTag>(
124 move |callback, data| {
125 callback_info.set((callback, data)).unwrap();
127 handle: Arc::into_raw(freed) as *mut () as u64,
133 let rust_future = Some(rust_future);
134 let mut mock_foreign_future = Self {
139 // Poll the future once, to start it up. This ensures that `callback_info` is set.
140 let _ = mock_foreign_future.poll();
144 fn poll(&mut self) -> Poll<String> {
145 let waker = Arc::new(NoopWaker).into();
146 let mut context = Context::from_waker(&waker);
154 fn complete_success(&self, value: String) {
155 let (callback, data) = self.callback_info.get().unwrap();
158 ForeignFutureResult {
159 return_value: <String as Lower<crate::UniFfiTag>>::lower(value),
160 call_status: RustCallStatus::new(),
165 fn complete_error(&self, error_message: String) {
166 let (callback, data) = self.callback_info.get().unwrap();
169 ForeignFutureResult {
170 return_value: RustBuffer::default(),
171 call_status: RustCallStatus::error(error_message),
176 fn drop_future(&mut self) {
177 self.rust_future = None
180 fn free_count(&self) -> u32 {
181 self.freed.load(Ordering::Relaxed)
184 extern "C" fn free(handle: u64) {
185 let flag = unsafe { Arc::from_raw(handle as *mut AtomicU32) };
186 flag.fetch_add(1, Ordering::Relaxed);
192 impl Wake for NoopWaker {
193 fn wake(self: Arc<Self>) {}
197 fn test_foreign_future() {
198 let mut mock_foreign_future = MockForeignFuture::new();
199 assert_eq!(mock_foreign_future.poll(), Poll::Pending);
200 mock_foreign_future.complete_success("It worked!".to_owned());
202 mock_foreign_future.poll(),
203 Poll::Ready("It worked!".to_owned())
205 // Since the future is complete, it should free the foreign future
206 assert_eq!(mock_foreign_future.free_count(), 1);
211 fn test_foreign_future_error() {
212 let mut mock_foreign_future = MockForeignFuture::new();
213 assert_eq!(mock_foreign_future.poll(), Poll::Pending);
214 mock_foreign_future.complete_error("It Failed!".to_owned());
215 let _ = mock_foreign_future.poll();
219 fn test_drop_after_complete() {
220 let mut mock_foreign_future = MockForeignFuture::new();
221 mock_foreign_future.complete_success("It worked!".to_owned());
222 assert_eq!(mock_foreign_future.free_count(), 0);
224 mock_foreign_future.poll(),
225 Poll::Ready("It worked!".to_owned())
227 // Dropping the future after it's complete should not panic, and not cause a double-free
228 mock_foreign_future.drop_future();
229 assert_eq!(mock_foreign_future.free_count(), 1);
233 fn test_drop_before_complete() {
234 let mut mock_foreign_future = MockForeignFuture::new();
235 mock_foreign_future.complete_success("It worked!".to_owned());
236 // Dropping the future before it's complete should cancel the future
237 assert_eq!(mock_foreign_future.free_count(), 0);
238 mock_foreign_future.drop_future();
239 assert_eq!(mock_foreign_future.free_count(), 1);