-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
e-nal-async: Missing lifetimes in UdpStack
#103
Comments
Supporting the So it seems sending with an arbitrary local address would require us to What's also weird is that there is quite a bit of asymmetry wrt UDP sockets between I'm unsure how to proceed, honestly.
In any case, not having an impl of the UDP socket traits for @chrysn @lulf @Dirbaio @MabezDev Sorry for pinging. Happy to listen to your feedback / comments / guidance. Did I miss the elephant in the room? |
I'm just about to implement this through embassy-net which sits atop smoltcp; will look into this concrete issue once back at work.
|
My own WIP implementation here, if that's helpful. |
... and my fork of EDIT: Don't pay attention to the comments in the fork yet, they are not updated. |
Even if there were I've had a similar issue with RIOT, not because it needs allocations but because it is self-referential and can only live in a pinned location (which would have needed a pinned constructor, which would also alleviate that second issue. In both cases, a sensible workaround is to implement a Stack on I haven't thought the lifetime'd trait through yet -- do you think it has a chance of resolving both issues? Or will we just run into the second issue anyway (unless we do something weird such as passing in something Default capable as an extra argument just to store the buffers)? The bindmultiple concern is separate from this, I'm creating a separate issue that will fullquote and open, because AIU its only overlap with this issue is that it's about that it is hard to implement embedded-nal-async on smoltcp. |
Here's how this problem is solved for the I've - for now - hard-coded the |
Ah thanks. That's the same "we're creating a stack with the capacity of creating N sockets" pattern. Comparing them also shows me why I didn't run into this: As the RIOT socket backends need to be pinned, they already demand that the stack-with-pool-of-sockets is 'static, and thus the types could be as well. And in my PR for embedded-nal-async support in embassy-net I don't even try to implement Socket so far, but merely implement some ConnectedUdp / UnconnectedUdp types. For many applications I think that's even good enough: If a library is providing a CoAP server, it doesn't need to be able to open a UDP socket from a stack, it can just take the UDP socket as an impl. Doing this may be worthwhile as a pattern no matter the outcome of this PR. The lifetime additions you propose in your PR sound reasonable to me -- I overlooked the need for this because the impls I did when prototyping the UDP traits were always either ZSTs or references to something that was static anyway. I'm not a member of this project, but if you file them as a PR I can promise to review them, and from what I see in your fork I'll expect that review to be very positive. (I'm not sure yet what to think of the splitting proposal -- guess I'll have to see how it is used. Also I'm not sure that every socket can be split, given that the underlying implementation may have a single socket object, and even if the split turned it into something like an [edit: Formatting, because Markdown Is Bad] |
Indeed.
I completely agree. Especially in the context of UDP that might even be enough. Yet - if we already have the notion of
Alright, will do!
No - splitting does not use
So the TL;DR here is - lifetimed associated types in |
I'd have to look it up to verify, but I'm relatively sure that RIOT's sockets are a counterexample. There the |
... as for cases for socket splitting:
|
As per above, in both STD as well as smoltcp the waker for reading is different from the waker for writing. So they cannot overwrite each other. Multiple readers or multiple writers is a wholly different topic and there the wakers WILL overwrite each other, but |
@chrysn Sorry, that's a complete digression now, but looking at the RIOT impl it seems sending is actually not really async (though non-blocking), so there should not be a problem with sending and receiving wakers overwriting each other. (I'm not sure what happens if RIOT returns On the other hand - yes - reading does require |
Indeed, now that I checked, RIOT does currently not have a blocking send (but may grow one; line 278: // When setting all this up, we got a &mut self, so we can be sure that there
// is no other thread or task simultaneously trying to receive on this, or even
// doing a blocking send on it (which we currently don't do anyway).
//
// (Otherwise, we'd have to worry about overwriting a callback). -- and then it'll be in a single callback, Your mDNS case mentions that you'd both send responses and do regular multicasts. That sounds like both the request/response part and the regular part would do sending, and unless they're in a single future (which is how I implement coap in embedded-nal-coap), that'd require the sender to be clonable. If we started demanding that, we could just already start doing
Not as it is used: self.socket is a &'static. The internal types have needless generics there though, and if they were used with anything but 'static, this would be UB. Tracked in RIOT-OS/rust-riot-wrappers#81 now, thanks for pointing it out. |
They happen to be in a single future, yes, but I don't think that's exactly what you meant, as in a way - a lot of things end up being in a single future, including how some executors model their execution.
Not necessarily. You can wrap the sender in an async mutex as I do, and it would work. What my splittable traits cannot guarantee, is that the returned read and write parts are |
(I've submitted the #106 now.) I just realized, that even if you have a single waker registration for both sending and receiving (i.e. it is awoken when either of the two happens - i.e. there's space to put the next datagram in the send queue or a new datagram had arrived), this should actually work just fine as long as you use only intra-task concurrency (per boats's terminology). Or to put it simply - if both your sending and receiving code is ultimately ending in a single future, which ends up being a single task (and not two futures spawned on the executor separately and thus being two separate tasks each with its own waker) there will be no CPU-consuming "fight" between the two separate wakers from the two separate tasks for constantly re-registering themselves in the single waker registration slot, because the task is a single one, and thus the waker is a single one. Given that the @Dirbaio sorry for pinging - would appreciate your expertise - and correct me if ^^^ is wrong. |
Connected
,UniquelyBound
andMultiplyBound
associated types are currently not lifetimed.I need them to be, so that I can keep a non-mutable reference to the "Udp Stack" instance in their impls.
While I can
Arc
the "Udp Stack" and then push the arc into the UDP socket impl, this just doesn't feel right.Perhaps it is no coincidence that embassy-net's UDP socket stack is not implementing the e-nal-async traits? I don't think it would be possible anyway, without the above lifetimes.
Background: I stumbled on this by accident, while trying to implement a toy websockets gateway that tries to implement
embedded-nal-async
traits by multiplexing all traffic (UDP and TCP) over a single websocket to an actual STD basedembedded-nal-async
impl.Anyway, I'll also work on a PR that implements a lifetimed version of the UDP associated types on top of
embassy-net
.The text was updated successfully, but these errors were encountered: