valis / Reference / API reference

Multiplexer - API reference

Exported surface for the multiplexer subsystem. Part of the API reference.

Package valis/src/multiplexer

Classes

datagram-source

A UDP datagram event source. The concrete subclass adds the iolib UDP socket and the receive buffer. This abstract class carries no iolib type.

event-source

Abstract base for objects registered with a multiplexer. Concrete subclasses are stream-source (TCP listening socket) and datagram-source (UDP socket). The invariant: source-ready must NOT block the event-dispatch thread beyond the cost of accept(2) + getsockname(2) + spawning a worker thread. Blocking here blocks every other source the mux drives until source-ready returns.

multiplexer

Abstract base for event-loop backends. Concrete backends (e.g. epoll-mux, iouring-mux) subclass this and specialise register-source, deregister-source, run-loop, and stop-loop. No iolib type is referenced in any generic signature of this class — a protocol plugin names valis's interface, never iolib directly. The seam exists so the event-source backend is replaceable without touching plugins.

stream-source

A TCP listening-socket event source. The concrete subclass (e.g. in the epoll backend) adds the iolib passive socket and any per-source registry reference. This abstract class carries no iolib type.

Conditions

mux-not-implemented

Signalled when a multiplexer generic is called on a class that has not specialised it. A registered multiplexer backend is a promise to drive the event loop; this condition marks a broken promise rather than letting it pass silently.

source-not-implemented

Signalled when an event-source generic is called on a class that has not specialised it. Concrete sources own a fd and a readiness handler; this condition marks the contract being unfulfilled.

Generic functions

cancel-loop-timer

(cancel-loop-timer mux handle)

Withdraw HANDLE, obtained from schedule-on-loop, so its body does not run again. Returns MUX. Tolerates a handle that has already fired or has already been cancelled: a caller cancelling on a close path cannot know which of those happened, and making it find out would put a race in every teardown.

close-multiplexer

(close-multiplexer mux)

Release any operating-system resources the multiplexer holds beyond its registered sources: a wakeup primitive's fds, the loop's own backing structure, and so on. Called by stop-listener after every source has been closed, so the mux is the last layer torn down. Returns MUX. The default method is a no-op so backends without extra-source resources can ignore it; concrete backends override to close what they allocated at construction. Safe to call more than once — concrete methods tolerate already-closed fds.

close-source

(close-source source)

Release any operating-system resources (sockets, fds, foreign buffers) held by SOURCE. Called by stop-listener after the event loop has joined, so the listening sockets a source acquired at construction time do not leak across start/stop cycles inside one process. The default method is a no-op so abstract sources (and sources that genuinely hold no external resource) can be ignored safely; concrete backend sources specialise this to close their iolib socket. Returns SOURCE.

deregister-source

(deregister-source mux source)

Remove SOURCE from MUX. No further source-ready callbacks will be issued for SOURCE after this returns. Returns SOURCE after removal.

mux-not-implemented-mux

(mux-not-implemented-mux condition)

Undocumented: this exported symbol needs a docstring.

register-source

(register-source mux source)

Register SOURCE with MUX so that MUX calls (source-ready source) when the source's fd is readable. Returns SOURCE.

run-loop

(run-loop mux)

Run the multiplexer loop until stop-loop is called. Blocks the calling thread until stop-loop is called.

run-on-loop-thread

(run-on-loop-thread mux thunk)

Schedule THUNK (a function of no arguments) to run on the loop thread — the thread executing run-loop — rather than the caller's thread. Returns immediately; THUNK runs asynchronously the next time the loop wakes.

This is the seam a worker thread uses to mutate loop-owned state safely. A backend whose event loop is not thread-safe to mutate from outside (iolib's event-base is exactly this) cannot have its source registration touched from a worker thread while the loop is dispatching; routing the mutation through this operation moves it onto the loop thread, where it is same-thread with the dispatch and therefore safe. The edge controller's accept backpressure uses it to re-register a deregistered listening source when a connection slot frees on a worker thread.

schedule-on-loop

(schedule-on-loop mux seconds thunk &key repeat)

Run THUNK, a function of no arguments, on MUX's loop thread after SECONDS. Once when REPEAT is false, and every SECONDS until cancelled while it is true. Returns an opaque handle the backend defines and no caller inspects; the only thing to do with it is hand it back to cancel-loop-timer.

This exists rather than a per-connection timer scheduled by the host implementation because a body that runs on the loop thread is same-thread with dispatch. It may therefore touch loop-owned state, a registration or a write interest, with no cross-thread routing and no lock, which is exactly what a liveness probe on an otherwise idle connection has to do.

set-write-interest

(set-write-interest mux source on)

Ask MUX to watch SOURCE for room to write. While ON is true MUX additionally calls source-writable on SOURCE whenever the source's descriptor will accept more output; when ON is false that interest is withdrawn and readability monitoring is left exactly as it was. Returns SOURCE.

Two properties a caller depends on. The interest is idempotent: asking for the state it already holds is not an error, so a send path may re-assert interest on every enqueue without remembering what it last asked for. And the interest may only be changed on the loop thread, because the change mutates state the loop owns while it is dispatching; a worker thread routes the call through run-on-loop-thread instead of making it directly.

source-bound-port

(source-bound-port source)

Return the integer port SOURCE's descriptor is bound to, recovered from the descriptor itself (getsockname), or NIL when the backend cannot recover it. For a steered source the port a connection is dialed on — its recovered destination-port — equals this, so it is the identity a serve wiring registers its protocol and tears its loop down by, independent of any port argument a caller passed (which may be nil). The default method returns NIL; concrete backend sources recover the real value.

source-fd

(source-fd source)

Return the integer file descriptor the multiplexer should monitor.

source-not-implemented-source

(source-not-implemented-source condition)

Undocumented: this exported symbol needs a docstring.

source-ready

(source-ready source)

Called by the multiplexer when source-fd is readable. Implementations must complete within the cost of accept(2) + getsockname(2) + spawning a worker thread — the dispatch loop is single-threaded, so blocking here blocks the entire event loop. For stream sources this means accept-non-blocking + build a tcp-connection + hand to the executor; for datagram sources this means receive-from + build a datagram-connection + hand to the executor.

source-transport-kind

(source-transport-kind object)

Undocumented: this exported symbol needs a docstring.

source-writable

(source-writable source)

Called by the multiplexer when SOURCE's descriptor will accept more output, for as long as write interest is set on it. The writability counterpart of source-ready, carrying the same non-blocking obligation: an implementation must return within the cost of one write syscall, because the dispatch loop is single-threaded and blocking here blocks every other source the mux drives.

stop-loop

(stop-loop mux)

Signal the loop to exit after the current iteration; returns immediately. The event-dispatch thread observes the signal at its next iteration and exits run-loop cleanly.