embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.25k stars 724 forks source link

UdpSocket created and used inside async function doesn't work properly #2193

Open yanshay opened 10 months ago

yanshay commented 10 months ago

I wrote a small program (ESP32/no_std, using embassy/embassy-net) that sends UDP packets and it work just fine.

When I moved the networking related code into an async function, it stopped working, UDP Packets wouldn't be sent. Everything seemed to progress properly w/o any errors, but the packets weren't sent.

I pinpointed it to the move of the UdpSocket::new into the async function. Once it moved into the async function the packets weren't sent. For the program to work, the UdpSocket has to be created in the main() and all the rest can run inside the async function.

I thought it might have to do with #2175 but the buffers are created in the main function which never ends (so exist before and after the networking code, so no memory issues). I tried letting the executor run after calling the new (using Time::after) , in case something needs to be processed before continuing in the net_task of .

Any ideas what's the reason? Is this maybe by design?

Dirbaio commented 10 months ago

could it be that you're creating the socket, sending some packet, then immediately dropping the socket?

"sending" the packets only enqueues them in the socket buffers, the socket needs to "stick around" for them to actually be sent.

yanshay commented 10 months ago

could it be that you're creating the socket, sending some packet, then immediately dropping the socket?

"sending" the packets only enqueues them in the socket buffers, the socket needs to "stick around" for them to actually be sent.

The buffers are in the main function, so never dropped. I await the send_to and then return from the function, so the socket is dropped then. Does the send_to.await return before the UDP packet is actually sent? If so, then why does it need to be awaited? And how can I wait until it gets sent?

Dirbaio commented 10 months ago

The buffers are in the main function, so never dropped. I await the send_to and then return from the function, so the socket is dropped then.

The buffers don't matter, it's the socket what must not get dropped.

Does the send_to.await return before the UDP packet is actually get sent? If

"send_to" only enqueues them in the socket buffers, the socket needs to "stick around" for them to actually be sent.

If so, then why does it need to be awaited?

if the socket buffers are full, it waits until some packets are sent so there's free space.

And how can I wait until it gets sent?

Currently, the only way is to not drop the socket. We could add a async fn flush(&mut self) to wait for all queued packets to be sent, like TCP has. PRs are welcome if you'd be interested in adding it.