Rust: Cannot define async fn with no_std

Created on 19 Dec 2018  路  23Comments  路  Source: rust-lang/rust

#![feature(async_await, futures_api)]

#![no_std]

pub async fn foo() {
}

(playground) currently fails with

error[E0433]: failed to resolve: could not find `from_generator` in `future`

This should also work with whatever the syntax for await is, currently it fails earlier than this error because of await! only being defined in std, see #56767.

A-async-await AsyncAwait-OnDeck AsyncAwait-Triaged C-bug F-async_await T-compiler WG-embedded

Most helpful comment

Opened https://github.com/rust-lang/rust/pull/69033 with the final fix for this

All 23 comments

cc @phil-opp

Actually, perhaps this is not entirely a duplicate. Regardless, marking as non-blocking.

Marking as not blocking stabilization -- obviously we want things to work from core eventually, but it's ok if that doesn't work to start, and I don't believe there are any major design questions at play here (right @cramertj?)

Yup-- this is an unfortunate restriction, but I think it's not a blocker for an MVP and resolving it requires some more complex investigation into generator arguments + some special-sauce :)

Could someone indicate how difficult this would be to tackle as a new Rust contributor? I fear like this might be a bit over my head, but I'd love to know.

It's not easy. There needs to be a way to pass an argument into the generator resume function which can be accessed by any .await call. Neither generator arguments nor any mechanism for accessing such a bizarrely scoped local variable exist today.

There is a way to implement it today using unsafe, as far as I know this proc-macro based implementation is sound (other than one non-hygienic local variable that could be an unnameable gensym in a compiler implementation). Although, I have seen one benchmark comparing this to the builtin transform and it seems to have much worse performance for some reason (but I don't know how that overhead compares to the actual operations that are going to be occurring inside the async fn).

For platforms with ELF TLS support, https://github.com/sunriseos/core-futures-tls solves this issue. It could be trivially modified for single-core platforms without ELF TLS, but multi-core ones would be much more difficult.

See also https://internals.rust-lang.org/t/pre-rfc-generator-resume-args/10011 for a Pre-RFC for generator resume arguments.

cc @cavedweller

Now that async fn is stabilized, is there any news on this?

@birktj This is fully supported in the compiler, however functions used internally by async/await are not implemented in libcore due to requiring TLS. core-futures-tls uses the unstable #[thread_local] attribute as an alternative.

@leo60228 so if I understand correctly, all that is required to use async fn on a single-core mcu with stable rust is to copy core-futures-tls and removing the #[thread_local] attribute? (Making the context global)

The underlying implementation is depending on it being thread local, it just happens to be sufficient on a MCU that you can define a how thread_local works as a Cell. (which is what core-futures-tls does) This is currently at least a little off on being able to run on stable, the TLS dependency needs to be removed from the underlying implementation.

This won't change the API hence why async/await was stabilizable, it just happens that no_std will get a strict upgrade when this is done. (I keep planning on getting around to it, but it's non-trivial)

@birktj that will not be interrupt-safe, polling a Future inside an interrupt would be a very weird thing to do, but without the context variable being #[thread_local] doing so with an async created Future would allow UB from safe code.

@Nemo157 @birktj On preemption based systems(ones using direct interrupt mapping) that won't be interrupt safe but on cooperative system that has task yielding by the developer (e.g. freertos with coop scheduling) that will work. That said same model can be used in any cooperative system too.

Triage: Highly relevant for embedded folks, tagging WG-embedded.

https://github.com/rust-lang/rfcs/pull/2781 proposes generator resume arguments, which would solve this issue once implemented (stabilization is not necessary).

Actually we shouldn't need an RFC at all for this, generators are experimental anyways

https://github.com/rust-lang/rust/pull/68524 removes one of the roadblocks for this by implementing generator resume arguments

I guess what's left is to update the Future impl of GenFuture to pass the Context instead of (), update the lowering of async fn to get new cx from yield instead of TLS, and move the whole thing from libstd/... to libcore/... ? Ala https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=eea6daa5f5a3b81fd65a3786f760c084

@Arnavion yep, I've started to work on that already. Expect a PR next week.

Opened https://github.com/rust-lang/rust/pull/69033 with the final fix for this

Was this page helpful?
0 / 5 - 0 ratings