|
Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
|
This page is the implementation map for the I/O reactor: the files, the reactor thread, the data flow, and the contract a new backend must satisfy. For the design and its rationale — why completion over readiness, why a thread of its own, why quiescence gains an in-flight clause — see the "The Reactor" and "Fiber I/O" sections of the language report. The fiber-facing side of the bridge is documented in Fiber subsystem implementation overview.
| File | Role |
|---|---|
| serene/rt/reactor.h | Public surface: the op set (srn_reactor_io_op_t), the submission (srn_reactor_io_request_t) and completion (srn_reactor_io_completion_t) structs, the lifecycle (srn_reactor_init/activate/shutdown), and submit/reap/consume. |
| rt/reactor/internal.h | The private srn_reactor_t, the channel (srn_reactor_io_channel_t = an SQ/CQ pair), srn_reactor_post_completion, and srn_reactor_errors_static. |
| rt/reactor/reactor.c | The core: lifecycle, the reactor thread loop, request routing, the timer min-heap, and the static IO error table. Backend-agnostic. |
| rt/reactor/backend.h | The backend vtable (srn_reactor_backend_t) and the SRN_REACTOR_BACKEND_FEAT_* capability flags. |
| rt/reactor/backend/epoll.c | The epoll backend: the one backend today. Linux-guarded. |
| rt/fiber/io.c, serene/rt/fiber/io.h | The fiber↔reactor bridge and the fiber-facing IO surface — see Fiber subsystem implementation overview. |
The reactor runs on its own OS thread, spawned at srn_reactor_activate and joined at srn_reactor_shutdown. Its whole life is reactor_thread_main:
The loop runs until running is cleared by srn_reactor_shutdown, which also nudges the thread so it observes the flag instead of blocking forever in wait.
A channel is a per-worker pair of single-producer/single-consumer rings (srn_spsc_ring_t): the SQ carries requests to the reactor, the CQ carries completions back. channel_id == worker_id. The reactor owns the rings; a worker holds only its channel index.
inflight is bumped on submit and dropped in op_done after the fiber is made runnable (ready-before-decrement), so the count reaches zero only once every result has been both produced and handled — never in the gap between. The scheduler folds srn_reactor_idle into quiescence: a pool with an empty run queue and parked workers is finished only when the reactor is idle too.
A backend is one srn_reactor_backend_t vtable instance in its own rt/reactor/backend/<name>.c, driven entirely on the reactor thread. To add one (io_uring, kqueue, IOCP):
Implement the vtable — name, features, init, shutdown, submit, wait, wake.
Uphold the invariants:
Wire it in — declare the extern vtable in backend.h, add a case to the backend switch in srn_reactor_init (reactor.c), and give SRN_CHOOSE_BACKEND in configuration.h a value for the target platform.
Guard the file — a backend file compiles only on its platform (epoll.c is #ifdef __linux__). Off-target it becomes an empty translation unit; a sibling backend supplies the symbol there. See the cross-compile scaffolding note in the report's portability milestone.