-
Notifications
You must be signed in to change notification settings - Fork 1.1k
refactor(tcp): use SelectAll for driving listener streams #3361
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
Conversation
acef5d2
to
acf8411
Compare
acf8411
to
7491aef
Compare
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.
Hi, and thanks for tackling this! Some notes left
8880b24
to
e054223
Compare
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.
A few comments, otherwise LGTM.
Clean work, thank you!
transports/tcp/src/lib.rs
Outdated
if let Some(event) = me.queued_events.pop_front() { | ||
return Poll::Ready(Some(event)); | ||
} | ||
|
||
if me.is_closed { | ||
// Terminate the stream if the listener closed and all remaining events have been reported. | ||
return Poll::Ready(None); | ||
} |
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.
We now have two return paths here that don't register their own waker. You'll need to store a waker in the listener before you return Poll::Pending
at the bottom.
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 lack expertise on this particular issue. Instead I have aligned the impl for the tcp
transport with the impl for the quic
transport, they are pretty similar.
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.
@elenaf9 Did you consider this case when implementing QUIC? I think we may have a potential unwoken task here:
- Assume 1 active listener
- Assume the listener had no work and returned
Pending
- We call
close
on the listener - Without an explicit waker, I believe the close will only happen once the listener gets polled again due to another event
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.
Well, it appears this PR can't be merged before this question is cleared out.
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.
We can move forward here by just adding the waker. Worst case, we have an unnecessary wake-up.
- Make a new field inside the listener, holding an
Option<Waker>
- Call it
close_listener_waker
- Set it before you return
Poll::Pending
at the bottom - Use
take
to remove the listener withinclose
and if it is present, callwake
on it
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.
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.
In my eyes, the suggestion above by @thomaseizinger is the way to go.
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.
Good catch @thomaseizinger. Yes you are right, we need the explicit waker here.
c1eba3d
to
8980a45
Compare
transports/tcp/src/lib.rs
Outdated
if let Some(event) = me.queued_events.pop_front() { | ||
return Poll::Ready(Some(event)); | ||
} | ||
|
||
if me.is_closed { | ||
// Terminate the stream if the listener closed and all remaining events have been reported. | ||
return Poll::Ready(None); | ||
} |
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.
In my eyes, the suggestion above by @thomaseizinger is the way to go.
44b83d9
to
ab06ff8
Compare
@mxinden @thomaseizinger
However,
error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq`
Error: --> muxers/mplex/src/codec.rs:68:14
|
68 | #![allow(clippy::derive_hash_xor_eq)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq`
|
= note: `-D renamed-and-removed-lints` implied by `-D warnings` if applied will also break the build stating that such a lint is not known... can someone confirm? |
This pull request has merge conflicts. Could you please resolve them @vnermolaev? 🙏 |
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 for tackling this @vnermolaev!
👍 LGTM
- now, there are linting errors coming from parts other than the TCP transport. Should they be addressed separately?
They are already addressed in #3389. Would you mind reverting ab06ff8?
- The issue with sending the last event on closing and thus setting up a waker, I assume, also exists in other transports as it was the case and this one.
Yes you are right. Would you be interested in doing patches to fix them there as well (in separate PRs; namely for libp2p-quic
and libp2p-webrtc
)?
transports/tcp/src/lib.rs
Outdated
if let Some(event) = me.queued_events.pop_front() { | ||
return Poll::Ready(Some(event)); | ||
} | ||
|
||
if me.is_closed { | ||
// Terminate the stream if the listener closed and all remaining events have been reported. | ||
return Poll::Ready(None); | ||
} |
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.
Good catch @thomaseizinger. Yes you are right, we need the explicit waker here.
ab06ff8
to
df3fa05
Compare
@elenaf9 I have rebased on top of the latest master, excluding the mentioned commit, clippy still fails on sources outside the TCP transport, i.e., |
Yes, #3389 is not merged yet. The failing check however is only the |
then I think it is ready to be merged :)
Yes, I am interested. |
transports/tcp/CHANGELOG.md
Outdated
@@ -17,9 +17,12 @@ | |||
|
|||
- Update `rust-version` to reflect the actual MSRV: 1.60.0. See [PR 3090]. | |||
|
|||
- Optimize listeners polling behaviour in the tcp transport, fixing [2781](https://github.com/libp2p/rust-libp2p/issues/2781). See [PR 3361]. |
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.
Do we need this? It is mostly a refactoring that is not user facing.
Could mention the bug fix but otherwise I'd suggest not adding a changelog entry.
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.
removed.
Co-authored-by: Elena Frank <[email protected]>
All right, all suggestions/requests are satisfied. Checking libp2p-request-response v0.24.0 (/home/runner/work/rust-libp2p/rust-libp2p/protocols/request-response)
error[E0369]: cannot multiply `&&EdwardsBasepointTable` by `&Scalar`
--> /home/runner/.cargo/registry/src/g.yxqyang.asia-1ecc6299db9ec823/snow-0.9.0/src/resolvers/default.rs:132:47
|
132 | let point = (&ED25519_BASEPOINT_TABLE * &self.privkey).to_montgomery();
| ------------------------ ^ ------------- &Scalar
| |
| &&EdwardsBasepointTable
|
help: `*` can be used on `&EdwardsBasepointTable` if you dereference the left-hand side
|
132 | let point = (*&ED25519_BASEPOINT_TABLE * &self.privkey).to_montgomery();
| + and clippy-beta as before. |
See mcginty/snow#146 for the build failure. |
I am afraid we cannot fix this on our end. We'd have to insert |
Do I understand correctly that this PR is on hold for now? |
Yes, we can't merge PRs at the moment due to the breakage in I have a proposal up to track |
Approvals have been dismissed because the PR was updated after the send-it
label was applied.
Description
The PR optimizes polling of the listeners in the TCP transport by using
futures::SelectAll
instead of storing them in a queue and polling manually.Resolves #2781.
Notes
I observed that the following branches are never triggered
rust-libp2p/transports/tcp/src/lib.rs
Lines 600 to 611 in d82c2a1
This is because the underlying
ListenStream
always returnsPoll::Ready(Some(Ok(..)))
and neitherPoll::Ready(Some(Err(...)))
(in-line documentation says such errors are not fatal), norPoll::Ready(None)
.I ended up following the approach found in
https://github.com/vnermolaev/rust-libp2p/blob/acf84113cff5843234ba6ea20c79b079e9d1195c/protocols/relay/src/priv_client/transport.rs#L232-L235
Please advise. Is this the desired behaviour? Otherwise, what approach should I follow? @elenaf9
Links to any relevant issues
#2781
Change checklist