Something went wrong. Try again.
🦀🚀 Abstract over `Send` and `!Send` traits crates.io/crates/future_form
Something went wrong. Try again.
5.2 kB · 125 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126//! FFI handle that pairs a future with a stashed effect snapshot.//!//! [`EffectHandle`] extends [`HostHandle`] with a//! per-handle effect cache and an application context pointer. When//! `poll_once()` returns [`Pending`](core::task::Poll::Pending), the//! bridge code snapshots the pending effect into the handle so the host//! can inspect it via [`last_effect`](EffectHandle::last_effect) or//! [`take_effect`](EffectHandle::take_effect) without racing.//!//! ```text//! ┌──────────┐ poll_once() ┌──────────────────────┐//! │ Host │ ──────────────> │ EffectHandle │//! │ │ │ ┌────────────────┐ │//! │ │ <────────────── │ │ HostHandle<F> │ │//! │ │ Pending │ └────────────────┘ │//! │ │ │ last_effect: E? │//! │ reads │ last_effect() │ context: *const Ctx │//! │ effect │ ──────────────> │ │//! │ │ <────────────── │ │//! └──────────┘ └──────────────────────┘//! ```
use core::{future::Future, task::Poll};
use crate::host_handle::HostHandle;
/// FFI handle wrapping a [`HostHandle`] with a stashed effect and context.////// This is the generalized version of an effect-carrying future handle./// It bundles:////// - A [`HostHandle<F>`] for the underlying future/// - A last-observed effect snapshot (`Option<E>`)/// - A context pointer (`*const Ctx`) for application-specific state////// The context pointer is a raw `*const Ctx` because FFI handles typically/// outlive any particular borrow scope. Bridge authors are responsible for/// ensuring the context outlives the handle.////// # Slot ownership////// For concurrent futures, each `EffectHandle` should be paired with/// its own `Arc<EffectSlot>`. The bridge creates the `Arc`, clones it/// into both the async closure and the handle. This gives each future/// an independent effect channel with no capacity limit. See the/// [crate-level docs](crate#slot-ownership) for examples.////// # Type parameters////// - `F`: the future type (e.g., `BoxFuture<'static, T>` or/// `LocalBoxFuture<'static, T>`)/// - `E`: the effect type (e.g., an enum of things the host can provide)/// - `Ctx`: the application context type (e.g., the counter struct)pub struct EffectHandle<F, E, Ctx> { handle: HostHandle<F>, context: *const Ctx, last_effect: Option<E>,}
impl<F, E: core::fmt::Debug, Ctx> core::fmt::Debug for EffectHandle<F, E, Ctx> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("EffectHandle") .field("last_effect", &self.last_effect) .finish_non_exhaustive() }}
// SAFETY: The only non-auto-Send/Sync field is `*const Ctx`. FFI handles// are single-owner — only the host's polling thread touches a given handle.// The raw context pointer is only dereferenced while the handle is alive// and the context has not been freed.unsafe impl<F: Send, E: Send, Ctx> Send for EffectHandle<F, E, Ctx> {}
impl<F: Future + Unpin, E, Ctx> EffectHandle<F, E, Ctx> { /// Poll the underlying future once. pub fn poll_once(&mut self) -> Poll<F::Output> { self.handle.poll_once() }}
impl<F, E, Ctx> EffectHandle<F, E, Ctx> { /// Create a new effect handle. /// /// # Parameters /// /// - `handle`: the underlying future handle /// - `context`: raw pointer to application-specific state (e.g., the /// object whose methods produced the future). The caller must ensure /// this pointer remains valid for the lifetime of the handle. #[must_use] pub const fn new(handle: HostHandle<F>, context: *const Ctx) -> Self { Self { handle, context, last_effect: None, } }
/// Stash an effect snapshot for later host inspection. /// /// Typically called by bridge code after `poll_once()` returns /// `Pending`, using the effect read from the [`EffectSlot`](crate::effect_slot::EffectSlot). pub fn stash_effect(&mut self, effect: E) { self.last_effect = Some(effect); }
/// Take the stashed effect, leaving `None` in its place. pub const fn take_effect(&mut self) -> Option<E> { self.last_effect.take() }
/// Borrow the stashed effect without taking it. pub const fn last_effect(&self) -> Option<&E> { self.last_effect.as_ref() }
/// Access the application context pointer. /// /// The returned pointer is only valid if the context has not been /// freed. The caller must ensure the context outlives the handle. #[must_use] pub const fn context(&self) -> *const Ctx { self.context }}