/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! This module defines a Rust Future that wraps an async foreign function call. //! //! The general idea is to create a oneshot channel, hand the sender to the foreign side, and //! await the receiver side on the Rust side. //! //! The foreign side should: //! * Input a [ForeignFutureCallback] and a `u64` handle in their scaffolding function. //! This is the sender, converted to a raw pointer, and an extern "C" function that sends the result. //! * Call the [ForeignFutureCallback] when the async function completes with: //! * The `u64` handle initially passed in //! * The `ForeignFutureResult` struct for the call //! * Optionally, set the `ForeignFutureDroppedCallback` value if you want to be notified when the //! Future is dropped in Rust. For languages that support it, this can be hooked up to //! cancelling the async task for the method.
usecrate::{oneshot, LiftReturn, RustCallStatus};
/// Callback that's passed to a foreign async functions. /// /// See `LiftReturn` trait for how this is implemented. pubtype ForeignFutureCallback<FfiType> = extern"C"fn(oneshot_handle: u64, ForeignFutureResult<FfiType>);
/// C struct that represents the result of a foreign future #[repr(C)] pubstruct ForeignFutureResult<T> { // Note: for void returns, T is `()`, which isn't directly representable with C since it's a ZST. // Foreign code should treat that case as if there was no `return_value` field.
return_value: T,
call_status: RustCallStatus,
}
/// C callback function that's called when the Rust side of a foreign future is dropped pubtype ForeignFutureDroppedCallback = extern"C"fn(data: u64);
/// C struct that represents a foreign future dropped callback. #[repr(C)] pubstruct ForeignFutureDroppedCallbackStruct { pub callback_data: u64, pub callback: ForeignFutureDroppedCallback,
}
impl Default for ForeignFutureDroppedCallbackStruct { /// The default value implements a no-op callback. /// This will be used for languages that don't set their own callbacks. fn default() -> Self { extern"C"fn callback(_data: u64) {} Self {
callback_data: 0,
callback,
}
}
}
impl Drop for ForeignFutureDroppedCallbackStruct { fn drop(&mutself) {
(self.callback)(self.callback_data)
}
}
unsafeimpl Send for ForeignFutureDroppedCallbackStruct {}
pubasyncfn foreign_async_call<F, T, UT>(call_scaffolding_function: F) -> T where
F: FnOnce(ForeignFutureCallback<T::ReturnType>, u64, &mut ForeignFutureDroppedCallbackStruct),
T: LiftReturn<UT>,
{ // Create a oneshot channel that will receive the result of the callback method let (sender, receiver) = oneshot::channel::<ForeignFutureResult<T::ReturnType>>(); // Create complete callback/data from the oneshot channel let complete_callback = foreign_future_complete::<T, UT>; let complete_callback_data = sender.into_raw() as u64; // Create a `ForeignFutureDroppedCallback`. // Since this is owned by the `Future` that we're generating, when the future is dropped it // will be dropped to. // future is. letmut foreign_future_dropped_callback = ForeignFutureDroppedCallbackStruct::default(); // Call the async method
call_scaffolding_function(
complete_callback,
complete_callback_data,
&mut foreign_future_dropped_callback,
); // Await the result and use it to return a value let result = receiver.await;
T::lift_foreign_return(result.return_value, result.call_status)
}
#[test] fn test_drop_after_complete() { letmut 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())
); // Dropping the future after it's complete should not panic, and not cause a double-free
mock_foreign_future.drop_future();
assert_eq!(mock_foreign_future.free_count(), 1);
}
#[test] fn test_drop_before_complete() { letmut mock_foreign_future = MockForeignFuture::new();
mock_foreign_future.complete_success("It worked!".to_owned()); // Dropping the future before it's complete should cancel the future
assert_eq!(mock_foreign_future.free_count(), 0);
mock_foreign_future.drop_future();
assert_eq!(mock_foreign_future.free_count(), 1);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.