Skip to content

Commit 9c83e1d

Browse files
authoredJan 30, 2025··
Merge pull request #2418 from jhugman/jhugman/2417-remove-send-from-wasm32-future
Relax `Send` bound for `Future` for wasm32 targets.
2 parents 80d5ce2 + a80080c commit 9c83e1d

File tree

3 files changed

+68
-10
lines changed

3 files changed

+68
-10
lines changed
 

‎CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ or use UDL with types from more than one crate.
6464

6565
- Switching jinja template engine from askama to rinja.
6666

67+
- For `wasm32` build targets, `Future`s do not have to be `Send` ([#2418](https://github.com/mozilla/uniffi-rs/pull/2418)),
68+
making them compatible with `wasm-bindgen` `Future`s.
69+
6770
### ⚠️ Breaking Changes for external bindings authors ⚠️
6871

6972
- Added the `FfiType::MutReference` variant.

‎uniffi_core/src/ffi/rustfuture/future.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@
7676
//! [`RawWaker`]: https://doc.rust-lang.org/std/task/struct.RawWaker.html
7777
7878
use std::{
79-
future::Future,
8079
marker::PhantomData,
8180
ops::Deref,
8281
panic,
@@ -85,14 +84,14 @@ use std::{
8584
task::{Context, Poll, Wake},
8685
};
8786

88-
use super::{RustFutureContinuationCallback, RustFuturePoll, Scheduler};
87+
use super::{RustFutureContinuationCallback, RustFuturePoll, Scheduler, UniffiCompatibleFuture};
8988
use crate::{rust_call_with_out_status, FfiDefault, LiftArgsError, LowerReturn, RustCallStatus};
9089

9190
/// Wraps the actual future we're polling
9291
struct WrappedFuture<F, T, UT>
9392
where
9493
// See rust_future_new for an explanation of these trait bounds
95-
F: Future<Output = Result<T, LiftArgsError>> + Send + 'static,
94+
F: UniffiCompatibleFuture<Result<T, LiftArgsError>> + 'static,
9695
T: LowerReturn<UT> + Send + 'static,
9796
UT: Send + 'static,
9897
{
@@ -106,7 +105,7 @@ where
106105
impl<F, T, UT> WrappedFuture<F, T, UT>
107106
where
108107
// See rust_future_new for an explanation of these trait bounds
109-
F: Future<Output = Result<T, LiftArgsError>> + Send + 'static,
108+
F: UniffiCompatibleFuture<Result<T, LiftArgsError>> + 'static,
110109
T: LowerReturn<UT> + Send + 'static,
111110
UT: Send + 'static,
112111
{
@@ -186,7 +185,7 @@ where
186185
unsafe impl<F, T, UT> Send for WrappedFuture<F, T, UT>
187186
where
188187
// See rust_future_new for an explanation of these trait bounds
189-
F: Future<Output = Result<T, LiftArgsError>> + Send + 'static,
188+
F: UniffiCompatibleFuture<Result<T, LiftArgsError>> + 'static,
190189
T: LowerReturn<UT> + Send + 'static,
191190
UT: Send + 'static,
192191
{
@@ -196,7 +195,7 @@ where
196195
pub(super) struct RustFuture<F, T, UT>
197196
where
198197
// See rust_future_new for an explanation of these trait bounds
199-
F: Future<Output = Result<T, LiftArgsError>> + Send + 'static,
198+
F: UniffiCompatibleFuture<Result<T, LiftArgsError>> + 'static,
200199
T: LowerReturn<UT> + Send + 'static,
201200
UT: Send + 'static,
202201
{
@@ -212,7 +211,7 @@ where
212211
impl<F, T, UT> RustFuture<F, T, UT>
213212
where
214213
// See rust_future_new for an explanation of these trait bounds
215-
F: Future<Output = Result<T, LiftArgsError>> + Send + 'static,
214+
F: UniffiCompatibleFuture<Result<T, LiftArgsError>> + 'static,
216215
T: LowerReturn<UT> + Send + 'static,
217216
UT: Send + 'static,
218217
{
@@ -267,7 +266,7 @@ where
267266
impl<F, T, UT> Wake for RustFuture<F, T, UT>
268267
where
269268
// See rust_future_new for an explanation of these trait bounds
270-
F: Future<Output = Result<T, LiftArgsError>> + Send + 'static,
269+
F: UniffiCompatibleFuture<Result<T, LiftArgsError>> + 'static,
271270
T: LowerReturn<UT> + Send + 'static,
272271
UT: Send + 'static,
273272
{
@@ -302,7 +301,7 @@ pub trait RustFutureFfi<ReturnType>: Send + Sync {
302301
impl<F, T, UT> RustFutureFfi<T::ReturnType> for RustFuture<F, T, UT>
303302
where
304303
// See rust_future_new for an explanation of these trait bounds
305-
F: Future<Output = Result<T, LiftArgsError>> + Send + 'static,
304+
F: UniffiCompatibleFuture<Result<T, LiftArgsError>> + 'static,
306305
T: LowerReturn<UT> + Send + 'static,
307306
UT: Send + 'static,
308307
{

‎uniffi_core/src/ffi/rustfuture/mod.rs

+57-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,62 @@ pub enum RustFuturePoll {
3030
/// to continue progress on the future.
3131
pub type RustFutureContinuationCallback = extern "C" fn(callback_data: u64, RustFuturePoll);
3232

33+
/// This marker trait allows us to put different bounds on the `Future`s we
34+
/// support, based on `#[cfg(..)]` configuration.
35+
///
36+
/// It should not be considered as a part of the public API, and as such as
37+
/// an implementation detail and subject to change.
38+
///
39+
/// It is _not_ intended to be implemented by libray users or bindings
40+
/// implementors.
41+
#[doc(hidden)]
42+
pub trait UniffiCompatibleFuture<T>: Future<Output = T> {}
43+
44+
/// The `Send` bound is required because the Foreign code may call the
45+
/// `rust_future_*` methods from different threads.
46+
#[cfg(not(target_arch = "wasm32"))]
47+
impl<T, F> UniffiCompatibleFuture<T> for F where F: Future<Output = T> + Send {}
48+
49+
/// `Future`'s on WASM32 are not `Send` because it's a single threaded environment.
50+
///
51+
/// # Safety:
52+
///
53+
/// WASM32 is a single threaded environment. However, in a browser there do
54+
/// exist [`WebWorker`][webworker]s which do not share memory or event-loop
55+
/// with the main browser context.
56+
///
57+
/// Communication between contexts is only possible by message passing,
58+
/// using a small number of ['transferable' object types][transferable].
59+
///
60+
/// The most common source of asynchrony in Rust compiled to WASM is
61+
/// [wasm-bindgen's `JsFuture`][jsfuture]. It is not `Send` because:
62+
///
63+
/// 1. `T` and `E` are both `JsValue`
64+
/// 2. `JsValue` may contain `JsFunction`s, either as a function themselves or
65+
/// an object containing functions.
66+
/// 3. Functions cannot be [serialized and sent][transferable] to `WebWorker`s.
67+
///
68+
/// Implementors of binding generators should be able to enumerate the
69+
/// combinations of Rust or JS communicating across different contexts (here
70+
/// using: <->), and in the same context (+) to account for why it is safe
71+
/// for UniFFI to support `Future`s that are not `Send`:
72+
///
73+
/// 1. JS + Rust in the same contexts: polling and waking happens in the same
74+
/// thread, no `Send` is needed.
75+
/// 2. Rust <-> Rust in different contexts: Futures cannot be sent between JS
76+
/// contexts within the same Rust crate (because they are not `Send`).
77+
/// 3. JS <-> Rust in different contexts: the `Promise` are [not transferable
78+
/// between contexts][transferable], so this is impossible.
79+
/// 4. JS <-> JS + Rust, this is possible, but safe since the Future is being
80+
/// driven by JS in the same thread. If a Promise metaphor is desired, then
81+
/// this must be built with JS talking to JS, because 3.
82+
///
83+
/// [webworker]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
84+
/// [jsfuture]: https://github.com/rustwasm/wasm-bindgen/blob/main/crates/futures/src/lib.rs
85+
/// [transferable]: (https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects
86+
#[cfg(target_arch = "wasm32")]
87+
impl<T, F> UniffiCompatibleFuture<T> for F where F: Future<Output = T> {}
88+
3389
// === Public FFI API ===
3490

3591
/// Create a new [Handle] for a Rust future
@@ -44,7 +100,7 @@ where
44100
// since it will move between threads for an indeterminate amount of time as the foreign
45101
// executor calls polls it and the Rust executor wakes it. It does not need to by `Sync`,
46102
// since we synchronize all access to the values.
47-
F: Future<Output = Result<T, LiftArgsError>> + Send + 'static,
103+
F: UniffiCompatibleFuture<Result<T, LiftArgsError>> + 'static,
48104
// T is the output of the Future. It needs to implement [LowerReturn]. Also it must be Send +
49105
// 'static for the same reason as F.
50106
T: LowerReturn<UT> + Send + 'static,

0 commit comments

Comments
 (0)
Please sign in to comment.