Repository: tokio-rs/tokio
Pull request #7166: net: debug_assert on creating a tokio socket from a blocking one
Author: login: Noah-Kennedy
Labels: name: A-tokio
description: Area: The main tokio crate
name: M-net
description: Module: tokio/net
Created: 2025-02-20T03:52:57Z | Merged: 2025-03-05T18:10:31Z

See #5595 and #7172.

This adds a debug assertion that checks that a supplied underlying std socket is set to nonblocking mode when constructing a tokio socket object from such an object.

This only works on unix.


Diff:
diff --git a/Cargo.toml b/Cargo.toml
index c215946f421..618b310e32c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,6 +23,7 @@ unexpected_cfgs = { level = "warn", check-cfg = [
   'cfg(fuzzing)',
   'cfg(loom)',
   'cfg(mio_unsupported_force_poll_poll)',
+  'cfg(tokio_allow_from_blocking_fd)',
   'cfg(tokio_internal_mt_counters)',
   'cfg(tokio_no_parking_lot)',
   'cfg(tokio_no_tuning_tests)',
diff --git a/tokio/src/net/tcp/listener.rs b/tokio/src/net/tcp/listener.rs
index 1545e654128..d29e6ee13a0 100644
--- a/tokio/src/net/tcp/listener.rs
+++ b/tokio/src/net/tcp/listener.rs
@@ -1,5 +1,6 @@
 use crate::io::{Interest, PollEvented};
 use crate::net::tcp::TcpStream;
+use crate::util::check_socket_for_blocking;
 
 cfg_not_wasi! {
     use crate::net::{to_socket_addrs, ToSocketAddrs};
@@ -209,6 +210,10 @@ impl TcpListener {
     /// will block the thread, which will cause unexpected behavior.
     /// Non-blocking mode can be set using [`set_nonblocking`].
     ///
+    /// Passing a listener in blocking mode is always erroneous,
+    /// and the behavior in that case may change in the future.
+    /// For example, it could panic.
+    ///
     /// [`set_nonblocking`]: std::net::TcpListener::set_nonblocking
     ///
     /// # Examples
@@ -236,6 +241,8 @@ impl TcpListener {
     /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
     #[track_caller]
     pub fn from_std(listener: net::TcpListener) -> io::Result<TcpListener> {
+        check_socket_for_blocking(&listener)?;
+
         let io = mio::net::TcpListener::from_std(listener);
         let io = PollEvented::new(io)?;
         Ok(TcpListener { io })
diff --git a/tokio/src/net/tcp/stream.rs b/tokio/src/net/tcp/stream.rs
index 0504bf2e1ea..7c753741c44 100644
--- a/tokio/src/net/tcp/stream.rs
+++ b/tokio/src/net/tcp/stream.rs
@@ -7,6 +7,7 @@ cfg_not_wasi! {
 use crate::io::{AsyncRead, AsyncWrite, Interest, PollEvented, ReadBuf, Ready};
 use crate::net::tcp::split::{split, ReadHalf, WriteHalf};
 use crate::net::tcp::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf};
+use crate::util::check_socket_for_blocking;
 
 use std::fmt;
 use std::io;
@@ -173,6 +174,10 @@ impl TcpStream {
     /// will block the thread, which will cause unexpected behavior.
     /// Non-blocking mode can be set using [`set_nonblocking`].
     ///
+    /// Passing a listener in blocking mode is always erroneous,
+    /// and the behavior in that case may change in the future.
+    /// For example, it could panic.
+    ///
     /// [`set_nonblocking`]: std::net::TcpStream::set_nonblocking
     ///
     /// # Examples
@@ -200,6 +205,8 @@ impl TcpStream {
     /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
     #[track_caller]
     pub fn from_std(stream: std::net::TcpStream) -> io::Result<TcpStream> {
+        check_socket_for_blocking(&stream)?;
+
         let io = mio::net::TcpStream::from_std(stream);
         let io = PollEvented::new(io)?;
         Ok(TcpStream { io })
diff --git a/tokio/src/net/udp.rs b/tokio/src/net/udp.rs
index 3e20dd03b2c..044096eb6e0 100644
--- a/tokio/src/net/udp.rs
+++ b/tokio/src/net/udp.rs
@@ -1,5 +1,6 @@
 use crate::io::{Interest, PollEvented, ReadBuf, Ready};
 use crate::net::{to_socket_addrs, ToSocketAddrs};
+use crate::util::check_socket_for_blocking;
 
 use std::fmt;
 use std::io;
@@ -192,6 +193,10 @@ impl UdpSocket {
     /// will block the thread, which will cause unexpected behavior.
     /// Non-blocking mode can be set using [`set_nonblocking`].
     ///
+    /// Passing a listener in blocking mode is always erroneous,
+    /// and the behavior in that case may change in the future.
+    /// For example, it could panic.
+    ///
     /// [`set_nonblocking`]: std::net::UdpSocket::set_nonblocking
     ///
     /// # Panics
@@ -220,6 +225,8 @@ impl UdpSocket {
     /// ```
     #[track_caller]
     pub fn from_std(socket: net::UdpSocket) -> io::Result<UdpSocket> {
+        check_socket_for_blocking(&socket)?;
+
         let io = mio::net::UdpSocket::from_std(socket);
         UdpSocket::new(io)
     }
diff --git a/tokio/src/net/unix/datagram/socket.rs b/tokio/src/net/unix/datagram/socket.rs
index e475778ef66..4f1fb669526 100644
--- a/tokio/src/net/unix/datagram/socket.rs
+++ b/tokio/src/net/unix/datagram/socket.rs
@@ -1,5 +1,6 @@
 use crate::io::{Interest, PollEvented, ReadBuf, Ready};
 use crate::net::unix::SocketAddr;
+use crate::util::check_socket_for_blocking;
 
 use std::fmt;
 use std::io;
@@ -449,6 +450,10 @@ impl UnixDatagram {
     /// will block the thread, which will cause unexpected behavior.
     /// Non-blocking mode can be set using [`set_nonblocking`].
     ///
+    /// Passing a listener in blocking mode is always erroneous,
+    /// and the behavior in that case may change in the future.
+    /// For example, it could panic.
+    ///
     /// [`set_nonblocking`]: std::os::unix::net::UnixDatagram::set_nonblocking
     ///
     /// # Panics
@@ -484,6 +489,8 @@ impl UnixDatagram {
     /// ```
     #[track_caller]
     pub fn from_std(datagram: net::UnixDatagram) -> io::Result<UnixDatagram> {
+        check_socket_for_blocking(&datagram)?;
+
         let socket = mio::net::UnixDatagram::from_std(datagram);
         let io = PollEvented::new(socket)?;
         Ok(UnixDatagram { io })
diff --git a/tokio/src/net/unix/listener.rs b/tokio/src/net/unix/listener.rs
index 9977020a283..996d3e1dced 100644
--- a/tokio/src/net/unix/listener.rs
+++ b/tokio/src/net/unix/listener.rs
@@ -1,5 +1,6 @@
 use crate::io::{Interest, PollEvented};
 use crate::net::unix::{SocketAddr, UnixStream};
+use crate::util::check_socket_for_blocking;
 
 use std::fmt;
 use std::io;
@@ -106,6 +107,10 @@ impl UnixListener {
     /// will block the thread, which will cause unexpected behavior.
     /// Non-blocking mode can be set using [`set_nonblocking`].
     ///
+    /// Passing a listener in blocking mode is always erroneous,
+    /// and the behavior in that case may change in the future.
+    /// For example, it could panic.
+    ///
     /// [`set_nonblocking`]: std::os::unix::net::UnixListener::set_nonblocking
     ///
     /// # Examples
@@ -133,6 +138,8 @@ impl UnixListener {
     /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
     #[track_caller]
     pub fn from_std(listener: net::UnixListener) -> io::Result<UnixListener> {
+        check_socket_for_blocking(&listener)?;
+
         let listener = mio::net::UnixListener::from_std(listener);
         let io = PollEvented::new(listener)?;
         Ok(UnixListener { io })
diff --git a/tokio/src/net/unix/stream.rs b/tokio/src/net/unix/stream.rs
index 16dc03f9a30..3f3220cd813 100644
--- a/tokio/src/net/unix/stream.rs
+++ b/tokio/src/net/unix/stream.rs
@@ -3,6 +3,7 @@ use crate::net::unix::split::{split, ReadHalf, WriteHalf};
 use crate::net::unix::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf};
 use crate::net::unix::ucred::{self, UCred};
 use crate::net::unix::SocketAddr;
+use crate::util::check_socket_for_blocking;
 
 use std::fmt;
 use std::future::poll_fn;
@@ -791,6 +792,10 @@ impl UnixStream {
     /// will block the thread, which will cause unexpected behavior.
     /// Non-blocking mode can be set using [`set_nonblocking`].
     ///
+    /// Passing a listener in blocking mode is always erroneous,
+    /// and the behavior in that case may change in the future.
+    /// For example, it could panic.
+    ///
     /// [`set_nonblocking`]: std::os::unix::net::UnixStream::set_nonblocking
     ///
     /// # Examples
@@ -818,6 +823,8 @@ impl UnixStream {
     /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
     #[track_caller]
     pub fn from_std(stream: net::UnixStream) -> io::Result<UnixStream> {
+        check_socket_for_blocking(&stream)?;
+
         let stream = mio::net::UnixStream::from_std(stream);
         let io = PollEvented::new(stream)?;
 
diff --git a/tokio/src/util/blocking_check.rs b/tokio/src/util/blocking_check.rs
new file mode 100644
index 00000000000..f7f35f6a1a8
--- /dev/null
+++ b/tokio/src/util/blocking_check.rs
@@ -0,0 +1,29 @@
+#[cfg(unix)]
+use std::os::fd::AsFd;
+
+#[cfg(unix)]
+#[allow(unused_variables)]
+#[track_caller]
+pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
+    #[cfg(not(tokio_allow_from_blocking_fd))]
+    {
+        let sock = socket2::SockRef::from(s);
+
+        debug_assert!(
+            sock.nonblocking()?,
+            "Registering a blocking socket with the tokio runtime is unsupported. \
+            If you wish to do anyways, please add `--cfg tokio_allow_from_blocking_fd` to your \
+            RUSTFLAGS. See github.com/tokio-rs/tokio/issues/7172 for details."
+        );
+    }
+
+    Ok(())
+}
+
+#[cfg(not(unix))]
+#[allow(unused_variables)]
+pub(crate) fn check_socket_for_blocking<S>(s: &S) -> crate::io::Result<()> {
+    // we cannot retrieve the nonblocking status on windows
+    // and i dont know how to support wasi yet
+    Ok(())
+}
diff --git a/tokio/src/util/mod.rs b/tokio/src/util/mod.rs
index a93bfe8304f..e585c665cd6 100644
--- a/tokio/src/util/mod.rs
+++ b/tokio/src/util/mod.rs
@@ -5,6 +5,12 @@ cfg_io_driver! {
 #[cfg(feature = "rt")]
 pub(crate) mod atomic_cell;
 
+#[cfg(feature = "net")]
+mod blocking_check;
+#[cfg(feature = "net")]
+#[allow(unused_imports)]
+pub(crate) use blocking_check::check_socket_for_blocking;
+
 pub(crate) mod metric_atomics;
 
 #[cfg(any(feature = "rt", feature = "signal", feature = "process"))]
diff --git a/tokio/tests/io_driver.rs b/tokio/tests/io_driver.rs
index 9f8915fb130..ddb81c64198 100644
--- a/tokio/tests/io_driver.rs
+++ b/tokio/tests/io_driver.rs
@@ -96,7 +96,10 @@ fn panics_when_io_disabled() {
     let rt = runtime::Builder::new_current_thread().build().unwrap();
 
     rt.block_on(async {
-        let _ =
-            tokio::net::TcpListener::from_std(std::net::TcpListener::bind("127.0.0.1:0").unwrap());
+        let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
+
+        listener.set_nonblocking(true).unwrap();
+
+        let _ = tokio::net::TcpListener::from_std(listener);
     });
 }
diff --git a/tokio/tests/io_driver_drop.rs b/tokio/tests/io_driver_drop.rs
index 0bbd379a95b..e01679b6eba 100644
--- a/tokio/tests/io_driver_drop.rs
+++ b/tokio/tests/io_driver_drop.rs
@@ -13,6 +13,9 @@ fn tcp_doesnt_block() {
     let listener = {
         let _enter = rt.enter();
         let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
+
+        listener.set_nonblocking(true).unwrap();
+
         TcpListener::from_std(listener).unwrap()
     };
 
@@ -33,6 +36,9 @@ fn drop_wakes() {
     let listener = {
         let _enter = rt.enter();
         let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
+
+        listener.set_nonblocking(true).unwrap();
+
         TcpListener::from_std(listener).unwrap()
     };
 
diff --git a/tokio/tests/no_rt.rs b/tokio/tests/no_rt.rs
index 0730090857c..0b6ee037b05 100644
--- a/tokio/tests/no_rt.rs
+++ b/tokio/tests/no_rt.rs
@@ -39,5 +39,9 @@ async fn timeout_value() {
 )]
 #[cfg_attr(miri, ignore)] // No `socket` in miri.
 fn io_panics_when_no_tokio_context() {
-    let _ = tokio::net::TcpListener::from_std(std::net::TcpListener::bind("127.0.0.1:0").unwrap());
+    let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
+
+    listener.set_nonblocking(true).unwrap();
+
+    let _ = tokio::net::TcpListener::from_std(listener);
 }
diff --git a/tokio/tests/tcp_peek.rs b/tokio/tests/tcp_peek.rs
index e633badb699..deb0028227e 100644
--- a/tokio/tests/tcp_peek.rs
+++ b/tokio/tests/tcp_peek.rs
@@ -13,11 +13,18 @@ use std::{io::Write, net};
 #[tokio::test]
 async fn peek() {
     let listener = net::TcpListener::bind("127.0.0.1:0").unwrap();
+
     let addr = listener.local_addr().unwrap();
     let t = thread::spawn(move || assert_ok!(listener.accept()).0);
 
     let left = net::TcpStream::connect(addr).unwrap();
+
+    left.set_nonblocking(true).unwrap();
+
     let mut right = t.join().unwrap();
+
+    right.set_nonblocking(true).unwrap();
+
     let _ = right.write(&[1, 2, 3, 4]).unwrap();
 
     let mut left: TcpStream = left.try_into().unwrap();


Reviews:
user: login: rupert648
state: APPROVED
author_association: NONE
submitted_at: 2025-02-20T10:49:15Z
user: login: jeff-hiner
state: COMMENTED
author_association: NONE
submitted_at: 2025-02-20T15:57:46Z
user: login: Noah-Kennedy
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-20T17:33:51Z
user: login: Ten0
body: Thanks for getting this to move forward!

Is `from_std_set_nonblocking`/`from_std_assume_nonblocking` [still](https://github.com/tokio-rs/tokio/pull/5597#issuecomment-1671414799) the long term plan?
![image](https://github.com/tokio-rs/tokio/assets/9094255/1e69197f-dfee-447b-b7b3-2df5b4c2c32d) 
Or will it instead be something where tokio sets it to nonblocking, [like axum_server currently does](https://github.com/programatik29/axum-server/blob/master/src/server.rs#L250)?
Or will the panic be considered to be enough?
state: APPROVED
author_association: NONE
submitted_at: 2025-02-20T20:23:40Z
user: login: Darksonn
body: This also needs review from Carl before it can be merged.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-21T10:16:15Z
user: login: carllerche
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-02-24T19:17:09Z
user: login: Darksonn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-24T19:19:02Z
user: login: Noah-Kennedy
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-01T15:52:31Z
user: login: Darksonn
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-03T19:02:39Z
user: login: carllerche
body: Small proposal for the panic message.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-03-04T16:34:00Z
user: login: carllerche
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-04T16:34:50Z
user: login: Darksonn
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-04T16:35:44Z
user: login: carllerche
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-04T17:20:31Z
user: login: carllerche
body: LGTM thanks
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-05T17:51:27Z

Review comments:
diff_hunk: @@ -0,0 +1,25 @@
+#[cfg(unix)]
+use std::os::fd::AsFd;
+
+#[cfg(unix)]
+#[allow(unused_variables)]
+pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
+    #[cfg(debug_assertions)]
+    {
+        let sock = socket2::SockRef::from(s);
+
+        if !sock.nonblocking()? {
+            eprintln!("Warning: registering a blocking socket, this may be a bug!");
path: tokio/src/util/blocking_check.rs
user: login: jeff-hiner
body: 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.
created_at: 2025-02-20T15:57:46Z
author_association: NONE
diff_hunk: @@ -0,0 +1,25 @@
+#[cfg(unix)]
+use std::os::fd::AsFd;
+
+#[cfg(unix)]
+#[allow(unused_variables)]
+pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
+    #[cfg(debug_assertions)]
+    {
+        let sock = socket2::SockRef::from(s);
+
+        if !sock.nonblocking()? {
+            eprintln!("Warning: registering a blocking socket, this may be a bug!");
path: tokio/src/util/blocking_check.rs
user: login: Noah-Kennedy
body: 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.
created_at: 2025-02-20T17:33:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -10,6 +10,9 @@ pub(crate) mod metric_atomics;
 #[cfg(any(feature = "rt", feature = "signal", feature = "process"))]
 pub(crate) mod once_cell;
 
+#[cfg(feature = "net")]
+pub(crate) mod blocking_check;
path: tokio/src/util/mod.rs
user: login: Darksonn
body: Consider re-exporting `check_socket_for_blocking` here to make the imports more nice.
created_at: 2025-02-21T10:11:18Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,25 @@
+#[cfg(unix)]
+use std::os::fd::AsFd;
+
+#[cfg(unix)]
+#[allow(unused_variables)]
+pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
+    #[cfg(debug_assertions)]
+    {
+        let sock = socket2::SockRef::from(s);
+
+        if !sock.nonblocking()? {
+            eprintln!("Warning: registering a blocking socket, this may be a bug!");
path: tokio/src/util/blocking_check.rs
user: login: Darksonn
body: The warning should say that it comes from Tokio. Perhaps include `Location::caller` via `#[track_caller]` to print the caller location.
created_at: 2025-02-21T10:12:12Z
author_association: MEMBER
diff_hunk: @@ -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.
path: tokio/src/net/tcp/listener.rs
user: login: Darksonn
body: 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."
created_at: 2025-02-21T10:15:52Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,25 @@
+#[cfg(unix)]
+use std::os::fd::AsFd;
+
+#[cfg(unix)]
+#[allow(unused_variables)]
+pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
path: tokio/src/util/blocking_check.rs
user: login: carllerche
body: Why does this return `io::Result`? It doesn't seem like the fn needs a return argument.
created_at: 2025-02-24T19:12:03Z
author_association: MEMBER
diff_hunk: @@ -5,6 +5,7 @@ cfg_not_wasi! {
     use crate::net::{to_socket_addrs, ToSocketAddrs};
 }
 
+use crate::util::blocking_check::check_socket_for_blocking;
path: tokio/src/net/tcp/listener.rs
user: login: carllerche
body: Very minor and not a needed change to merge, but IMO this check makes more sense in the `net` module.
created_at: 2025-02-24T19:12:43Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,25 @@
+#[cfg(unix)]
+use std::os::fd::AsFd;
+
+#[cfg(unix)]
+#[allow(unused_variables)]
+pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
+    #[cfg(debug_assertions)]
+    {
+        let sock = socket2::SockRef::from(s);
+
+        if !sock.nonblocking()? {
+            eprintln!("Warning: registering a blocking socket, this may be a bug!");
path: tokio/src/util/blocking_check.rs
user: login: carllerche
body: 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?
created_at: 2025-02-24T19:17:02Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,25 @@
+#[cfg(unix)]
+use std::os::fd::AsFd;
+
+#[cfg(unix)]
+#[allow(unused_variables)]
+pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
+    #[cfg(debug_assertions)]
+    {
+        let sock = socket2::SockRef::from(s);
+
+        if !sock.nonblocking()? {
+            eprintln!("Warning: registering a blocking socket, this may be a bug!");
path: tokio/src/util/blocking_check.rs
user: login: Darksonn
body: Using a debug assertion with a `--cfg` to disable it seems preferable to the `eprintln!`.
created_at: 2025-02-24T19:19:02Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,25 @@
+#[cfg(unix)]
+use std::os::fd::AsFd;
+
+#[cfg(unix)]
+#[allow(unused_variables)]
+pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
path: tokio/src/util/blocking_check.rs
user: login: Noah-Kennedy
body: If the nonblocking check fails, propating the error seems the most straightforward response.
created_at: 2025-03-01T15:52:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,29 @@
+#[cfg(unix)]
+use std::os::fd::AsFd;
+
+#[cfg(unix)]
+#[allow(unused_variables)]
+#[track_caller]
+pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
+    #[cfg(not(tokio_allow_from_blocking_fd))]
+    {
+        let sock = socket2::SockRef::from(s);
+
+        debug_assert!(
+            sock.nonblocking()?,
+            "Registering a blocking socket with the tokio runtime is unsupported. \
+            If you wish to do anyways, please add `--cfg tokio_allow_from_blocking_fd` to your \
+            RUSTFLAGS. See 7172 for more details."
path: tokio/src/util/blocking_check.rs
user: login: carllerche
body: Edit: updated based on thread.

```suggestion
            RUSTFLAGS. See github.com/tokio-rs/tokio/issues/7172 for details."
```

How about including the link for those who don't realize 7172 references an issue?
created_at: 2025-03-04T16:32:42Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,29 @@
+#[cfg(unix)]
+use std::os::fd::AsFd;
+
+#[cfg(unix)]
+#[allow(unused_variables)]
+#[track_caller]
+pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
+    #[cfg(not(tokio_allow_from_blocking_fd))]
+    {
+        let sock = socket2::SockRef::from(s);
+
+        debug_assert!(
+            sock.nonblocking()?,
+            "Registering a blocking socket with the tokio runtime is unsupported. \
+            If you wish to do anyways, please add `--cfg tokio_allow_from_blocking_fd` to your \
+            RUSTFLAGS. See 7172 for more details."
path: tokio/src/util/blocking_check.rs
user: login: carllerche
body: Many modern terminals will make URLs clickable as well.
created_at: 2025-03-04T16:34:50Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,29 @@
+#[cfg(unix)]
+use std::os::fd::AsFd;
+
+#[cfg(unix)]
+#[allow(unused_variables)]
+#[track_caller]
+pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
+    #[cfg(not(tokio_allow_from_blocking_fd))]
+    {
+        let sock = socket2::SockRef::from(s);
+
+        debug_assert!(
+            sock.nonblocking()?,
+            "Registering a blocking socket with the tokio runtime is unsupported. \
+            If you wish to do anyways, please add `--cfg tokio_allow_from_blocking_fd` to your \
+            RUSTFLAGS. See 7172 for more details."
path: tokio/src/util/blocking_check.rs
user: login: Darksonn
body: How about just
```
See https://github.com/tokio-rs/tokio/issues/7172 for details.
```
created_at: 2025-03-04T16:35:44Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,29 @@
+#[cfg(unix)]
+use std::os::fd::AsFd;
+
+#[cfg(unix)]
+#[allow(unused_variables)]
+#[track_caller]
+pub(crate) fn check_socket_for_blocking<S: AsFd>(s: &S) -> crate::io::Result<()> {
+    #[cfg(not(tokio_allow_from_blocking_fd))]
+    {
+        let sock = socket2::SockRef::from(s);
+
+        debug_assert!(
+            sock.nonblocking()?,
+            "Registering a blocking socket with the tokio runtime is unsupported. \
+            If you wish to do anyways, please add `--cfg tokio_allow_from_blocking_fd` to your \
+            RUSTFLAGS. See 7172 for more details."
path: tokio/src/util/blocking_check.rs
user: login: carllerche
body: I don't think the `https://` is needed. It should end up getting parsed without it and default to https.
created_at: 2025-03-04T17:20:31Z
author_association: MEMBER

PR comments:
user: login: Noah-Kennedy
created_at: 2025-02-20T04:51:50Z
body: Windows does not support this, so it only works on unix for now.
author_association: CONTRIBUTOR
user: login: Noah-Kennedy
created_at: 2025-02-20T21:50:24Z
body: > Thanks for getting this to move forward!
> 
> Is `from_std_set_nonblocking`/`from_std_assume_nonblocking` [still](https://github.com/tokio-rs/tokio/pull/5597#issuecomment-1671414799) the long term plan? ![image](https://private-user-images.githubusercontent.com/9094255/258658201-1e69197f-dfee-447b-b7b3-2df5b4c2c32d.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDAwODgxOTksIm5iZiI6MTc0MDA4Nzg5OSwicGF0aCI6Ii85MDk0MjU1LzI1ODY1ODIwMS0xZTY5MTk3Zi1kZmVlLTQ0N2ItYjdiMy0yZGY1YjRjMmMzMmQucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDIyMCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAyMjBUMjE0NDU5WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9MDIzZjllYzhjN2M3ODQ2NWQyNjE2Njg5YzA2NDA4ODU2NDA0Mzk3ZTBlMDE3YTg5Zjc2MTVlN2Y0Y2ViNWFmOSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.LvbHuoE2W8j57npMKIECLyBDKsWyX4mSrjEmkCZYIVA) Or will it instead be something where tokio sets it to nonblocking, [like axum_server currently does](https://github.com/programatik29/axum-server/blob/master/src/server.rs#L250)? 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.
author_association: CONTRIBUTOR
user: login: Noah-Kennedy
created_at: 2025-03-01T16:38:39Z
body: @carllerche @Darksonn I have updated the PR and addressed the feedback. This is ready for re-review.
author_association: CONTRIBUTOR
user: login: Noah-Kennedy
created_at: 2025-03-03T19:17:55Z
body: from offline discussion with @Darksonn: need to wrap the lines on the docs to not be super long

this is good to merge after that is done
author_association: CONTRIBUTOR