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

net: warn on creating a tokio socket from a blocking one #7166

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

Noah-Kennedy
Copy link
Contributor

@Noah-Kennedy Noah-Kennedy commented Feb 20, 2025

See #5595 and #7172.

This adds a warning when constructing a tokio socket object from an underlying std socket that is not set to nonblocking mode.

This warning is likely to incrementally turn into a hard error in the future.

Noah-Kennedy and others added 6 commits February 19, 2025 21:52
See #5595.

This adds a warning when constructing a tokio socket object from an underlying std socket that is not set to nonblocking mode.

This warning is likely to incrementally turn into a hard error in the future.
@Noah-Kennedy
Copy link
Contributor Author

Windows does not support this, so it only works on unix for now.

@Noah-Kennedy Noah-Kennedy changed the title net: warn on creating a tokio socket from a nonblocking one. net: warn on creating a tokio socket from a nonblocking one Feb 20, 2025
@Darksonn Darksonn requested a review from carllerche February 20, 2025 08:00
@Darksonn Darksonn added A-tokio Area: The main tokio crate M-net Module: tokio/net labels Feb 20, 2025
@Noah-Kennedy Noah-Kennedy changed the title net: warn on creating a tokio socket from a nonblocking one net: warn on creating a tokio socket from a blocking one Feb 20, 2025
let sock = socket2::SockRef::from(s);

if !sock.nonblocking()? {
eprintln!("Warning: registering a blocking socket, this may be a bug!");

Choose a reason for hiding this comment

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

Is it possible/likely that users are already using stderr for other things? Emitting stderr from within a library feels kinda gross; it might be easier to spot if we just panic here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I actually feel similarly, the request to do it this way specifically came from @carllerche.

I'll leave it up to him to voice why he wants this approach.

Copy link
Member

Choose a reason for hiding this comment

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

I spent some more time thinking about this question. I think that I am OK starting with a debug_assert. I would like there to be a way to disable the assertion (in case a user has a legitimate case) , and the panic message links to an issue for the user to find out how to disable the panic and report their legitimate use case.

To disable the panic, we can use a tokio_allow_from_blocking_fd cfg flag or something similar. The linked issue can include steps for setting it.

cc/ @Darksonn wdyt?

Copy link
Contributor

Choose a reason for hiding this comment

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

Using a debug assertion with a --cfg to disable it seems preferable to the eprintln!.

Copy link

@Ten0 Ten0 left a comment

Choose a reason for hiding this comment

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

Thanks for getting this to move forward!

Is from_std_set_nonblocking/from_std_assume_nonblocking still the long term plan?
image
Or will it instead be something where tokio sets it to nonblocking, like axum_server currently does?
Or will the panic be considered to be enough?

@Noah-Kennedy
Copy link
Contributor Author

Thanks for getting this to move forward!

Is from_std_set_nonblocking/from_std_assume_nonblocking still the long term plan? image Or will it instead be something where tokio sets it to nonblocking, like axum_server currently does? Or will the panic be considered to be enough?

I think there's a bit that needs figured out still about what we do going forward. The trouble with the prior suggestion is that we actually still have the issue of TryFrom to figure out - we can't exactly deprecate that, and we need to figure out whether what it's behavior will be.

I think our two long-term options here are going to be to either panic, or set the socket option silently, and there's good arguments to be made for both. There should also be something along the lines of from_std_unchecked or from_std_assume_nonblocking, of course.

Given that we either need to leave the footgun in TryFrom or make a behavioral change anyways, I don't think leaving from_std as is makes much sense.

@Noah-Kennedy Noah-Kennedy enabled auto-merge (squash) February 20, 2025 21:51
Copy link
Contributor

@Darksonn Darksonn left a comment

Choose a reason for hiding this comment

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

This also needs review from Carl before it can be merged.

Comment on lines +13 to +14
#[cfg(feature = "net")]
pub(crate) mod blocking_check;
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider re-exporting check_socket_for_blocking here to make the imports more nice.

let sock = socket2::SockRef::from(s);

if !sock.nonblocking()? {
eprintln!("Warning: registering a blocking socket, this may be a bug!");
Copy link
Contributor

Choose a reason for hiding this comment

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

The warning should say that it comes from Tokio. Perhaps include Location::caller via #[track_caller] to print the caller location.

@@ -209,6 +210,8 @@ impl TcpListener {
/// will block the thread, which will cause unexpected behavior.
/// Non-blocking mode can be set using [`set_nonblocking`].
///
/// Tokio's handling of blocking sockets may change in the future.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we improve this wording? I don't think it very clearly explains the situation. You could say something along the lines of "Passing a listener in blocking mode is always errornous, and the behavior in that case may change in the future. For example, it could panic."


#[cfg(unix)]
#[allow(unused_variables)]
pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
Copy link
Member

Choose a reason for hiding this comment

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

Why does this return io::Result? It doesn't seem like the fn needs a return argument.

@@ -5,6 +5,7 @@ cfg_not_wasi! {
use crate::net::{to_socket_addrs, ToSocketAddrs};
}

use crate::util::blocking_check::check_socket_for_blocking;
Copy link
Member

Choose a reason for hiding this comment

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

Very minor and not a needed change to merge, but IMO this check makes more sense in the net module.

let sock = socket2::SockRef::from(s);

if !sock.nonblocking()? {
eprintln!("Warning: registering a blocking socket, this may be a bug!");
Copy link
Member

Choose a reason for hiding this comment

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

I spent some more time thinking about this question. I think that I am OK starting with a debug_assert. I would like there to be a way to disable the assertion (in case a user has a legitimate case) , and the panic message links to an issue for the user to find out how to disable the panic and report their legitimate use case.

To disable the panic, we can use a tokio_allow_from_blocking_fd cfg flag or something similar. The linked issue can include steps for setting it.

cc/ @Darksonn wdyt?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tokio Area: The main tokio crate M-net Module: tokio/net
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants