uniffi::ffi

Module rustfuture

Source
Expand description

[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:

  1. At startup, register a RustFutureContinuationCallback by calling rust_future_continuation_callback_set.
  2. Call the scaffolding function to get a RustFutureHandle 2a. In a loop:
  1. Call rust_future_complete to get the result of the future.
  2. 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) 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.

Structs§

  • Opaque handle for a Rust future that’s stored by the foreign language code

Enums§

Functions§

Type Aliases§