diff --git a/src/rust/async/future.c++ b/src/rust/async/future.c++ index 4d02d73e682..c5b63fdc4e7 100644 --- a/src/rust/async/future.c++ +++ b/src/rust/async/future.c++ @@ -3,22 +3,19 @@ namespace workerd::rust::async { -BoxFutureVoid::BoxFutureVoid(BoxFutureVoid&& other) noexcept: repr(other.repr) { - other.repr = {0, 0}; +template <> +void box_future_drop_in_place(BoxFuture* self) { + box_future_void_drop_in_place(self); } - -BoxFutureVoid::~BoxFutureVoid() noexcept { - if (repr != std::array{0, 0}) { - box_future_void_drop_in_place(this); - } +template <> +bool box_future_poll(BoxFuture& self, const CxxWaker& waker) { + return box_future_void_poll(self, waker); } - -bool BoxFutureVoid::poll(const CxxWaker& waker) noexcept { - return box_future_void_poll(*this, waker); +template <> +bool box_future_poll_with_co_await_waker(BoxFuture& self, const CoAwaitWaker& waker) { + return box_future_void_poll_with_co_await_waker(self, waker); } -bool BoxFutureVoid::poll(const CoAwaitWaker& waker) noexcept { - return box_future_void_poll_with_co_await_waker(*this, waker); -} +// --------------------------------------------------------- } // namespace workerd::rust::async diff --git a/src/rust/async/future.h b/src/rust/async/future.h index 81566c9a5cd..a5399a3b4cf 100644 --- a/src/rust/async/future.h +++ b/src/rust/async/future.h @@ -8,15 +8,33 @@ namespace workerd::rust::async { class CoAwaitWaker; +template +class BoxFuture; + +// Function templates which are explicitly specialized for each instance of BoxFuture. +template +void box_future_drop_in_place(BoxFuture* self); +template +bool box_future_poll(BoxFuture& self, const CxxWaker& waker); +template +bool box_future_poll_with_co_await_waker(BoxFuture& self, const CoAwaitWaker& waker); + // A `Pin>>` owned by C++. // // The only way to construct a BoxFutureVoid is by returning one from a Rust function. // // TODO(now): Figure out how to make this a template, BoxFuture. -class BoxFutureVoid { +template +class BoxFuture { public: - BoxFutureVoid(BoxFutureVoid&&) noexcept; - ~BoxFutureVoid() noexcept; + BoxFuture(BoxFuture&& other) noexcept: repr(other.repr) { + other.repr = {0, 0}; + } + ~BoxFuture() noexcept { + if (repr != std::array{0, 0}) { + box_future_drop_in_place(this); + } + } // This function constructs a `std::task::Context` in Rust wrapping the given `CxxWaker`. It then // calls the future's `Future::poll()` trait function. @@ -35,8 +53,12 @@ class BoxFutureVoid { // The overload accepting a `const CxxWaker&` exists mostly to simplify testing. // // TODO(now): Figure out how to return non-unit/void values and exceptions. - bool poll(const CxxWaker& waker) noexcept; - bool poll(const CoAwaitWaker& waker) noexcept; + bool poll(const CxxWaker& waker) noexcept { + return box_future_poll(*this, waker); + } + bool poll(const CoAwaitWaker& waker) noexcept { + return box_future_poll_with_co_await_waker(*this, waker); + } // Tell cxx-rs that this type follows Rust's move semantics, and can thus be passed across the FFI // boundary. @@ -47,8 +69,30 @@ class BoxFutureVoid { std::array repr; }; +// --------------------------------------------------------- + +template +class BoxFutureFallible: private BoxFuture { +public: + using BoxFuture::BoxFuture; + using BoxFuture::poll; + using typename BoxFuture::IsRelocatable; +}; + +// --------------------------------------------------------- + +using BoxFutureVoid = BoxFuture; + // We define this the pointer typedef so that cxx-rs can associate it with the same pointer type our // drop function uses. using PtrBoxFutureVoid = BoxFutureVoid*; +// --------------------------------------------------------- + +using BoxFutureFallibleVoid = BoxFutureFallible; + +// We define this the pointer typedef so that cxx-rs can associate it with the same pointer type our +// drop function uses. +using PtrBoxFutureFallibleVoid = BoxFutureFallibleVoid*; + } // namespace workerd::rust::async diff --git a/src/rust/async/future.rs b/src/rust/async/future.rs index 0e1af46b897..c0f99223509 100644 --- a/src/rust/async/future.rs +++ b/src/rust/async/future.rs @@ -8,6 +8,8 @@ use cxx::ExternType; use crate::ffi::CoAwaitWaker; use crate::ffi::CxxWaker; +use crate::Result; + // Expose Pin> to C++ as BoxFutureVoid. // // We want to allow C++ to own Rust Futures in a Box. At present, cxx-rs can easily expose Box @@ -33,6 +35,11 @@ impl + Send + 'static> From>> for BoxFuture< } } +#[repr(transparent)] +pub struct PtrBoxFuture(*mut BoxFuture); + +// ======================================================================================= + // TODO(now): Define these trait implementations with a macro? // Safety: The size of a Pin

is the size of P; the size of a Box is the size of a reference to @@ -47,9 +54,6 @@ unsafe impl ExternType for BoxFuture<()> { type Kind = cxx::kind::Trivial; } -#[repr(transparent)] -pub struct PtrBoxFuture(*mut BoxFuture); - // Safety: Raw pointers are the same size in both languages. unsafe impl ExternType for PtrBoxFuture<()> { type Id = cxx::type_id!("workerd::rust::async::PtrBoxFutureVoid"); @@ -76,3 +80,44 @@ pub fn box_future_void_poll_with_co_await_waker( pub unsafe fn box_future_void_drop_in_place(ptr: PtrBoxFuture<()>) { std::ptr::drop_in_place(ptr.0); } + +// --------------------------------------------------------- + +// Safety: The size of a Pin

is the size of P; the size of a Box is the size of a reference to +// T, and references to `dyn Trait` types contain two pointers: one for the object, one for the +// vtable. So, the size of `Pin>>` (a.k.a. `BoxFuture`) is two +// pointers, and that is unlikely to change. +// +// https://doc.rust-lang.org/std/keyword.dyn.html +// - "As such, a dyn Trait reference contains two pointers." +unsafe impl ExternType for BoxFuture> { + type Id = cxx::type_id!("workerd::rust::async::BoxFutureFallibleVoid"); + type Kind = cxx::kind::Trivial; +} + +// Safety: Raw pointers are the same size in both languages. +unsafe impl ExternType for PtrBoxFuture> { + type Id = cxx::type_id!("workerd::rust::async::PtrBoxFutureFallibleVoid"); + type Kind = cxx::kind::Trivial; +} + +pub fn box_future_fallible_void_poll(future: &mut BoxFuture>, waker: &CxxWaker) -> bool { + let waker = Waker::from(waker); + let mut cx = Context::from_waker(&waker); + // TODO(now): Figure out how to propagate value-or-exception. + future.0.as_mut().poll(&mut cx).is_ready() +} + +pub fn box_future_fallible_void_poll_with_co_await_waker( + future: &mut BoxFuture>, + waker: &CoAwaitWaker, +) -> bool { + let waker = Waker::from(waker); + let mut cx = Context::from_waker(&waker); + // TODO(now): Figure out how to propagate value-or-exception. + future.0.as_mut().poll(&mut cx).is_ready() +} + +pub unsafe fn box_future_fallible_void_drop_in_place(ptr: PtrBoxFuture>) { + std::ptr::drop_in_place(ptr.0); +} diff --git a/src/rust/async/lib.rs b/src/rust/async/lib.rs index 9cc32183118..a816d470d24 100644 --- a/src/rust/async/lib.rs +++ b/src/rust/async/lib.rs @@ -3,6 +3,9 @@ pub use await_::GuardedRustPromiseAwaiter; use await_::PtrGuardedRustPromiseAwaiter; mod future; +use future::box_future_fallible_void_drop_in_place; +use future::box_future_fallible_void_poll; +use future::box_future_fallible_void_poll_with_co_await_waker; use future::box_future_void_drop_in_place; use future::box_future_void_poll; use future::box_future_void_poll_with_co_await_waker; @@ -16,6 +19,7 @@ pub use promise::OwnPromiseNode; use promise::PtrOwnPromiseNode; mod test_futures; +use test_futures::new_errored_future_fallible_void; use test_futures::new_layered_ready_future_void; use test_futures::new_naive_select_future_void; use test_futures::new_pending_future_void; @@ -27,6 +31,8 @@ use test_futures::new_wrapped_waker_future_void; mod waker; use waker::RustWaker; +type Result = std::io::Result; + #[cxx::bridge(namespace = "workerd::rust::async")] mod ffi { unsafe extern "C++" { @@ -65,6 +71,9 @@ mod ffi { type BoxFutureVoid = crate::BoxFuture<()>; type PtrBoxFutureVoid = crate::PtrBoxFuture<()>; + + type BoxFutureFallibleVoid = crate::BoxFuture>; + type PtrBoxFutureFallibleVoid = crate::PtrBoxFuture>; } extern "Rust" { @@ -74,6 +83,16 @@ mod ffi { waker: &CoAwaitWaker, ) -> bool; unsafe fn box_future_void_drop_in_place(ptr: PtrBoxFutureVoid); + + fn box_future_fallible_void_poll( + future: &mut BoxFutureFallibleVoid, + waker: &CxxWaker, + ) -> bool; + fn box_future_fallible_void_poll_with_co_await_waker( + future: &mut BoxFutureFallibleVoid, + waker: &CoAwaitWaker, + ) -> bool; + unsafe fn box_future_fallible_void_drop_in_place(ptr: PtrBoxFutureFallibleVoid); } unsafe extern "C++" { @@ -141,5 +160,7 @@ mod ffi { fn new_layered_ready_future_void() -> BoxFutureVoid; fn new_naive_select_future_void() -> BoxFutureVoid; fn new_wrapped_waker_future_void() -> BoxFutureVoid; + + fn new_errored_future_fallible_void() -> BoxFutureFallibleVoid; } } diff --git a/src/rust/async/test_futures.rs b/src/rust/async/test_futures.rs index a118158633a..24bb8731e7f 100644 --- a/src/rust/async/test_futures.rs +++ b/src/rust/async/test_futures.rs @@ -212,3 +212,10 @@ pub fn new_wrapped_waker_future_void() -> BoxFuture<()> { }) .into() } + +use std::io::Error; +use std::io::ErrorKind; + +pub fn new_errored_future_fallible_void() -> BoxFuture> { + Box::pin(std::future::ready(Err(Error::new(ErrorKind::Other, "test error")))).into() +}