ZettaScaleLabs / stabby

A Stable ABI for Rust with compact sum-types
Other
317 stars 12 forks source link

How to use stabby futures #56

Closed kylezs closed 5 months ago

kylezs commented 6 months ago

It's not clear to me what's meant by the comment on the futures module.

/// Futures can be ABI-stable if you wish hard enough
#[cfg_attr(
    feature = "unsafe_wakers",
    deprecated = "Warning! you are using the `stabby/unsafe_wakers` feature. This could cause UB if you poll a future received from another shared library with mismatching ABI! (this API isn't actually deprecated)"
)]

I've currently got a simple entry point that returns a future:

#[stabby::import(name = "first_shared_lib", canaries = "none")]
extern "C" {
    fn entry_point(
        args: crate::MyArgsMirror,
    ) -> stabby::future::DynFuture<'static, stabby::result::Result<(), ()>>;
}

This works, the code is called and returned correctly (nb: both are compiled with the same version here, but they won't necessarily be in the future - hence I'm trying to use stabby) - however I get a warning:

warning: `extern` block uses type `RawWaker`, which is not FFI-safe
  --> runner/src/main.rs:14:10
   |
14 |     ) -> stabby::future::DynFuture<'static, stabby::result::Result<(), ()>>;
   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
   |
   = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
   = note: this struct has unspecified layout
   = note: `#[warn(improper_ctypes)]` on by default

What's the intended way for this to work? Are there more detailed examples somewhere?

Thanks!

p-avital commented 5 months ago

Hi there,

Sorry for taking so long to reply, this fell through while I was on holiday.

The unsafe_wakers feature exists for users who do compiler-version pinning and use canaries (ideally without disabling any of them) to detect if compiler options that might affect ABI have occurred. Even if you do pin compiler version and options, there's no guarantee the layout of the standard waker won't change between compiling the dynamic library and its host, hence their name.

I strongly advise against using that feature (which has now become a cfg to let the final crate in the compile chain have final say on whether or not it is enabled, regardless of the dependencies) if you don't do compiler pinning, as this will be a very fast way to get UB.

In general, I'd advise you stick with safe wakers until you've measured that the (small) overhead they add is unacceptable for your application (and that compiler pinning is an option for you).

Finally, I'm working (at the slow pace that my schedule and small experience in rustc's codebase dictate) on an RFC to make futures generic over their wakers, allowing the existence of stable wakers with a stable ABI without compromising the current wakers' API and performance.

If you need ABI-stability and 0-overhead cross-FFI futures (arguably, this need is unlikely since the overhead is negligible compared to the typical time-scales of async workloads), I'd appreciate any support you can give to this RFC to try and get traction for it :)

p-avital commented 5 months ago

Closing this issue as it doesn't really have a CTA. Feel free to reopen it if you have more questions on the topic that you feel haven't been addressed :)