-
Notifications
You must be signed in to change notification settings - Fork 964
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
Transport: Poll Transport directly, remove ListenersStream #2652
Conversation
Remove `Transport::Listener: Stream`. Instead require the Transport itself to implement a stream-like API with `Transport::poll`. In case of multiple listeners, transports are now required to handle the multiple listener streams themselves internally.
Remove ListenersStream, instead poll the boxed transport directly in the `Swarm` for `TransportEvent`s (which replace the former `ListenersEvent`s).
Add new struct `GenTcpTransport` as wrapper for `GenTcpConfig` to manage multiple listener streams. This is essentially the old ListenerStream logic from swarm/connection.
Adapt majority of helper transports to the new Transport trait. For most transports this just removes the extra *Listener type and instead implements that logic in `Transport::poll`. To adapt the `Boxed` transport the restriction had to be added that transport is `Unpin`. TODO: check if we can solve polling `Boxed` without the inner Transport being unpin.
core/tests/transport_upgrade.rs
Outdated
match poll_fn(|cx| Pin::new(&mut listener_transport).poll(cx)) | ||
.await |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Polling transports this way is rather inconvenient. Should we auto-implement Stream
for transports?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be in favor yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
impl<T: Transport> Stream for T
sounds good to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
impl<T: Transport> Stream for T sounds good to me
Rust unfortunately does not allow this (I forget that every single time).
I considered adding a StreamTransport
that just wraps a transport and implemented Stream
, but since we already have the Boxed
type I just implemented it for Boxed
and adjusted the tests (bfd5fb0). Also implemented Stream for Edit: with d7f5019 we can now also box the tcp transport and don't need an extra Stream impl anymore.GenTcpTransport
(90721b9) because you can't box a TCP transport directly.
Converted it to a draft for you :) |
I think from an API PoV, this would be better because it unifies handling of new connections to the caller of the This way, concurrent dialling (and aborting the dial at any point) can still be implemented on top and is not a concern of the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
Strong concept ACK for going in this direction. Left some comments :)
core/tests/transport_upgrade.rs
Outdated
match poll_fn(|cx| Pin::new(&mut listener_transport).poll(cx)) | ||
.await |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be in favor yes.
While trying to adapt |
Expanding on this for the case of DNS. Websockets should be analogous. On Does the above make sense? Happy to expand further. We could return a I can not think of a way to get rid of the |
If I am not mistaken the efforts of (1) refactoring In addition, I am sensing that we have rough consensus on (1), namely of getting rid of listeners. Also, please correct me if I am wrong, we agree on (1) being a step forward even if we don't do (2). With all of the above in mind, I suggest we do (1) only for now. Delaying (2) to another pull request. What do you think? |
Haven't thought of this. All the clones only ever call |
Yes makes sense, thanks! I agree then that we need the
We could at least prevent it for users of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made it to the end. Couple of more comments, though nothing major.
Co-authored-by: Max Inden <mail@max-inden.de>
Thank you both for the reviews @mxinden @thomaseizinger! I know it is a large diff, so it's very much appreciated. |
Missing changelog entries. Otherwise this looks good to me 🎉 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. 🚀
@thomaseizinger any objections to merging this?
Nope, not at all! |
Thanks @elenaf9 for taking this from start to finish. Very large undertaking! |
Adapt to the transport changes of libp2p#2652. Note: this is only a draft "to make it work", and not a proper implementation. It does not support listening on multiple addresses. The listening logic with multiple Endpoints will need to be supported for the upstream implementation.
…er (libp2p#2652) Remove the concept of individual `Transport::Listener` streams from `Transport`. Instead the `Transport` is polled directly via `Transport::poll`. The `Transport` is now responsible for driving its listeners.
Description
Opened as a draft: I'd like to open the discussion on whether this direction is desired, and to discuss how this could be implement / how existing transport could be adapted to the change. Not ready for review yet.Remove the concept of
Listener
s from theTransport
trait. Instead require the Transport itself to implement a stream-like API withTransport::poll
. Transport implementations can then internally implement the logic for managing one or multiple listeners.The main motivation for this change is that the concept of multiple listeners is rather TCP specific and introduces unnecessary complexity for the QUIC transport (#2289 (comment)).
Apart from that, this generally follows the same direction as #2648 :
The change includes:
Transport::Listener
and changeTransport::listen_on
to just return aResult<ListenerId, _>
Transport::poll
, which returnsTransportEvent
s (replacing the former `swarm/connection/ListenersEvent). Listeners should be driven here.swarm/connection/ListenersStream
, instead poll the boxed transport directly in the swarmGenTcpConfig
into a new structGenTcpTransport
that implementsTransport
:swarm/connection/ListenersStream
Links to any relevant issues
Relevant discussions:
Transport: Clone
and take&mut
#2529 (comment)quinn-proto
#2289 (comment)StreamMuxer
trait #2648Open Questions
Transport::dial
I did not change how
Transport::dial
works. I originally planned to also removeTransport::Dial
and push that logic intoTransport::poll
as well. The problem with this would be that it does not work well with the concurrent-dial logic.ConcurrentDial
currently drives multiple dial futures (from potentially multiple transports) at the same time. Once the first succeeds, it drops the other ongoing dials. If the Transport would internally manage theDial
future, this logic would not work anymore. What we could do is something like the following:DialId
,ConcurrentDial
tracks the dial-ids of concurrent dialsTransport::abort_dial
that can be called from thePool
once the first dial of a concurrent dialing batch succeedsI am not sure if this is worth the extra complexity. Happy to hear alternative suggestions.
Open TODOs
Transport::remove_listener
quinn-proto
#2289 could be adapted to this change (CC @kpp)Change checklist