aimux-ffi: C ABI boundary for multi-language bindings.
Provides an opaque handle registry + JSON wire boundary + push callback
stream. Only used by C ABI bindings (Swift / Kotlin / C). Native bindings
(Python / Node / Flutter) bypass this layer and use aimux-providers
directly.
Memory ownership
- Every function returning
*mut c_char— constructors included — transfers ownership to the caller, who MUST free it with [aimux_free_string]. - [
aimux_stream_text] callbacks receive*const c_charpointers that are valid only for the duration of the callback. The callback must copy the data synchronously; the backing buffer is freed when the callback returns.
Concurrency
All async provider work runs on a shared multi-threaded tokio runtime. The
C ABI functions are synchronous: they block_on the runtime until the
operation completes. Callbacks execute on the same thread/call-stack that
invoked the FFI function, so they must not re-enter the FFI layer:
a nested block_on on the same thread makes tokio panic ("Cannot start
a runtime from within a runtime"). Rust's non-unwind extern "C" ABI does
not allow a panic to propagate — the process terminates (and under this
workspace's release profile, panic = "abort", it terminates at the panic
site). Either way the re-entrant call must be rejected before it reaches
the runtime; the thread-local guard in ffi_block_on does that
(issue M7).