Name Description Size
future.rs [`RustFuture`] represents a [`Future`] that can be sent to the foreign code over FFI. This type is not instantiated directly, but via the procedural macros, such as `#[uniffi::export]`. # The big picture We implement async foreign functions using a simplified version of the Future API: 0. At startup, register a [RustFutureContinuationCallback] by calling rust_future_continuation_callback_set. 1. Call the scaffolding function to get a [Handle] 2a. In a loop: - Call [rust_future_poll] - Suspend the function until the [rust_future_poll] continuation function is called - If the continuation was function was called with [RustFuturePoll::Ready], then break otherwise continue. 2b. If the async function is cancelled, then call [rust_future_cancel]. This causes the continuation function to be called with [RustFuturePoll::Ready] and the [RustFuture] to enter a cancelled state. 3. Call [rust_future_complete] to get the result of the future. 4. Call [rust_future_free] to free the future, ideally in a finally block. This: - Releases any resources held by the future - Calls any continuation callbacks that have not been called yet Note: Technically, the foreign code calls the scaffolding versions of the `rust_future_*` functions. These are generated by the scaffolding macro, specially prefixed, and extern "C", and manually monomorphized in the case of [rust_future_complete]. See `uniffi_macros/src/setup_scaffolding.rs` for details. ## How does `Future` work exactly? A [`Future`] in Rust does nothing. When calling an async function, it just returns a `Future` but nothing has happened yet. To start the computation, the future must be polled. It returns [`Poll::Ready(r)`][`Poll::Ready`] if the result is ready, [`Poll::Pending`] otherwise. `Poll::Pending` basically means: > Please, try to poll me later, maybe the result will be ready! This model is very different than what other languages do, but it can actually be translated quite easily, fortunately for us! But… wait a minute… who is responsible to poll the `Future` if a `Future` does nothing? Well, it's _the executor_. The executor is responsible _to drive_ the `Future`: that's where they are polled. But… wait another minute… how does the executor know when to poll a [`Future`]? Does it poll them randomly in an endless loop? Well, no, actually it depends on the executor! A well-designed `Future` and executor work as follows. Normally, when [`Future::poll`] is called, a [`Context`] argument is passed to it. It contains a [`Waker`]. The [`Waker`] is built on top of a [`RawWaker`] which implements whatever is necessary. Usually, a waker will signal the executor to poll a particular `Future`. A `Future` will clone or pass-by-ref the waker to somewhere, as a callback, a completion, a function, or anything, to the system that is responsible to notify when a task is completed. So, to recap, the waker is _not_ responsible for waking the `Future`, it _is_ responsible for _signaling_ the executor that a particular `Future` should be polled again. That's why the documentation of [`Poll::Pending`] specifies: > When a function returns `Pending`, the function must also ensure that the > current task is scheduled to be awoken when progress can be made. “awakening” is done by using the `Waker`. [`Future`]: https://doc.rust-lang.org/std/future/trait.Future.html [`Future::poll`]: https://doc.rust-lang.org/std/future/trait.Future.html#tymethod.poll [`Pol::Ready`]: https://doc.rust-lang.org/std/task/enum.Poll.html#variant.Ready [`Poll::Pending`]: https://doc.rust-lang.org/std/task/enum.Poll.html#variant.Pending [`Context`]: https://doc.rust-lang.org/std/task/struct.Context.html [`Waker`]: https://doc.rust-lang.org/std/task/struct.Waker.html [`RawWaker`]: https://doc.rust-lang.org/std/task/struct.RawWaker.html 13511
mod.rs 5666
scheduler.rs 4037
tests.rs 9102