Skip to content

Commit

Permalink
Make TCP connect() handle EINTR correctly
Browse files Browse the repository at this point in the history
According to the POSIX standard, if connect() is interrupted by a
signal that is caught while blocked waiting to establish a connection,
connect() shall fail and set errno to EINTR, but the connection
request shall not be aborted, and the connection shall be established
asynchronously.

If asynchronous connection was successfully established after EINTR
and before the next connection attempt, OS returns EISCONN that was
handled as an error before. This behavior is fixed now and we handle
it as success.

The problem affects MacOS users: Linux doesn't return EISCONN in this
case, Windows connect() can not be interrupted without an old-fashoin
WSACancelBlockingCall function that is not used in the library.
So current solution gives connect() as OS specific implementation.
  • Loading branch information
darthunix committed Oct 13, 2023
1 parent 130ff8c commit dfadd17
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 12 deletions.
6 changes: 6 additions & 0 deletions library/std/src/sys/hermit/net.rs
Expand Up @@ -56,6 +56,12 @@ impl Socket {
unimplemented!()
}

pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
let (addr, len) = addr.into_inner();
cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?;
Ok(())
}

pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
self.set_nonblocking(true)?;
let r = unsafe {
Expand Down
11 changes: 7 additions & 4 deletions library/std/src/sys/solid/net.rs
Expand Up @@ -233,12 +233,15 @@ impl Socket {
}
}

pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
let (addr, len) = addr.into_inner();
cvt(unsafe { netc::connect(self.0.raw(), addr.as_ptr(), len) })?;
Ok(())
}

pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
self.set_nonblocking(true)?;
let r = unsafe {
let (addr, len) = addr.into_inner();
cvt(netc::connect(self.0.raw(), addr.as_ptr(), len))
};
let r = self.connect(addr);
self.set_nonblocking(false)?;

match r {
Expand Down
17 changes: 17 additions & 0 deletions library/std/src/sys/unix/net.rs
Expand Up @@ -6,6 +6,7 @@ use crate::net::{Shutdown, SocketAddr};
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::str;
use crate::sys::fd::FileDesc;
use crate::sys::unix::IsMinusOne;
use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr};
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::{Duration, Instant};
Expand Down Expand Up @@ -140,6 +141,22 @@ impl Socket {
unimplemented!()
}

pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
let (addr, len) = addr.into_inner();
loop {
let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) };
if result.is_minus_one() {
let err = crate::sys::os::errno();
match err {
libc::EINTR => continue,
libc::EISCONN => return Ok(()),
_ => return Err(io::Error::from_raw_os_error(err)),
}
}
return Ok(());
}
}

pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
self.set_nonblocking(true)?;
let r = unsafe {
Expand Down
12 changes: 7 additions & 5 deletions library/std/src/sys/windows/net.rs
Expand Up @@ -140,13 +140,15 @@ impl Socket {
}
}

pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
let (addr, len) = addr.into_inner();
let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) };
cvt(result).map(drop)
}

pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
self.set_nonblocking(true)?;
let result = {
let (addr, len) = addr.into_inner();
let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) };
cvt(result).map(drop)
};
let result = self.connect(addr);
self.set_nonblocking(false)?;

match result {
Expand Down
4 changes: 1 addition & 3 deletions library/std/src/sys_common/net.rs
Expand Up @@ -226,9 +226,7 @@ impl TcpStream {
init();

let sock = Socket::new(addr, c::SOCK_STREAM)?;

let (addr, len) = addr.into_inner();
cvt_r(|| unsafe { c::connect(sock.as_raw(), addr.as_ptr(), len) })?;
sock.connect(addr)?;
Ok(TcpStream { inner: sock })
}

Expand Down

0 comments on commit dfadd17

Please sign in to comment.