Crate async_ffi[][src]

Expand description

FFI-compatible futures

In case of an async program with some async plugins, Futures need to cross the FFI boundary. But Rust currently doesn’t provide stable ABI nor stable layout of related structs like dyn Future and Waker. With this crate, we can easily wrap async blocks or async functions to make this happen.

FfiFuture<T> provides the same function as Box<dyn Future<Output = T> + Send> but FFI-compatible (repr(C)). Any future implementing Send can be converted to FfiFuture<T> by calling into_ffi on it.

FfiFuture<T> also implements Future<Output = T> + Send. You can simply await a FfiFuture<T> like a normal Future to get the output.

There is also a non-Send version LocalFfiFuture<T> working like Box<dyn Future<Output = T>>, which can be used for local future or single-threaded targets. It is ABI-compatible to FfiFuture<T>, but it’s your duty to guarantee that non-Send types never cross thread boundary.

Panics

Unwinding across an FFI boundary is Undefined Behaviour.

Panic in Future::poll

Since the body of async fn is translated to Future::poll by compiler, it is most likely to panic. In this case, the wrapped FfiFuture will catch unwinding with std::panic::catch_unwind, returning FfiPoll::Panicked. And the other side (usually the plugin host) will get this value and explicit panic, just like std::sync::Mutex’s poisoning mechanism.

Panic in Future::drop or any waker vtable functions Waker::*

Unfortunately, this is very difficult to handle since drop cleanup and Waker functions are expected to be infallible. If these functions panic, we would just call std::process::abort.

Example

Provide some async functions in library: (plugin side)

// Compile with `crate-type = ["cdylib"]`.
use async_ffi::{FfiFuture, FutureExt};

#[no_mangle]
pub extern "C" fn work(arg: u32) -> FfiFuture<u32> {
    async move {
        let ret = do_some_io(arg).await;
        do_some_sleep(42).await;
        ret
    }
    .into_ffi()
}

Execute async functions from external library: (host or executor side)

use async_ffi::{FfiFuture, FutureExt};

// #[link(name = "myplugin...")]
extern "C" {
    #[no_mangle]
    fn work(arg: u32) -> FfiFuture<u32>;
}

async fn run_work(arg: u32) -> u32 {
    unsafe { work(arg).await }
}

Structs

The FFI compatible future type with Send bound.

The FFI compatible std::task::Context

The FFI compatible future type without Send bound.

Represents that the poll function panicked.

Enums

The FFI compatible std::task::Poll

Constants

The ABI version of FfiFuture and LocalFfiFuture. Every non-compatible ABI change will increase this number.

Traits

Helper trait to provide convenience methods for converting a std::task::Context to FfiContext

Helper trait to provide conversion from Future to FfiFuture or LocalFfiFuture.

Type Definitions

The FFI compatible future type with Send bound and 'static lifetime, which is needed for most use cases.

The FFI compatible future type without Send bound but with 'static lifetime.