Skip to content
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

src/connection: Process command or socket result immediately #138

Merged
merged 2 commits into from
Aug 5, 2022

Conversation

mxinden
Copy link
Member

@mxinden mxinden commented Aug 4, 2022

The frame future might be ready with an Error from the underlying
socket (i.e. here libp2p-websocket). Though given that the result of the
control_command Future is handled first, on_control_command is called
despite frame having returned an Error. on_control_command itself may try
to write to the underlying socket, which will panic given that the socket
returned an error earlier via the frame Future.

With this patch, once any of next_stream_command, next_control_command or
next_frame Future is ready, the result is processed right away, without
additionally polling the remaining pending Futures, thus surfacing errors as
early as possible.

See libp2p/rust-libp2p#2598 for details.

@elenaf9 @thomaseizinger could either of you do me a favor and take a look here? Happy to expand on the pull request description in case more context is needed.

> The `frame` future might be _ready_ with an `Error` from the underlying
socket (i.e. here `libp2p-websocket`). Though given that the result of the
`control_command` `Future` is handled first, `on_control_command` is called
despite `frame` having returned an `Error`. `on_control_command` itself may try
to write to the underlying socket, which will panic given that the socket
returned an error earlier via the `frame` `Future`.

With this patch, once any of `next_stream_command`, `next_control_command` or
`next_frame` `Future` is ready, the result is processed right away, without
additionally polling the remaining pending `Future`s, thus surfacing errors as
early as possible.

See libp2p/rust-libp2p#2598 for details.
@mxinden
Copy link
Member Author

mxinden commented Aug 4, 2022

I am not happy with next and next_stream. I don't think this patch makes things worse. I don't think it improves anything beyond the bug fix.

At this point I don't think we should refactor this Yamux implementation. Instead I suggest to focus on the two new transport implementations libp2p/rust-libp2p#2289 and libp2p/rust-libp2p#2622, as these leverage more powerful stream multiplexing protocols (i.e. QUIC's multiplexer and SCTP).

Copy link
Contributor

@thomaseizinger thomaseizinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense to me. Just one suggestion :)

Comment on lines +476 to 489
let combined_future = future::select(
future::select(next_stream_command, next_control_command),
next_frame,
);
match combined_future.await {
Either::Left((Either::Left((cmd, _)), _)) => self.on_stream_command(cmd).await?,
Either::Left((Either::Right((cmd, _)), _)) => self.on_control_command(cmd).await?,
Either::Right((frame, _)) => {
if let Some(stream) =
self.on_frame(frame.transpose().map_err(Into::into)).await?
{
return Ok(Some(stream));
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered using the futures::select! macro instead of nesting future::select calls?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have, though I favor futures::future::select over futures::select! due to the following reasons:

  • While more verbose, I find futures::future::select easier to reason about than futures::select!.

  • In general I prefer functions over macros.

  • I understand the inner workings of futures::future::select. The implementation is very simple.

  • I don't fully understand the guarantees of futures::select!, e.g.:

    If multiple futures are ready, one will be pseudo-randomly selected at runtime.

    https://docs.rs/futures/latest/futures/macro.select.html

    What happens to the result of the non-selected ready futures?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM!

jasl added a commit to Phala-Network/khala-parachain that referenced this pull request Aug 4, 2022
@mxinden
Copy link
Member Author

mxinden commented Aug 5, 2022

Been tested without further panics libp2p/rust-libp2p#2598 (comment)

@mxinden mxinden merged commit e6e10f8 into libp2p:master Aug 5, 2022
@mxinden
Copy link
Member Author

mxinden commented Aug 5, 2022

Tagged and published.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants