#![cfg_attr(not(feature = "udp"), allow(dead_code, unused_imports, unused_mut))]
use crate::handler::{Acceptor, CloseReason, ConnInfo, Connection, Endpoint, Message, UdpInfo, UdpService};
use crate::offload::{OffloadPool, Rejected};
use crate::pool::BufPool;
use crate::runtime::{Config, Shared};
use crate::timer::{TimerId, TimerWheel};
use simoom_sys::driver::{Driver, Event, Id, OutBuf, Waker};
use simoom_sys::net::Socket;
use simoom_sys::udp::Transmit;
use std::io;
use std::net::SocketAddr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
const LISTENER_BIT: u64 = 1 << 63;
const INITIAL_RECV_BUFFERS: usize = 8;
const COMPACT_INPUT: usize = 512;
const BALANCE_AFTER_GROWTH_NS: u64 = 1_000_000_000;
#[cfg_attr(not(feature = "idle-trim"), allow(dead_code))]
const IDLE_TRIM_NS: u64 = 1_000_000_000;
#[cfg_attr(not(feature = "idle-trim"), allow(dead_code))]
const IDLE_POOL_KEEP: usize = 4;
const FLUSH_EARLY_BATCH: usize = 16;
#[cfg_attr(not(feature = "idle-trim"), allow(dead_code))]
const BURST_EVENTS: u64 = 4096;
const UDP_BIT: u64 = 1 << 62;
pub(crate) type Task = Box<dyn FnOnce(&mut LoopCtx<'_>) + Send>;
pub(crate) enum Item {
Task(Task),
Conn(u64, Message),
Udp(usize, Message),
}
pub(crate) struct Queue {
items: simoom_sys::mpsc::Mpsc<Item>,
pub(crate) waker: Waker,
closed: AtomicBool,
}
impl Queue {
pub(crate) fn new(waker: Waker) -> Queue {
Queue { items: simoom_sys::mpsc::Mpsc::new(), waker, closed: AtomicBool::new(false) }
}
fn push(&self, t: Item) -> bool {
if self.closed.load(Ordering::Acquire) {
return false;
}
self.items.push(t);
self.waker.post();
!self.closed.load(Ordering::Acquire)
}
fn take(&self, into: &mut Vec<Item>) {
self.items.take(into);
}
fn close(&self) {
self.closed.store(true, Ordering::Release);
let mut v = Vec::new();
self.items.take(&mut v);
}
}
#[derive(Clone)]
pub struct LoopHandle {
pub(crate) index: usize,
pub(crate) queue: Arc<Queue>,
}
impl std::fmt::Debug for LoopHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LoopHandle").field("index", &self.index).finish()
}
}
impl LoopHandle {
pub fn index(&self) -> usize {
self.index
}
pub fn run(&self, f: impl FnOnce(&mut LoopCtx<'_>) + Send + 'static) -> bool {
self.queue.push(Item::Task(Box::new(f)))
}
}
#[derive(Clone)]
pub struct ConnHandle {
queue: Arc<Queue>,
key: u64,
loop_index: usize,
}
impl std::fmt::Debug for ConnHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ConnHandle").field("loop", &self.loop_index).field("key", &self.key).finish()
}
}
impl ConnHandle {
pub fn send(&self, msg: Message) -> bool {
let key = self.key;
self.queue.push(Item::Conn(key, msg))
}
pub fn loop_index(&self) -> usize {
self.loop_index
}
}
#[derive(Clone)]
pub struct EndpointHandle {
queue: Arc<Queue>,
idx: usize,
}
impl std::fmt::Debug for EndpointHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EndpointHandle").field("idx", &self.idx).finish()
}
}
impl EndpointHandle {
pub fn send(&self, msg: Message) -> bool {
let idx = self.idx;
self.queue.push(Item::Udp(idx, msg))
}
}
struct Slab<T> {
slots: Vec<(u32, Option<T>)>,
free: Vec<u32>,
len: usize,
}
impl<T> Slab<T> {
fn new() -> Self {
Slab { slots: Vec::new(), free: Vec::new(), len: 0 }
}
fn next_key(&self) -> u64 {
match self.free.last() {
Some(&i) => i as u64 | (self.slots[i as usize].0 as u64) << 32,
None => self.slots.len() as u64,
}
}
fn insert(&mut self, v: T) -> u64 {
self.len += 1;
match self.free.pop() {
Some(i) => {
self.slots[i as usize].1 = Some(v);
i as u64 | (self.slots[i as usize].0 as u64) << 32
}
None => {
self.slots.push((0, Some(v)));
self.slots.len() as u64 - 1
}
}
}
fn get_mut(&mut self, key: u64) -> Option<&mut T> {
match self.slots.get_mut((key & 0xFFFF_FFFF) as usize) {
Some((g, Some(v))) if *g == (key >> 32) as u32 => Some(v),
_ => None,
}
}
fn remove(&mut self, key: u64) -> Option<T> {
let s = self.slots.get_mut((key & 0xFFFF_FFFF) as usize)?;
if s.0 != (key >> 32) as u32 || s.1.is_none() {
return None;
}
s.0 = s.0.wrapping_add(1) & 0x3FFF_FFFF; self.free.push((key & 0xFFFF_FFFF) as u32);
self.len -= 1;
s.1.take()
}
fn keys(&self) -> Vec<u64> {
self.slots.iter().enumerate().filter(|(_, s)| s.1.is_some()).map(|(i, s)| i as u64 | (s.0 as u64) << 32).collect()
}
}
#[derive(Clone, Copy)]
pub(crate) enum TimerTarget {
Conn(u64, u64),
Udp(usize, u64),
Linger(u64),
}
struct Conn {
id: Id,
handler: Option<Box<dyn Connection>>,
input: Option<Vec<u8>>,
queued: usize,
reason: Option<CloseReason>,
user_paused: bool,
hw_paused: bool,
want_drain: bool,
closing: bool,
closed: bool,
timers: ConnTimers,
lingering: bool,
linger_left: u32,
}
#[cold]
#[inline(never)]
fn no_connection() -> ! {
panic!("connection exists during its callback")
}
#[derive(Default)]
struct ConnTimers {
one: Option<TimerId>,
more: Option<Box<Vec<TimerId>>>,
}
impl ConnTimers {
fn push(&mut self, id: TimerId, wheel: &TimerWheel<TimerTarget>) {
if self.one.is_none_or(|t| !wheel.is_pending(t)) {
self.one = Some(id);
return;
}
let v = self.more.get_or_insert_with(Default::default);
if v.len() >= 4 {
v.retain(|t| wheel.is_pending(*t));
}
v.push(id);
}
fn remove(&mut self, id: TimerId) {
if self.one == Some(id) {
self.one = None;
} else if let Some(v) = self.more.as_mut() {
if let Some(i) = v.iter().position(|t| *t == id) {
v.swap_remove(i);
}
}
}
fn cancel_all(&mut self, wheel: &mut TimerWheel<TimerTarget>) {
if let Some(t) = self.one.take() {
wheel.cancel(t);
}
for t in self.more.take().into_iter().flat_map(|v| *v) {
wheel.cancel(t);
}
}
}
pub(crate) struct ListenerSlot {
pub(crate) acceptor: Arc<dyn Acceptor>,
pub(crate) socket: Option<Socket>,
handoff: bool,
id: Option<Id>,
pub(crate) bind: Option<(SocketAddr, simoom_sys::net::ListenOptions)>,
}
impl ListenerSlot {
pub(crate) fn new(acceptor: Arc<dyn Acceptor>, socket: Option<Socket>, handoff: bool) -> ListenerSlot {
ListenerSlot { acceptor, socket, handoff, id: None, bind: None }
}
}
pub(crate) struct UdpSlot {
pub(crate) service: Arc<dyn UdpService>,
pub(crate) binding: usize,
pub(crate) socket: Option<Socket>,
gro: bool,
id: Option<Id>,
local: Option<SocketAddr>,
pub(crate) bind: Option<(SocketAddr, simoom_sys::net::UdpOptions)>,
}
#[cfg_attr(not(feature = "udp"), allow(dead_code))]
pub(crate) const GRO_BUFFER: usize = 64 * 1024;
impl UdpSlot {
#[cfg_attr(not(feature = "udp"), allow(dead_code))]
pub(crate) fn new(service: Arc<dyn UdpService>, binding: usize, socket: Option<Socket>, gro: bool) -> UdpSlot {
UdpSlot { service, binding, socket, gro, id: None, local: None, bind: None }
}
}
pub(crate) struct Core {
pub(crate) index: usize,
driver: Driver,
pool: BufPool,
timers: TimerWheel<TimerTarget>,
conns: Slab<Conn>,
now_ns: u64,
tick_ns: u64,
queue: Arc<Queue>,
peers: Vec<LoopHandle>,
offload: OffloadPool,
cfg: Arc<Config>,
shared: Arc<Shared>,
listeners: Vec<ListenerSlot>,
udps: Vec<UdpSlot>,
endpoints: Vec<Option<Box<dyn Endpoint>>>,
redeliver: Vec<u64>,
drains: Vec<u64>,
rr: usize,
want_grow: bool,
grow_next: bool,
grown: bool,
parked: Vec<(Socket, Option<SocketAddr>, usize)>,
handoff_until: u64,
accepted: u64,
rejected: u64,
closed: u64,
#[cfg_attr(not(feature = "idle-trim"), allow(dead_code))]
last_io: u64,
#[cfg_attr(not(feature = "idle-trim"), allow(dead_code))]
io_since_trim: u64,
batch_left: usize,
}
impl Core {
fn conn(&mut self, key: u64) -> &mut Conn {
match self.conns.get_mut(key) {
Some(c) => c,
None => no_connection(),
}
}
fn deadline_tick(&self, after: Duration) -> u64 {
let at = self.now_ns.saturating_add(after.as_nanos().min(u64::MAX as u128) as u64);
at.div_ceil(self.tick_ns)
}
fn with_conn(&mut self, key: u64, f: impl FnOnce(&mut dyn Connection, &mut Ctx<'_>)) {
let mut f = Some(f);
self.with_conn_dyn(key, &mut |h, cx| {
if let Some(f) = f.take() {
f(h, cx)
}
});
}
#[inline(never)]
fn with_conn_dyn(&mut self, key: u64, f: &mut dyn FnMut(&mut dyn Connection, &mut Ctx<'_>)) {
let Some(c) = self.conns.get_mut(key) else { return };
if c.closed {
return;
}
let Some(mut h) = c.handler.take() else { return };
{
let mut cx = Ctx { core: self, key };
f(&mut *h, &mut cx);
}
if let Some(c) = self.conns.get_mut(key) {
c.handler = Some(h);
}
}
fn with_udp(&mut self, idx: usize, f: impl FnOnce(&mut dyn Endpoint, &mut UdpCtx<'_>)) {
if !cfg!(feature = "udp") {
return;
}
let Some(slot) = self.endpoints.get_mut(idx) else { return };
let Some(mut ep) = slot.take() else { return };
{
let mut cx = UdpCtx { core: self, idx };
f(&mut *ep, &mut cx);
}
self.endpoints[idx] = Some(ep);
}
#[inline(never)]
pub(crate) fn deliver(&mut self, key: u64, msg: Message) {
self.with_conn(key, |h, cx| h.on_message(cx, msg));
self.flush_conn(key);
}
pub(crate) fn deliver_udp(&mut self, idx: usize, msg: Message) {
self.with_udp(idx, |h, cx| h.on_message(cx, msg));
}
fn ensure_endpoint(&mut self, idx: usize) {
let Some(slot) = self.udps.get(idx) else { return };
if slot.id.is_none() || self.endpoints[idx].is_some() {
return;
}
let info = UdpInfo { binding: slot.binding, loop_index: self.index, local_addr: slot.local.unwrap_or(SocketAddr::from(([0, 0, 0, 0], 0))) };
self.endpoints[idx] = Some(slot.service.endpoint(&info));
}
fn close_now(&mut self, key: u64, reason: CloseReason) {
let Some(c) = self.conns.get_mut(key) else { return };
if c.closed {
return;
}
c.closed = true;
if c.reason.is_none() {
c.reason = Some(reason);
}
let id = c.id;
self.driver.close(id);
}
fn linger(&mut self, key: u64) {
let linger = self.cfg.linger;
let id = match self.conns.get_mut(key) {
Some(c) if !c.closed && !c.lingering => c.id,
_ => return,
};
let shut = !linger.is_zero() && self.driver.socket(id).is_some_and(|s| s.shutdown(std::net::Shutdown::Write).is_ok());
if !shut {
self.close_now(key, CloseReason::Closed);
return;
}
let Some(c) = self.conns.get_mut(key) else { return };
c.lingering = true;
c.linger_left = self.cfg.linger_bytes.min(u32::MAX as usize) as u32;
if let Some(i) = c.input.take() {
self.pool.put(i);
}
let at = self.deadline_tick(linger);
let t = self.timers.insert(at, TimerTarget::Linger(key));
if let Some(c) = self.conns.get_mut(key) {
c.timers.push(t, &self.timers);
}
self.driver.recv(id, true);
}
pub(crate) fn adopt(&mut self, socket: Socket, peer: Option<SocketAddr>, listener: usize) {
let total = self.shared.active.fetch_add(1, Ordering::AcqRel) + 1;
let here = self.conns.len + 1;
if total > self.cfg.max_connections || here > self.cfg.max_connections_per_loop {
self.shared.active.fetch_sub(1, Ordering::AcqRel);
self.rejected += 1;
drop(socket);
return;
}
let info = ConnInfo { listener, peer, loop_index: self.index };
let Some(handler) = self.listeners.get(listener).and_then(|l| l.acceptor.accept(&info)) else {
self.shared.active.fetch_sub(1, Ordering::AcqRel);
self.rejected += 1;
return;
};
if self.register(socket, handler).is_err() {
self.shared.active.fetch_sub(1, Ordering::AcqRel);
self.rejected += 1;
return;
}
if self.accepted == 0 {
self.driver.set_buffer_target(self.cfg.recv_buffers);
}
if self.cfg.lazy_loops && self.index == 0 && self.conns.len >= 3 {
self.want_grow |= !self.grown;
}
self.accepted += 1;
}
fn register(&mut self, socket: Socket, handler: Box<dyn Connection>) -> io::Result<u64> {
let key = self.conns.next_key();
let id = self.driver.stream(socket, key)?;
let k = self.conns.insert(Conn {
id,
handler: Some(handler),
input: None,
queued: 0,
reason: None,
user_paused: false,
hw_paused: false,
want_drain: false,
closing: false,
closed: false,
timers: ConnTimers::default(),
lingering: false,
linger_left: 0,
});
debug_assert_eq!(k, key);
self.driver.recv(id, true);
self.with_conn(key, |h, cx| h.on_open(cx));
Ok(key)
}
}
pub struct Ctx<'a> {
core: &'a mut Core,
key: u64,
}
impl std::fmt::Debug for Ctx<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ctx").field("loop", &self.core.index).field("key", &self.key).finish()
}
}
impl Ctx<'_> {
pub fn send(&mut self, buf: Vec<u8>) {
self.send_buf(OutBuf::Owned(buf));
}
pub fn send_static(&mut self, bytes: &'static [u8]) {
self.send_buf(OutBuf::Static(bytes));
}
pub fn send_buf(&mut self, buf: OutBuf) {
let high = self.core.cfg.write_high_water;
let c = self.core.conn(self.key);
if c.closed || buf.is_empty() {
if let OutBuf::Owned(v) = buf {
self.core.pool.put(v);
}
return;
}
c.queued += buf.len();
let id = c.id;
let pause = c.queued > high && !c.hw_paused;
if pause {
c.hw_paused = true;
}
let user_paused = c.user_paused;
self.core.driver.send(id, buf);
if pause && !user_paused {
self.core.driver.recv(id, false);
}
}
pub fn buffer(&mut self, min: usize) -> Vec<u8> {
self.core.pool.get(min)
}
pub fn recycle(&mut self, buf: Vec<u8>) {
self.core.pool.put(buf);
}
pub fn queued(&mut self) -> usize {
self.core.conn(self.key).queued
}
pub fn pause_reading(&mut self) {
let c = self.core.conn(self.key);
if c.user_paused || c.closed {
return;
}
c.user_paused = true;
let (id, hw) = (c.id, c.hw_paused);
if !hw {
self.core.driver.recv(id, false);
}
}
pub fn resume_reading(&mut self) {
let c = self.core.conn(self.key);
if !c.user_paused || c.closed {
return;
}
c.user_paused = false;
let (id, hw, has_input) = (c.id, c.hw_paused, c.input.as_ref().is_some_and(|i| !i.is_empty()));
if !hw {
self.core.driver.recv(id, true);
}
if has_input {
self.core.redeliver.push(self.key);
}
}
pub fn redeliver(&mut self) {
let c = self.core.conn(self.key);
if c.user_paused || c.closed || !c.input.as_ref().is_some_and(|i| !i.is_empty()) {
return;
}
self.core.redeliver.push(self.key);
}
pub fn notify_drain(&mut self) {
let low = self.core.cfg.write_low_water;
let c = self.core.conn(self.key);
if c.queued <= low {
self.core.drains.push(self.key);
} else {
c.want_drain = true;
}
}
pub fn close(&mut self) {
let c = self.core.conn(self.key);
if c.closed || c.closing {
return;
}
c.closing = true;
if c.queued == 0 {
self.core.linger(self.key);
}
}
pub fn abort(&mut self) {
self.core.close_now(self.key, CloseReason::Aborted);
}
pub fn is_closing(&mut self) -> bool {
let c = self.core.conn(self.key);
c.closing || c.closed
}
pub fn set_timer(&mut self, after: Duration, token: u64) -> TimerId {
let at = self.core.deadline_tick(after);
let id = self.core.timers.insert(at, TimerTarget::Conn(self.key, token));
let core = &mut *self.core;
if let Some(c) = core.conns.get_mut(self.key) {
c.timers.push(id, &core.timers);
}
id
}
pub fn cancel_timer(&mut self, id: TimerId) -> bool {
if let Some(c) = self.core.conns.get_mut(self.key) {
c.timers.remove(id);
}
self.core.timers.cancel(id).is_some()
}
pub fn handle(&mut self) -> ConnHandle {
ConnHandle { queue: self.core.queue.clone(), key: self.key, loop_index: self.core.index }
}
pub fn offload<F>(&mut self, job: F) -> Result<(), Rejected>
where
F: FnOnce() -> Message + Send + 'static,
{
let h = self.handle();
self.core.offload.try_submit(Box::new(move || {
h.send(job());
}))
}
pub fn offload_pool(&mut self) -> &OffloadPool {
&self.core.offload
}
pub fn now_ns(&mut self) -> u64 {
self.core.now_ns
}
pub fn peer_addr(&mut self) -> io::Result<SocketAddr> {
let id = self.core.conn(self.key).id;
self.core.driver.socket(id).ok_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "closed"))?.peer_addr()
}
pub fn local_addr(&mut self) -> io::Result<SocketAddr> {
let id = self.core.conn(self.key).id;
self.core.driver.socket(id).ok_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "closed"))?.local_addr()
}
pub fn socket(&mut self) -> Option<&Socket> {
let id = self.core.conn(self.key).id;
self.core.driver.socket(id)
}
pub fn loop_index(&mut self) -> usize {
self.core.index
}
#[inline(never)]
pub fn connection_pressure(&mut self) -> bool {
let max = self.core.cfg.max_connections;
let budget = simoom_sys::net::descriptor_limit().map_or(max, |fds| max.min(fds.saturating_sub(32).max(1)));
let open = self.core.shared.active.load(Ordering::Relaxed);
open.saturating_mul(4) > budget.saturating_mul(3)
}
}
pub struct UdpCtx<'a> {
core: &'a mut Core,
idx: usize,
}
impl std::fmt::Debug for UdpCtx<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UdpCtx").field("loop", &self.core.index).field("idx", &self.idx).finish()
}
}
impl UdpCtx<'_> {
pub fn send(&mut self, msgs: &[Transmit<'_>]) -> io::Result<usize> {
let id = self.core.udps[self.idx].id.ok_or_else(|| io::Error::new(io::ErrorKind::NotConnected, "closed"))?;
self.core.driver.send_to(id, msgs)
}
pub fn local_addr(&mut self) -> Option<SocketAddr> {
self.core.udps[self.idx].local
}
pub fn set_timer(&mut self, after: Duration, token: u64) -> TimerId {
let at = self.core.deadline_tick(after);
self.core.timers.insert(at, TimerTarget::Udp(self.idx, token))
}
pub fn cancel_timer(&mut self, id: TimerId) -> bool {
self.core.timers.cancel(id).is_some()
}
pub fn handle(&mut self) -> EndpointHandle {
EndpointHandle { queue: self.core.queue.clone(), idx: self.idx }
}
pub fn buffer(&mut self, min: usize) -> Vec<u8> {
self.core.pool.get(min)
}
pub fn recycle(&mut self, buf: Vec<u8>) {
self.core.pool.put(buf);
}
pub fn offload_pool(&mut self) -> &OffloadPool {
&self.core.offload
}
pub fn now_ns(&mut self) -> u64 {
self.core.now_ns
}
pub fn loop_index(&mut self) -> usize {
self.core.index
}
}
pub struct LoopCtx<'a> {
pub(crate) core: &'a mut Core,
}
impl std::fmt::Debug for LoopCtx<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LoopCtx").field("loop", &self.core.index).finish()
}
}
impl LoopCtx<'_> {
pub fn index(&self) -> usize {
self.core.index
}
pub fn adopt(&mut self, socket: Socket, handler: Box<dyn Connection>) -> io::Result<ConnHandle> {
self.core.shared.active.fetch_add(1, Ordering::AcqRel);
match self.core.register(socket, handler) {
Ok(key) => Ok(ConnHandle { queue: self.core.queue.clone(), key, loop_index: self.core.index }),
Err(e) => {
self.core.shared.active.fetch_sub(1, Ordering::AcqRel);
Err(e)
}
}
}
pub fn connections(&self) -> usize {
self.core.conns.len
}
pub fn now_ns(&self) -> u64 {
self.core.now_ns
}
pub fn driver_stats(&self) -> simoom_sys::driver::DriverStats {
self.core.driver.stats()
}
pub fn pool_stats(&self) -> crate::pool::PoolStats {
self.core.pool.stats()
}
}
pub(crate) struct LoopSetup {
pub(crate) index: usize,
pub(crate) cfg: Arc<Config>,
pub(crate) shared: Arc<Shared>,
pub(crate) offload: OffloadPool,
pub(crate) listeners: Vec<ListenerSlot>,
pub(crate) udps: Vec<UdpSlot>,
}
pub(crate) fn build(mut setup: LoopSetup) -> io::Result<(Core, Arc<Queue>)> {
let cfg = setup.cfg;
for l in setup.listeners.iter_mut() {
if let Some((addr, opts)) = l.bind.take() {
l.socket = Some(Socket::tcp_listen(addr, &opts)?);
}
}
#[cfg(feature = "udp")]
for u in setup.udps.iter_mut() {
if let Some((addr, opts)) = u.bind.take() {
u.socket = Some(Socket::udp_bind(addr, &opts)?);
}
}
let mut driver = Driver::new(&cfg.driver)?;
driver.set_buffer_target(if setup.udps.is_empty() { cfg.recv_buffers.min(INITIAL_RECV_BUFFERS) } else { cfg.recv_buffers });
let queue = Arc::new(Queue::new(driver.waker()));
let tick_ns = (cfg.timer_tick.as_nanos() as u64).max(1);
let now_ns = simoom_sys::time::now_ns();
let core = Core {
index: setup.index,
driver,
pool: BufPool::new(cfg.buffer_classes, cfg.pool_cache),
timers: TimerWheel::new(now_ns / tick_ns),
conns: Slab::new(),
now_ns,
tick_ns,
queue: queue.clone(),
peers: Vec::new(),
offload: setup.offload,
shared: setup.shared,
listeners: setup.listeners,
endpoints: if cfg!(feature = "udp") { setup.udps.iter().map(|_| None).collect() } else { Vec::new() },
udps: setup.udps,
redeliver: Vec::new(),
drains: Vec::new(),
rr: setup.index,
want_grow: false,
grow_next: false,
grown: false,
parked: Vec::new(),
handoff_until: 0,
accepted: 0,
rejected: 0,
closed: 0,
last_io: now_ns,
io_since_trim: 0,
batch_left: 0,
cfg,
};
Ok((core, queue))
}
impl Core {
pub(crate) fn backend(&self) -> (simoom_sys::driver::Backend, Option<String>) {
(self.driver.backend(), self.driver.fallback_reason().map(str::to_string))
}
fn start_io(&mut self) -> io::Result<()> {
for i in 0..self.listeners.len() {
if let Some(s) = self.listeners[i].socket.take() {
self.listeners[i].id = Some(self.driver.listen(s, LISTENER_BIT | i as u64)?);
}
}
#[cfg(feature = "udp")]
for i in 0..self.udps.len() {
if let Some(s) = self.udps[i].socket.take() {
let local = s.local_addr().ok();
let key = UDP_BIT | i as u64;
let id = if self.udps[i].gro { self.driver.udp_large(s, key, GRO_BUFFER)? } else { self.driver.udp(s, key)? };
self.driver.recv(id, true);
let slot = &mut self.udps[i];
slot.id = Some(id);
slot.local = local;
self.ensure_endpoint(i);
}
}
Ok(())
}
fn spread_parked(&mut self) {
self.peers = self.shared.handles();
let n = self.peers.len().max(1);
for (j, (socket, peer, li)) in std::mem::take(&mut self.parked).into_iter().enumerate() {
let p = (self.rr + j) % n;
match self.peers.get(p) {
Some(h) if h.index != self.index => {
if !h.run(move |lcx| lcx.core.adopt(socket, peer, li)) {
self.rejected += 1;
}
}
_ => self.adopt(socket, peer, li),
}
}
}
fn pick_loop(&mut self) -> Option<usize> {
if self.shared.started.load(Ordering::Acquire) != self.peers.len() {
self.peers = self.shared.handles();
}
let n = self.peers.len();
if n <= 1 {
return None;
}
self.rr = (self.rr + 1) % n;
let a = self.rr;
let b = (a + 1 + self.rr * 7 % (n - 1).max(1)) % n;
let la = self.shared.loop_active[self.peers[a].index].load(Ordering::Relaxed);
let lb = self.shared.loop_active[self.peers[b].index].load(Ordering::Relaxed);
let p = if lb < la { b } else { a };
(self.peers[p].index != self.index).then_some(p)
}
fn dispatch(&mut self, ev: Event) {
match ev {
Event::Accepted { key, socket, peer } => {
let li = (key & !LISTENER_BIT) as usize;
if self.want_grow || self.grow_next || (self.grown && self.shared.growing.load(Ordering::Acquire)) {
self.parked.push((socket, peer, li));
return;
}
let handoff = self.listeners.get(li).is_some_and(|l| l.handoff) || self.now_ns < self.handoff_until;
match if handoff { self.pick_loop() } else { None } {
None => self.adopt(socket, peer, li),
Some(p) => {
let ok = self.peers[p].run(move |lcx| lcx.core.adopt(socket, peer, li));
if !ok {
self.rejected += 1;
}
}
}
}
Event::Data { key, buf } => self.on_data(key, buf),
Event::Eof { key } => {
if self.conns.get_mut(key).is_some_and(|c| c.lingering) {
self.close_now(key, CloseReason::Closed);
return;
}
self.with_conn(key, |h, cx| {
if !cx.is_closing() {
h.on_eof(cx)
}
})
}
Event::Error { key, error } => {
if key & (LISTENER_BIT | UDP_BIT) != 0 {
return; }
self.close_now(key, CloseReason::Error(error));
}
Event::Sent { key, buf } => self.on_sent(key, buf),
Event::Closed { key } => self.on_closed(key),
#[cfg(not(feature = "udp"))]
Event::Datagram { buf, .. } => self.pool.put(buf),
#[cfg(feature = "udp")]
Event::Datagram { key, buf, meta } => {
let idx = (key & !UDP_BIT) as usize;
let data = buf;
self.with_udp(idx, |h, cx| h.on_datagram(cx, &data, &meta));
if self.udps.get(idx).is_some_and(|u| u.gro) {
self.driver.provide(data); } else {
self.pool.put(data);
}
}
Event::Wake => {}
}
}
fn on_data(&mut self, key: u64, buf: Vec<u8>) {
let max_input = self.cfg.max_input;
let Some(c) = self.conns.get_mut(key) else {
self.pool.put(buf);
return;
};
if c.closed || c.closing {
let n = buf.len();
self.pool.put(buf);
if c.lingering {
if n > c.linger_left as usize {
self.close_now(key, CloseReason::Closed);
} else {
c.linger_left -= n as u32;
}
}
return;
}
match c.input.as_mut() {
None => c.input = Some(buf),
Some(cur) => {
if cur.capacity() - cur.len() < buf.len() {
let mut bigger = self.pool.get(cur.len() + buf.len());
bigger.extend_from_slice(cur);
let old = std::mem::replace(cur, bigger);
self.pool.put(old);
}
cur.extend_from_slice(&buf);
self.pool.put(buf);
}
}
let c = self.conns.get_mut(key).unwrap();
if !c.user_paused && c.input.as_ref().map_or(0, |i| i.len()) > max_input {
self.close_now(key, CloseReason::InputLimit);
return;
}
if c.user_paused {
return;
}
self.call_data(key);
self.flush_conn(key);
}
#[inline]
fn flush_conn(&mut self, key: u64) {
if self.batch_left < FLUSH_EARLY_BATCH {
return;
}
if let Some(c) = self.conns.get_mut(key) {
if !c.closed {
let id = c.id;
self.driver.flush_now(id);
}
}
}
fn call_data(&mut self, key: u64) {
let Some(c) = self.conns.get_mut(key) else { return };
if c.closed || c.closing || c.user_paused {
return;
}
let Some(input) = c.input.take() else { return };
let Some(mut h) = c.handler.take() else {
c.input = Some(input);
return;
};
let n = {
let mut cx = Ctx { core: self, key };
h.on_data(&mut cx, &input)
};
let Some(c) = self.conns.get_mut(key) else {
self.pool.put(input);
return;
};
c.handler = Some(h);
let mut input = input;
if n >= input.len() {
self.pool.put(input);
} else {
let rest = input.len() - n;
if rest <= COMPACT_INPUT && input.capacity() >= 4 * rest {
let tail = input[n..].to_vec();
self.pool.put(input);
if c.input.is_none() {
c.input = Some(tail);
}
return;
}
if n > 0 {
let len = input.len();
input.copy_within(n..len, 0);
input.truncate(len - n);
}
if c.input.is_none() {
c.input = Some(input);
} else {
self.pool.put(input);
}
}
}
fn on_sent(&mut self, key: u64, buf: OutBuf) {
let len = buf.len();
if let OutBuf::Owned(v) = buf {
self.pool.put(v);
}
let low = self.cfg.write_low_water;
let Some(c) = self.conns.get_mut(key) else { return };
c.queued = c.queued.saturating_sub(len);
if c.closed {
return;
}
if c.closing {
if c.queued == 0 {
self.linger(key);
}
return;
}
let mut drain = false;
if c.hw_paused && c.queued <= low {
c.hw_paused = false;
drain = true;
if !c.user_paused {
let id = c.id;
let has_input = c.input.as_ref().is_some_and(|i| !i.is_empty());
self.driver.recv(id, true);
if has_input {
self.redeliver.push(key);
}
}
}
let c = self.conns.get_mut(key).unwrap();
if c.want_drain && c.queued <= low {
c.want_drain = false;
drain = true;
}
if drain {
self.with_conn(key, |h, cx| h.on_drain(cx));
}
}
fn on_closed(&mut self, key: u64) {
if key & LISTENER_BIT != 0 {
let i = (key & !LISTENER_BIT) as usize;
if let Some(l) = self.listeners.get_mut(i) {
l.id = None;
}
return;
}
if cfg!(feature = "udp") && key & UDP_BIT != 0 {
let i = (key & !UDP_BIT) as usize;
if let Some(u) = self.udps.get_mut(i) {
u.id = None;
self.endpoints[i] = None;
}
return;
}
let Some(mut c) = self.conns.remove(key) else { return };
c.timers.cancel_all(&mut self.timers);
if let Some(i) = c.input.take() {
self.pool.put(i);
}
self.shared.active.fetch_sub(1, Ordering::AcqRel);
self.closed += 1;
let reason = c.reason.take().unwrap_or(CloseReason::Closed);
if let Some(mut h) = c.handler.take() {
h.on_close(reason);
}
}
fn fire_timers(&mut self) {
let now_tick = self.now_ns / self.tick_ns;
self.timers.collect(now_tick);
while let Some(t) = self.timers.pop_ready() {
match t {
TimerTarget::Conn(key, token) => self.with_conn(key, |h, cx| h.on_timer(cx, token)),
TimerTarget::Udp(idx, token) => self.with_udp(idx, |h, cx| h.on_timer(cx, token)),
TimerTarget::Linger(key) => self.close_now(key, CloseReason::Closed),
}
}
}
#[cfg(feature = "idle-trim")]
#[cold]
#[inline(never)]
fn idle_trim(&mut self, timeout: Option<Duration>) -> Option<Duration> {
let at = self.last_io.saturating_add(IDLE_TRIM_NS);
if self.now_ns >= at {
self.pool.trim(IDLE_POOL_KEEP);
self.driver.trim_buffers(INITIAL_RECV_BUFFERS);
self.io_since_trim = 0;
simoom_sys::alloc::release_free_memory();
return timeout;
}
let left = Duration::from_nanos(at - self.now_ns);
Some(timeout.map_or(left, |t| t.min(left)))
}
fn after_callbacks(&mut self) {
while !self.redeliver.is_empty() || !self.drains.is_empty() {
for key in std::mem::take(&mut self.redeliver) {
self.call_data(key);
}
for key in std::mem::take(&mut self.drains) {
self.with_conn(key, |h, cx| h.on_drain(cx));
}
}
}
fn publish(&self) {
let s = &self.shared.loops[self.index];
let d = self.driver.stats();
s.store(d, self.pool.stats(), self.accepted, self.rejected, self.closed, self.conns.len as u64);
s.timers.store(self.timers.len() as u64, Ordering::Relaxed);
self.shared.loop_active[self.index].store(self.conns.len, Ordering::Relaxed);
}
fn stop_done(&self, aborted: bool) -> bool {
self.conns.len == 0 && !self.listeners.iter().any(|l| l.id.is_some()) && (aborted || !(0..self.udp_count()).any(|i| self.endpoint_busy(i)))
}
fn udp_count(&self) -> usize {
if cfg!(feature = "udp") {
self.udps.len()
} else {
0
}
}
fn endpoint_busy(&self, i: usize) -> bool {
self.endpoints[i].as_ref().is_some_and(|e| e.is_busy())
}
fn begin_stop(&mut self) {
for i in 0..self.listeners.len() {
if let Some(id) = self.listeners[i].id {
self.driver.close(id);
}
}
for key in self.conns.keys() {
self.with_conn(key, |h, cx| {
if !cx.is_closing() {
h.on_stop(cx)
}
});
}
for i in 0..self.udp_count() {
self.with_udp(i, |h, cx| h.on_stop(cx));
}
}
pub(crate) fn run(&mut self) -> io::Result<()> {
self.start_io()?;
let mut events = Vec::with_capacity(256);
let mut tasks: Vec<Item> = Vec::new();
let mut stop_at: Option<u64> = None;
let mut aborted = false;
let recv_size = self.cfg.buffer_classes[0];
loop {
self.now_ns = simoom_sys::time::now_ns();
self.fire_timers();
self.after_callbacks();
if stop_at.is_some() && self.stop_done(aborted) {
break;
}
for _ in 0..self.driver.buffers_wanted() {
let b = self.pool.get(recv_size);
self.driver.provide(b);
}
let mut timeout = self.timers.next_deadline().map(|t| Duration::from_nanos((t * self.tick_ns).saturating_sub(self.now_ns)));
if let Some(at) = stop_at {
let left = Duration::from_nanos(at.saturating_sub(self.now_ns));
timeout = Some(timeout.map_or(left, |t| t.min(left)));
if aborted {
timeout = Some(timeout.unwrap().min(Duration::from_millis(10)));
}
}
if self.grow_next || (!self.parked.is_empty() && self.grown && !self.shared.growing.load(Ordering::Acquire)) {
timeout = Some(Duration::ZERO);
}
#[cfg(feature = "idle-trim")]
if self.pool.stats().cached > 3 * IDLE_POOL_KEEP || self.io_since_trim >= BURST_EVENTS {
timeout = self.idle_trim(timeout);
}
self.driver.turn(timeout, &mut events)?;
self.now_ns = simoom_sys::time::now_ns();
#[cfg(feature = "idle-trim")]
if !events.is_empty() {
self.last_io = self.now_ns;
self.io_since_trim += events.len() as u64;
}
let mut woke = false;
let n = events.len();
for (i, ev) in events.drain(..).enumerate() {
if matches!(ev, Event::Wake) {
woke = true;
}
self.batch_left = n - i;
self.dispatch(ev);
}
if woke {
self.queue.take(&mut tasks);
let n = tasks.len();
for (i, t) in tasks.drain(..).enumerate() {
self.batch_left = n - i;
match t {
Item::Task(t) => t(&mut LoopCtx { core: self }),
Item::Conn(key, msg) => self.deliver(key, msg),
Item::Udp(idx, msg) => self.deliver_udp(idx, msg),
}
}
}
self.batch_left = 0;
if self.grown && self.handoff_until == 0 && !self.shared.growing.load(Ordering::Acquire) {
self.handoff_until = self.now_ns.saturating_add(BALANCE_AFTER_GROWTH_NS).max(1);
self.spread_parked();
simoom_sys::cpu::keep_current_thread_off_net_rx();
}
if self.grow_next {
self.grow_next = false;
self.grown = true;
self.shared.grow();
} else if self.want_grow {
self.want_grow = false;
self.grow_next = true;
}
self.after_callbacks();
self.publish();
if stop_at.is_none() && self.shared.stop.load(Ordering::Acquire) {
let grace = self.shared.grace_ns.load(Ordering::Acquire);
stop_at = Some(self.now_ns.saturating_add(grace));
self.begin_stop();
}
if let Some(at) = stop_at {
if !aborted && self.now_ns >= at {
aborted = true;
for key in self.conns.keys() {
self.close_now(key, CloseReason::Stopped);
}
for i in 0..self.udp_count() {
if self.endpoint_busy(i) {
self.with_udp(i, |h, cx| h.on_abort(cx));
}
}
}
if self.stop_done(aborted) {
break;
}
}
}
for i in 0..self.udp_count() {
if let Some(id) = self.udps[i].id.take() {
self.driver.close(id);
}
}
let end = simoom_sys::time::now_ns() + 200_000_000;
while !self.driver.is_empty() && simoom_sys::time::now_ns() < end {
self.driver.turn(Some(Duration::from_millis(10)), &mut events)?;
for ev in events.drain(..) {
if let Event::Sent { buf: OutBuf::Owned(v), .. } = ev {
self.pool.put(v);
}
}
}
self.queue.close();
self.publish();
Ok(())
}
}