valis / Reference / API reference

Listener - API reference

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

Package valis/src/listener

Classes

listener

Handle for a running listener. Holds the multiplexer, the event-loop thread, and the sources registered with the mux so stop-listener has everything it needs to shut down cleanly. The listener is opaque to protocol plugins: they interact with the multiplexer through valis/src/multiplexer's abstract generics and never read the listener handle directly.

Conditions

event-loop-did-not-stop

Signalled by stop-listener when the event-loop thread is still running after the stop timeout. It replaces a silent wait with no end.

Generic functions

listener-mux

(listener-mux object)

Undocumented: this exported symbol needs a docstring.

listener-sources

(listener-sources object)

Undocumented: this exported symbol needs a docstring.

listener-thread

(listener-thread object)

Undocumented: this exported symbol needs a docstring.

Functions

start-listener

(start-listener &key sources (mux (funcall *default-multiplexer-factory*)))

Register SOURCES with MUX, spawn a background thread running the event loop, and return a listener handle. SOURCES is a list of pre-built event-source objects (created via make-stream-source / make-datagram-source on the chosen backend); each source already carries its executor. MUX defaults to a fresh multiplexer produced by default-multiplexer-factory: bind that var to override the backend choice. Does NOT accept an executor keyword: source ownership of the executor is the contract, so a listener-level executor would be either redundant or contradictory. The listener retains SOURCES so stop-listener can close their underlying OS resources after the loop joins.

If any step fails before the listener handle is constructed (register-source signals fd-already-registered, an iolib error from set-io-handler, or make-thread fails), the caller never receives a handle, so stop-listener could never reach what the partial start acquired. The function releases it instead: it deregisters every source already wired up, closes their sockets, and closes the multiplexer. When the failure comes after the loop thread started, it stops and joins that thread first. If the thread does not return within listener-stop-timeout-seconds, the function deliberately leaves the sockets and the multiplexer open. Freeing the multiplexer's event buffer while its thread still runs would corrupt the heap, and a leak is the lesser failure.

stop-listener

(stop-listener &optional (listener *listener*))

Signal LISTENER's multiplexer to exit, join the event-loop thread, close each registered source's underlying OS resource, then close the multiplexer itself. Safe to call with NIL (no-op). Returns LISTENER. The join completes promptly because stop-loop on the epoll backend wakes the loop via its self-pipe wakeup; without that wakeup, an idle loop with no traffic would block indefinitely in epollwait. close-source runs after the join so no source-ready callback races a closed socket; close-multiplexer runs last so the wakeup pipe and event-base stay alive while sources are being closed.

Stopping a listener that is already stopped does nothing, and two threads stopping the same listener at once take turns, so the sources and the multiplexer are closed exactly once. Closing them a second time could shut descriptors that by then belong to someone else.

A loop that has not returned within listener-stop-timeout-seconds signals EVENT-LOOP-DID-NOT-STOP rather than leaving you waiting with no word of why. Nothing is closed in that case, because the loop thread may still be using every source and the multiplexer, and a later call tries the stop again.

Variables

*default-multiplexer-factory*

A function of no arguments returning a fresh multiplexer instance. Defaults to the epoll backend. This is the single backend-selection point in the assembly layer: rebind to select a different backend (e.g. an iouring multiplexer in a future phase) without changing any call site. Tests rebind it to inject a mock or to pre-allocate a mux with extra inspection hooks.

*listener*

The process-wide listener instance, or NIL if not yet started. run-foreground sets this after start-listener returns so stop-listener can default to the running listener without the caller threading a handle through every code path.

*listener-stop-timeout-seconds*

How long stop-listener waits for the event loop to return before it signals EVENT-LOOP-DID-NOT-STOP. A healthy loop returns within one wakeup, so reaching this means the loop thread is stuck inside a callback or never heard the request.