Expand description
simoom runtime: one event loop per core, timer wheel, buffer pools, cross-thread wakeups, offload pool.
§Model
- One loop thread per core, shared-nothing. Each loop owns an I/O driver
(
simoom_sys::driver::Driver: epoll or io_uring on Linux, IOCP on Windows, kqueue on macOS), a buffer pool (pool::BufPool), a timer wheel (timer::TimerWheel) and its connections. Nothing on the hot path is shared between loops. - Completion style. The driver receives into pooled buffers and sends owned buffers; a connection sees bytes in and hands bytes out. Readiness is never exposed.
- An idle connection holds no buffer. Buffers are lent to the driver, taken only when data arrives, and returned to the pool as soon as a handler has consumed the data and the reply is written.
- Accepting. Linux: one
SO_REUSEPORTlistener per loop. Windows: one listener with a queue ofAcceptExcalls on loop 0, which hands connections to the less busy of two loops. macOS: one shared listener, handed out the same way. - UDP. One endpoint per loop (
SO_REUSEPORTsockets on Linux; loop 0 elsewhere) with batched receive and send, GRO/GSO, ECN and packet info, for QUIC. - Offload. Blocking work goes to a bounded shared pool (
offload::OffloadPool) with CoDel admission; results come back to the connection’s loop as messages. - Caps and backpressure. Connection caps (total and per loop: over the cap a connection is accepted and closed at once), a cap on unconsumed input per connection, and write high/low-water marks that pause and resume reading.
- Graceful stop with a deadline (
RuntimeHandle::stop).
§Driving a protocol
Protocol crates are sans-io: they take received bytes and produce bytes to send. The glue is
a Connection per connection, created by an Acceptor:
use simoom_rt::{Connection, ConnInfo, Ctx, Runtime};
struct Echo;
impl Connection for Echo {
fn on_data(&mut self, cx: &mut Ctx<'_>, data: &[u8]) -> usize {
let mut out = cx.buffer(data.len()); // pooled
out.extend_from_slice(data);
cx.send(out); // written at the end of the loop turn, batched with other output
data.len() // consumed everything: the input buffer goes back to the pool
}
}
let rt = Runtime::builder()
.threads(0) // one loop per CPU
.listen("127.0.0.1:8080", |_: &ConnInfo| Some(Box::new(Echo) as Box<dyn Connection>))
.start()
.unwrap();
println!("listening on {:?}", rt.local_addrs());
rt.stop(std::time::Duration::from_secs(5));
rt.join();Connection::on_datagets every unconsumed byte and returns how many it consumed. A partial message stays buffered for the next call.Ctx::send/Ctx::send_staticqueue output;Ctx::closecloses after flushing.Ctx::set_timerarms a timer (Connection::on_timer), for request deadlines and idle timeouts.Ctx::pause_reading/Ctx::resume_readingfor backpressure;Ctx::notify_drainandConnection::on_drainfor streaming output.Ctx::handlegives aSendConnHandleto deliver aMessagefrom any thread (a deferred reply);Ctx::offloadruns a closure on the offload pool and delivers its result the same way.
UDP protocols (QUIC) implement Endpoint, created per loop by a UdpService.
Modules§
- offload
- A bounded, shared worker pool for blocking work (the M/M/c side of the hybrid design).
- pool
- Per-loop buffer pool in size classes.
- timer
- A hierarchical timing wheel (Varghese and Lauck, SOSP 1987).
Structs§
- Builder
- Builds and starts a runtime. See
crate::Runtime::builder. - Config
- Runtime settings.
Config::default()suits a server. - Conn
Handle - A handle to one connection, usable from any thread. Messages to a closed connection are dropped.
- Conn
Info - What the runtime knows about a new connection when it asks an
Acceptorfor a handler. - Ctx
- What a
Connectioncallback can do: send, close, pause, set timers, reach other threads. - Driver
Config - Driver settings. The defaults suit a server loop.
- Driver
Stats - System calls and events counted by a driver (plain counters; read them on the loop thread).
- Endpoint
Handle - A handle to a UDP endpoint, usable from any thread.
- Listen
Options - Options for
Socket::tcp_listen. - LoopCtx
- Access to a loop from a task run with
LoopHandle::run. - Loop
Handle - A handle to one loop, usable from any thread: run closures on the loop thread.
- Loop
Snapshot - Counters of one loop, as last published by it (once per loop turn).
- Recv
Meta - What the OS reported about one received datagram (or one GRO batch of datagrams).
- Runtime
- Entry point:
Runtime::builder(). - Runtime
Handle - A running runtime. Dropping it stops the runtime at once (no grace) and waits for its
threads; call
RuntimeHandle::stopandRuntimeHandle::joinfor a graceful stop. - Socket
- An owned, non-blocking socket. Closed on drop.
- Stats
- Runtime counters.
- Transmit
- One datagram (or, with
segment_size, a GSO batch of datagrams) to send. - UdpCtx
- What an
Endpointcallback can do. - UdpInfo
- Information for creating a UDP endpoint on a loop.
- UdpOptions
- Options for
Socket::udp_bind.
Enums§
- Backend
- Which I/O back end a driver uses.
- Close
Reason - Why a connection was closed, passed to
Connection::on_close. - OutBuf
- A buffer to send: owned (returned by
Event::Sentfor reuse) or static.
Traits§
- Acceptor
- Creates a
Connectionfor each accepted socket. Shared by every loop, so it must beSend + Sync; any closureFn(&ConnInfo) -> Option<Box<dyn Connection>>is one. - Connection
- A connection’s protocol state machine. It lives on one loop thread and is called only from
there, so it need not be
Send. A sans-io protocol crate sits behind it: received bytes go in throughConnection::on_data, bytes to send go out throughCtx::send. - Endpoint
- A UDP endpoint (for QUIC, a whole connection table) on one loop.
- UdpService
- Creates the per-loop
Endpointof a UDP binding. On Linux every loop gets its ownSO_REUSEPORTsocket and endpoint; elsewhere only loop 0 does.
Type Aliases§
- Message
- A value sent to a connection or endpoint from another thread (
crate::ConnHandle,crate::EndpointHandle,Ctx::offload). Downcast it withmsg.downcast::<T>().