rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.08k stars 12.69k forks source link

Cannot define async fn with no_std #56974

Closed Nemo157 closed 4 years ago

Nemo157 commented 5 years ago
#![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.

oli-obk commented 5 years ago

cc @phil-opp

nikomatsakis commented 5 years ago

Closing as duplicate of https://github.com/rust-lang/rust/issues/56974.

nikomatsakis commented 5 years ago

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

nikomatsakis commented 5 years ago

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?)

cramertj commented 5 years ago

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 :)

rubdos commented 5 years ago

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.

cramertj commented 5 years ago

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.

Nemo157 commented 5 years ago

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).

leo60228 commented 5 years ago

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.

phil-opp commented 5 years ago

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

cramertj commented 5 years ago

cc @cavedweller

birktj commented 4 years ago

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

leo60228 commented 4 years ago

@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.

birktj commented 4 years ago

@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)

benbrittain commented 4 years ago

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)

Nemo157 commented 4 years ago

@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.

vertexclique commented 4 years ago

@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.

jonas-schievink commented 4 years ago

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).

jonas-schievink commented 4 years ago

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

jonas-schievink commented 4 years ago

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

Arnavion commented 4 years ago

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

jonas-schievink commented 4 years ago

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

jonas-schievink commented 4 years ago

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