Async in [no_std] - .await blocking?! and why?

Hi there,

I'm dealing with async/await in a [no_std] environment which runs quite smooth so far. I've implemented my own Runtime to drive Futures to completion.

I have seen this post: https://users.rust-lang.org/t/await-seems-to-be-blocking/45089/3 saying that .await is blocking/waiting for the thing it is used on to finish.

In my current environment (Raspberry Pi) I'm utilizing 4 cores each one running a single "thread". So the Runtime is using all 4 cores and who ever is free picks up the next Future/Task that need to be driven to completion. The thing is, that if .await is really blocking the current "thread" with some kind of loop this could quite easy lead to a dead lock situation where nothing is able to run to completion ?

So the question would be: If .await is blocking, why it is designed like this? Please excuse my ignorance as my current understanding would be that any async function will be translated into a Future by the compiler. On this Future the poll function will be called, given a context and as part of this a Waker that could be signaled once the Future is able to make progress. So in case the Future is returning Poll::Pending it requires to be "re-visited" by the runtime once it got woken.

When writing async functions there is deed no way to access the waker or register it as there is no way to return Poll::Pending from an async function as such - as can be seen in the following simple async function:

async fn simple() -> u32 {
  120u32
}

This will always immediately return Poll::Ready(120) and will never require to be woken.
However, typically (as far as I understand), more complex async functions will await a "hand written" Future that can return Poll::Pending and require to be woken. Therefore I'd assume that the context and the contained waker from Future generated from the compiler for the async function will passed forward to the poll function of the handwritten Future as it is contained in the function body of the async function. This from my point of view should allow to propagate the Poll::Pending to the generated Future and therefore could allow to be re-visited once woken ?

Is my understanding here more or less correct? If so, what could be the reason that .await is actually blocking? Would there be a way to circumvent the blocking nature of .await using combinators like .then on the Futures instead of using async functions to wrap them (properly for a more convinient API) ?

Thanks in advance for sharing your knowledge and experience with this...

While the OS threads are "blocking", underlying physical CPU cores are not blocked but executes other threads and "wake" the blocked thread again when the blocking condition is resolved by some event.

Similarly, while the async tasks are "blocking", underlying OS threads are not blocked but executes other tasks and "wake" the blocked task again when the blocking condition is resolved by some event.

Details are vary e.g.) syscall/userspace, preemptive/corporative etc, but they do similar jobs in high level.

Hi thanks for this first glimbs. However, I'm not running any OS in my scenario, or lets say a custom bare-metal OS that does not have any threads. So each core is executing the async runtime directly - I thought [no_std] was indicating this :blush:

This would be interpreted by me that even if the .await is implemented as a loop on a statemachine the OS might decide to put the thread running this loop aside and pick up another thread?

From the perspective of the runtime, await is not blocking. If the code of the task is "blocked" on an await, that task will just immediately return Pending when polled.

await is only blocking from the perspective of the surrounding code, i.e. the code following the await will not be executed until the awaited future resolves. That's ensured by the poll implementation provided by the compiler for that async function or block.

.await does not block the thread. It will make the future it is used in wait for the thing that is awaited, but the thread is not blocked, and the runtime is able to schedule other things while the future is waiting at the .await.

Using .then and friends is equivalent to using .await.

Your confusion likely stems from people using "blocking" to mean "it waits for the thing to complete".

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.