use std::net::SocketAddr;
use std::os::fd::RawFd;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use crossbeam_channel::Sender;
pub struct AcceptorConfig {
pub listen_fd: RawFd,
pub worker_channels: Vec<Sender<(RawFd, SocketAddr)>>,
pub worker_wake_handles: Vec<crate::wakeup::WakeFd>,
#[allow(dead_code)] pub shutdown_flag: Arc<AtomicBool>,
pub tcp_nodelay: bool,
pub conn_chunk_size: usize,
#[cfg(feature = "timestamps")]
pub timestamps: bool,
}
pub fn run_acceptor(config: AcceptorConfig) {
let num_workers = config.worker_channels.len();
if num_workers == 0 {
return;
}
let chunk_size = config.conn_chunk_size.max(1);
let mut conn_count = 0usize; let mut addr_storage: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
let mut alive = vec![true; num_workers];
let mut alive_count = num_workers;
loop {
let mut addr_len: libc::socklen_t =
std::mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
let fd = accept_nonblock(config.listen_fd, &mut addr_storage, &mut addr_len);
if fd < 0 {
let err = std::io::Error::last_os_error();
match err.raw_os_error() {
Some(libc::EINTR) => continue,
Some(libc::EMFILE) | Some(libc::ENFILE) => {
std::thread::sleep(std::time::Duration::from_millis(10));
continue;
}
Some(libc::ECONNABORTED) | Some(libc::ECONNRESET) | Some(libc::EPERM) => {
continue;
}
_ => {
return;
}
}
}
if config.tcp_nodelay && addr_storage.ss_family != libc::AF_UNIX as libc::sa_family_t {
let optval: libc::c_int = 1;
unsafe {
libc::setsockopt(
fd,
libc::IPPROTO_TCP,
libc::TCP_NODELAY,
&optval as *const _ as *const libc::c_void,
std::mem::size_of::<libc::c_int>() as libc::socklen_t,
);
}
}
#[cfg(all(target_os = "linux", feature = "timestamps"))]
if config.timestamps {
let flags: libc::c_int = (libc::SOF_TIMESTAMPING_SOFTWARE
| libc::SOF_TIMESTAMPING_RX_SOFTWARE)
as libc::c_int;
unsafe {
libc::setsockopt(
fd,
libc::SOL_SOCKET,
libc::SO_TIMESTAMPING,
&flags as *const _ as *const libc::c_void,
std::mem::size_of::<libc::c_int>() as libc::socklen_t,
);
}
}
let peer_addr = sockaddr_to_socket_addr(&addr_storage)
.unwrap_or_else(|| SocketAddr::from(([0, 0, 0, 0], 0)));
let primary = (conn_count / chunk_size) % num_workers;
let mut sent = false;
for i in 0..num_workers {
let worker_idx = (primary + i) % num_workers;
if !alive[worker_idx] {
continue;
}
match config.worker_channels[worker_idx].try_send((fd, peer_addr)) {
Ok(()) => {
config.worker_wake_handles[worker_idx].wake();
conn_count = conn_count.wrapping_add(1);
sent = true;
break;
}
Err(crossbeam_channel::TrySendError::Full(_)) => {
continue;
}
Err(crossbeam_channel::TrySendError::Disconnected(_)) => {
alive[worker_idx] = false;
alive_count -= 1;
if alive_count == 0 {
unsafe {
libc::close(fd);
}
return;
}
continue;
}
}
}
if !sent {
unsafe {
libc::close(fd);
}
continue;
}
}
}
fn accept_nonblock(
listen_fd: libc::c_int,
addr: &mut libc::sockaddr_storage,
addr_len: &mut libc::socklen_t,
) -> libc::c_int {
#[cfg(target_os = "linux")]
{
unsafe {
libc::accept4(
listen_fd,
addr as *mut _ as *mut libc::sockaddr,
addr_len,
libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC,
)
}
}
#[cfg(not(target_os = "linux"))]
{
let fd =
unsafe { libc::accept(listen_fd, addr as *mut _ as *mut libc::sockaddr, addr_len) };
if fd >= 0 {
unsafe {
let flags = libc::fcntl(fd, libc::F_GETFL);
libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK);
let fd_flags = libc::fcntl(fd, libc::F_GETFD);
libc::fcntl(fd, libc::F_SETFD, fd_flags | libc::FD_CLOEXEC);
}
}
fd
}
}
fn sockaddr_to_socket_addr(storage: &libc::sockaddr_storage) -> Option<SocketAddr> {
match storage.ss_family as libc::c_int {
libc::AF_INET => {
let sa = unsafe { &*(storage as *const _ as *const libc::sockaddr_in) };
let ip = std::net::Ipv4Addr::from(u32::from_be(sa.sin_addr.s_addr));
let port = u16::from_be(sa.sin_port);
Some(SocketAddr::from((ip, port)))
}
libc::AF_INET6 => {
let sa = unsafe { &*(storage as *const _ as *const libc::sockaddr_in6) };
let ip = std::net::Ipv6Addr::from(sa.sin6_addr.s6_addr);
let port = u16::from_be(sa.sin6_port);
Some(SocketAddr::from((ip, port)))
}
_ => None,
}
}