lights0123 / async-avr

async/await for AVR with Rust
https://lights0123.com/blog/2020/07/25/async-await-for-avr-with-rust/
Apache License 2.0
33 stars 5 forks source link

[Question] Manually polling for Read instead of using .await? #7

Closed oliverek12 closed 2 years ago

oliverek12 commented 2 years ago

Hello! I'm a new recruit from the C++ world and trying to get some serial comms working on an arduino uno...and thus find myself here. Thanks for the library! I've got a bit of a issue that im a bit stuck on and looking for some insights. I would like to communicate with my board (tx + rx) asynchronously. I have things mostly working except for (I think just some confusion from me) the awaiting/polling of the read()/read_exact() calls. For my application, I do not have a steady stream of rx data to the board, so I wish to do something like this in a loop: "see if there is anything to read, and then yield if not, if yes handle the data". The ability to yield in that loop would mean that I can allow my tx loop to execute. I have tried using .await as it is pretty straight forward...however when I do and have no incoming data, it blocks my single thread on the board which in turn does not allow my tx loop to run.

Basically my question is, for the comment for the Read struct: futures do nothing unless you ".await" or poll them, how do I "poll them" :) ? I'm a bit lost in if that is a something that is intrinsic to this library, is that something I should be handling with something like the futures lib, am I not supposed to be polling this from my application space for some reason, or I'm completely off-base.

Sorry for the long-winded-newb question. Thanks though! Oliver

lights0123 commented 2 years ago

You'll need to use a tool like futures::join! to execute multiple tasks concurrently.

https://github.com/lights0123/async-avr/blob/d11b08993a2a9d6d0f7fa77567fa7aa9c0ee2026/examples/serial.rs#L92

oliverek12 commented 2 years ago

Thanks for the quick reply! Gotcha thanks. My understanding from futures_util::join is that it will only defer to the other async loops to execute when you call Yield. Assuming this is correct, is there any way to do something like serial.read_exact(&mut data).await.unwrap();, in your example,.. but I Yield if there are not enough bytes ready on the serial port yet... if that makes any sense.

To explain in a different way, say I want to serial.write() at 1hz in an async loop, but also serial.read() data that comes in infrequently (much less that 1hz). If I use futures_util::join! for my read and write loops, the .await on the read loop will block my 1hz write loop...assuming I'm understanding correctly?

lights0123 commented 2 years ago

You're correct that you need to yield to the scheduler—however, that happens whenever you use .await: manually Yielding is only necessary when you're performing e.g. a long-running calculation and want to allow other tasks to run periodically.

lights0123 commented 2 years ago

On a low level, if avr-hal reports that no serial information is available, it returns nb::Error::WouldBlock. This library converts that to a Poll::pending: https://github.com/lights0123/async-avr/blob/d11b08993a2a9d6d0f7fa77567fa7aa9c0ee2026/src/lib.rs#L44 This tells the scheduler that it should try again later, but most importantly, this is the yielding mechanism! It says that no work can be performed immediately, so it should go run other tasks.

If I use futures_util::join! for my read and write loops, the .await on the read loop will block my 1hz write loop...assuming I'm understanding correctly?

So that's not correct, because your read call will return Poll::pending, telling the scheduler to go ahead and run other tasks.

oliverek12 commented 2 years ago

Super. Makes sense. Thanks a ton for the info, I really appreciate it. My issue must be a bug elsewhere! Thanks!