Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Threading

Javascript—and by extension, Typescript—is a single-threaded language.

Uniffi purposely does not get involved in threads, but does take its lead from the host language.

React Native

The Rust integration for React Native provides the potential for multi-threaded Rust.

Dispatching to a background thread is an exercise for the developer on the Rust side of the FFI.

But what happens for background threads calling into Javascript? In such cases, because any client callbacks:

  • may return something
  • may throw an error that is declared and expected
  • may throw an unexpected error

the background thread on the Rust side must block, waiting for Javascript callback to complete.

This might lead to a non-obvious deadlock position, if a Mutex is held while the callback is being called, and then is contended by the foreground call into the Rust, e.g. by a update or repaint.

This can be mitigated by one of:

  • ensuring that a Mutex is released before the callback is called
  • making the callback async and scheduling the repaint on the next tick.

WASM

WASM is currently a single-threaded environment. At the time of writing, the migration path to a multi-threaded virtual machine is unclear.

uniffi-rs provides a wasm-unstable-single-threaded feature. This should be enabled in the target crate.

Additionally, the following may be helpful to adapt your Rust for running with uniffi and WASM.

Async trait:

You may have code using the async_trait crate:

#![allow(unused)]
fn main() {
#[uniffi::export]
#[async_trait::async_trait]
pub trait MyRustTrait {
    async fn do_something_on_the_background(&self, ms: u16, who: String) -> String;
}
}

If do_something_on_the_background in turn awaits something in the browser, e.g. a fetch or a timer, these things are not Send, in which case, you should re-write the #[async_trait::async_trait] to not be Send for wasm32 targets.

#![allow(unused)]
fn main() {
#[uniffi::export]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
pub trait MyRustTrait {
    async fn do_something_on_the_background(&self, ms: u16, who: String) -> String;
}
}

Alternatively, you can forgo async_trait altogether, in favor of removing the clippy lint about async functions in traits.

#![allow(unused)]
fn main() {
#[uniffi::export]
#[allow(async_fn_in_trait)]
pub trait MyRustTrait {
    async fn do_something_on_the_background(&self, ms: u16, who: String) -> String;
}
}