TcpStream::connect blocks the current fiber in case port is non responsive
На данный момент TcpStream::connect не работает с нашим async рантаймом, вместо этого он блокирует текущий файбер. Мы сделали так, потому что не смогли найти способ обернуть connect в фьючу, но решение всё таки есть.
Вот псевдокод:
impl TcpStream {
async fn connect(url: &str, port: u16) -> Result<Self> {
// TODO: if resolve_addr starts blocking the fiber for long we should implement an async
// resolve_addr by sending a task to a special separate fiber which only calls resolve_addr
let (v4_addrs, v6_addrs) = unsafe { resolve_addr(url, port, timeout.as_secs_f64())? };
let mut last_error = None;
for v4_addr in v4_addrs {
let res = Self::connect_single(LibcSocketAddr::V4(v4_addr)).await;
match res {
Ok(stream) => return Ok(stream);
Err(e) => last_error = Some(e);
}
}
for v6_addr in v6_addrs {
let res = Self::connect_single(LibcSocketAddr::V6(v6_addr)).await;
match res {
Ok(stream) => return Ok(stream);
Err(e) => last_error = Some(e);
}
}
// handle error
}
async fn connect_single(socket_addr: LibcSocketAddr) -> Result<Self> {
// prepare fd, addr, addr_len
let res = cvt(unsafe { libc::connect(fd.0, addr, addr_len as _) });
let Err(e) = res else {
// Connection established instantly (highly unlikely)
return Self::from_raw_fd(fd);
};
let f = poll_fn(move |context| {
if let Err(e) = check_socket_error(fd) {
// Connection failed
return Poll::Ready(Err(e));
}
let dummy: libc::sockaddr = unsafe { std::mem::zeroed() };
let dummy_size: u32 = std::mem::size_of_val(&dummy) as _;
let rc = unsafe { libc::getpeername(fd, &mut dummy, &mut dummy_size) };
if rc == 0 {
// Connection established
return Poll::Ready(Ok(Self::from_raw_fd(fd)));
}
// Connection state unknown yet, retry later
unsafe { ContextExt::set_coio_wait(cx, fd, ffi::CoIOFlags::WRITE) }
Poll::Pending
});
f.await
}
}
Это позволит использовать один общий таймаут например для операции, которая создаст соединение, отправит запрос, и будет ждать ответа. И если соединение не смогло установиться за отведённое время, то вся фьюча целиком вернёт Err(Timeout)
Edited by Georgy Moshkin