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

Handle pid_shutdown_sockets when accepting connections #6394

Open
dlon opened this issue Mar 12, 2024 · 2 comments
Open

Handle pid_shutdown_sockets when accepting connections #6394

dlon opened this issue Mar 12, 2024 · 2 comments
Labels
A-tokio Area: The main tokio crate C-bug Category: This is a bug. M-net Module: tokio/net

Comments

@dlon
Copy link

dlon commented Mar 12, 2024

Version
1.36.0

Platform
Darwin Kernel Version 23.2.0
macOS 14.2.1

Description
TcpListener::accept() doesn't fail when the socket is forcibly closed, whereas std::net::TcpListener does.

macOS has a syscall pid_shutdown_sockets which can close sockets for arbitrary processes. I want to be able to handle this situation.

Run this code:

use tokio::net::TcpListener;
use std::{time::Duration, os::fd::AsRawFd, ffi::c_int};

#[tokio::main]
async fn main() {
    
    //let mut listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
    let mut listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
    let fd = listener.as_raw_fd();

    std::thread::spawn(move || {
        std::thread::sleep(Duration::from_secs(5));

        extern "C" {
            fn pid_shutdown_sockets(pid: c_int, level: c_int) -> c_int;
        }
        const SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL: i32 = 2;
        unsafe {
            pid_shutdown_sockets(std::process::id() as _, SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL);
        }
    });

    loop {
        // this fails, as expected
        //let result = listener.accept();

        // this keeps waiting forever:
        let result = listener.accept().await;

        if result.is_ok() {
            println!("accepted stream!");
        } else {
            eprintln!("error: {result:?}");
            break;
        }
    }
}

Expected: TcpListener::accept should return (fail) when pid_shutdown_sockets shuts down the socket. This is what occurs using std::net::TcpListener. Instead, accept never returns.

@dlon dlon added A-tokio Area: The main tokio crate C-bug Category: This is a bug. labels Mar 12, 2024
@Darksonn Darksonn added the M-net Module: tokio/net label Mar 12, 2024
@Darksonn
Copy link
Contributor

If it doesn't already work, then there's a good chance that this is not possible.

But I would be happy to hear from someone who knows more about kqueue.

@tglane
Copy link
Contributor

tglane commented Mar 23, 2024

I would agree that this is not possible.
Kqueue removes the file descriptor from its set when the file descriptor gets closed. As far as I know there is no event that you can register to Kqueue to notify you when a socket descriptor gets closed. Since a call to pid_shutdown_sockets will close all file descriptors forcibly it will just be removed from the Kqueue set without notifying the tokio::net::TcpListener.
See Kqueue man for reference.

The reason why a call to std::net::TcpListener::accept will return an error is that it calls libc::accept and blocks. When the FD gets closed this syscall returns with an error.

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 C-bug Category: This is a bug. M-net Module: tokio/net
Projects
None yet
Development

No branches or pull requests

3 participants