Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
reactor.h
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <[email protected]>
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19/** @file
20 Reactor overview
21
22 The reactor is in charge of all the IO in the runtime.
23
24 It is completion based and modeled after
25 [io_uring](https://man7.org/linux/man-pages/man7/io_uring.7.html); a submission
26 describes a whole request, and a completion reports its result. The reactor
27 utilizes different backends for different platforms.
28 A readiness backend such as epoll performs the transfer itself when the
29 descriptor signals readiness and posts the finished result, so the contract is
30 the common subset every platform can honour.
31
32 A worker and the reactor talk through a channel: a channel is a pair of
33 submission queue (SQ) and a completion queue (CQ), a SQ carrying
34 requests toward the reactor and a CQ carrying results back. There
35 is one channel per worker. The reactor owns every channel; a worker holds only
36 the channel id and never touches a raw queue.
37
38 Each request carries `fiber_data`, an opaque token the reactor round-trips
39 untouched: it rides in on the submission and comes back out on the completion,
40 and is how a finished result finds the fiber that was waiting.
41*/
42
43#pragma once
44
45#include <stddef.h>
46#include <stdint.h>
47
48#include "serene/rt/errors.h"
49#include "serene/rt/trace.h"
50
51typedef struct srn_engine_t srn_engine_t;
53
54/// The implementation is internal to the runtime
55typedef struct srn_reactor_t srn_reactor_t;
56typedef struct srn_scheduler_t srn_scheduler_t;
57
58#define REACTOR_TRACEPOINT(...) SRN_TRACEPOINT_WITH_GROUP(reactor __VA_OPT__(, ) __VA_ARGS__)
59
60/**
61 * The operation a submission asks the reactor to perform.
62 */
63typedef enum srn_reactor_io_op_t {
64 /// Completes immediately with a zero result. Exercises the
65 /// submit-and-complete
66 /// path on its own, with no descriptor and no kernel mechanism.
68 /// Wait out a deadline.
70 /// Read from a descriptor into `buf`. Uses `fd`, `buf`, `len`, `offset`.
72 /// Write `buf` to a descriptor. Uses `fd`, `buf`, `len`, `offset`.
74 /// Cancel one inflight operation, named by its `fiber_data`. Not
75 /// implemented, submitting it panics. It lands together with the fd
76 /// deregistration work.
78 /// Accept a pending connection on the listening socket `fd`, with `flags`
79 /// (the `accept4` flags). The peer address is written to `addr`/`addrlen`
80 /// when `addr` is non-null. The completion's `result` is the accepted
81 /// descriptor; failures arrive through the completion's `maybe_error`.
83 /// Connect `fd` to the address in `addr`/`addrlen`. The connect is started at
84 /// submit time and completes once the socket is writable. `result` is 0 on
85 /// success; failures arrive through the completion's `maybe_error`.
87 /// Receive a datagram from `fd` into `buf`/`len` with `flags`. The sender
88 /// address is written to `addr`/`addrlen` when `addr` is non-null. `result`
89 /// is bytes received; failures arrive through the completion's
90 /// `maybe_error`.
92 /// Send `buf`/`len` on `fd` to the address in `addr`/`addrlen` with `flags`.
93 /// `result` is bytes sent; failures arrive through the completion's
94 /// `maybe_error`.
96 /// Receive into the `struct msghdr` that `buf` points at, on `fd`, with
97 /// `flags` (scatter/gather and ancillary data). `result` is bytes received;
98 /// failures arrive through the completion's `maybe_error`.
100 /// Send the `struct msghdr` that `buf` points at, on `fd`, with `flags`.
101 /// `result` is bytes sent; failures arrive through the completion's
102 /// `maybe_error`.
104 /// Wait until `fd` is ready for the events requested in `flags`
105 /// (`EPOLLIN`/`EPOLLOUT`) without performing any transfer. `result` is the
106 /// ready-events bitmask. The gateway for timerfd, signalfd, eventfd, inotify
107 /// and pidfd.
110
111/**
112 * A submission, one request a fiber places on its channel. The reactor copies
113 * it into the submission queue, so the struct itself need not outlive the call;
114 * a buffer it points at (`buf`) must stay valid until the operation completes.
115 */
118 /// The descriptor the operation targets. Unused by SRN_REACTOR_IO_NOP and
119 /// SRN_REACTOR_IO_SLEEP.
120 int fd;
121 /// The transfer buffer for READ, WRITE, RECVFROM, and SENDTO, and the
122 /// `struct msghdr *` for RECVMSG and SENDMSG.
123 void *buf;
124 /// The transfer length, in bytes, for READ, WRITE, RECVFROM, and SENDTO.
125 size_t len;
126 /// The file offset for SRN_REACTOR_IO_READ and SRN_REACTOR_IO_WRITE; -1 reads
127 /// at the current position.
128 int64_t offset;
129 /// The wake deadline for SRN_REACTOR_IO_SLEEP, on the monotonic clock, in
130 /// nanoseconds.
131 uint64_t deadline_ns;
132 /// Opaque token, round tripped to the matching completion. The reactor never
133 /// reads it. For SRN_REACTOR_IO_CANCEL it names the target operation. The
134 /// workers or the scheduler set their data here and expect it back on the
135 /// completion counterpart
137 /// Socket address. Input for SRN_REACTOR_IO_CONNECT and SRN_REACTOR_IO_SENDTO
138 /// (the peer to use); output for SRN_REACTOR_IO_ACCEPT and
139 /// SRN_REACTOR_IO_RECVFROM (the peer that connected or sent), or null to
140 /// ignore. A `struct sockaddr *`, kept as `void *` so this header stays free
141 /// of socket headers.
142 void *addr;
143 /// Length of `addr`, the exact address length for SRN_REACTOR_IO_CONNECT and
144 /// SRN_REACTOR_IO_SENDTO, the buffer capacity for SRN_REACTOR_IO_ACCEPT and
145 /// SRN_REACTOR_IO_RECVFROM.
146 uint32_t addrlen;
147 /// Operation flags, the `MSG_*` flags for the datagram and message ops, the
148 /// `accept4` flags for SRN_REACTOR_IO_ACCEPT, and the requested readiness
149 /// (`EPOLLIN`/`EPOLLOUT`) for SRN_REACTOR_IO_POLL.
150 int flags;
152
153/**
154 * A completion, the result of one submission, echoed back on the channel. It is a fallible value
155 * (see errors.h). `maybe_error` is null on success and points at the failure otherwise, so
156 * `result`/`addrlen` are meaningful only when there is no error. The backend sets `maybe_error`
157 * from a static IO error to avoid unnecessary memory allocation.
158 */
161 /// The `fiber_data` of the submission this completes.
163 /// The result of the operation on success, bytes transferred for the
164 /// transfers, the accepted descriptor for SRN_REACTOR_IO_ACCEPT, 0 for SRN_REACTOR_IO_CONNECT and
165 /// SRN_REACTOR_IO_NOP. Unspecified when `maybe_error` is set.
166 size_t result;
167 /// For SRN_REACTOR_IO_ACCEPT and SRN_REACTOR_IO_RECVFROM, the actual length of
168 /// the address written into the request's `addr` (the kernel's value-result
169 /// `addrlen`), which lets a caller interpret a variable or mixed-family
170 /// address (IPv6, Unix-domain, a dual-stack listener). The consumer knows its
171 /// op, so it reads this only for those two; unused otherwise.
172 uint32_t addrlen;
174
175/**
176 * The interface that the scheduler supplies so the reactor can announce that a channel has
177 * completions, without the reactor knowing about workers or parking. The reactor calls it after
178 * placing completions on `channel`; the scheduler wakes that channel's worker if it is parked.
179 */
180typedef void (*srn_reactor_notify_fn)(srn_scheduler_t *sched, size_t channel);
181
182// -----------------------------------------------------------------------------
183// Lifecycle
184// -----------------------------------------------------------------------------
185/// Allocate the IO reactor from the engine.
186[[nodiscard]] [[gnu::nonnull(1)]]
188
189/**
190 * Bring the reactor up, allocate `nchannels` channels (one per worker) and
191 * start the reactor thread. `notify` is called with the scheduler and the
192 * channel id whenever a channel gains completions.
193 */
194[[gnu::nonnull(1)]]
195void srn_reactor_activate(srn_reactor_t *reactor, size_t nchannels, srn_reactor_notify_fn notify);
196
197/**
198 * Tear the reactor down, stop and join the reactor thread and release its
199 * channels.
200 */
201[[gnu::nonnull(1)]]
203
204// -----------------------------------------------------------------------------
205// Submit and consume
206// -----------------------------------------------------------------------------
207/**
208 * Place a request on `channel`'s submission queue and rouse the reactor.
209 * Returns false without submitting when the channel already has a full
210 * ring's worth of operations in flight. The condition is transient, it
211 * clears as the channel's worker consumes completions, so the caller can
212 * fail the operation back with CHANNEL_BUSY and let the fiber retry.
213 */
214[[nodiscard]] [[gnu::nonnull(1, 3)]]
216 srn_reactor_t *reactor, size_t channel, const srn_reactor_io_request_t *request
217);
218
219/**
220 * Drain up to `max` completions from `channel`'s completion queue into `out`,
221 * returning how many were taken.
222 */
223[[gnu::nonnull(1, 3)]]
224size_t srn_reactor_reap(
225 srn_reactor_t *reactor, size_t channel, srn_reactor_io_completion_t *out, size_t max
226);
227
228/**
229 * Drop `channel`'s and the reactor's in-flight counts by one. The consumer of
230 * a completion calls this after it has finished acting on it (for fibers,
231 * after the waiting fiber is readied), so that "in flight" reaches zero only
232 * once every result has been both produced and handled. Dropping the channel
233 * count is also what re-opens the channel's admission window (see
234 * srn_reactor_submit).
235 */
236[[gnu::nonnull(1)]]
237void srn_reactor_op_done(srn_reactor_t *reactor, size_t channel);
238
239/**
240 * Whether the reactor has no operations in flight. The scheduler folds this
241 * into quiescence, a pool with parked workers and an empty run queue is only
242 * truly done when the reactor is idle too.
243 */
244[[nodiscard]] [[gnu::nonnull(1)]]
245bool srn_reactor_idle(srn_reactor_t *reactor);
246
247/**
248 * Drain and act on `channel`'s completions. Called by the worker that owns the
249 * channel, from its loop. Per completion it sets the waiter's result, readies
250 * the fiber, and drops the in-flight count.
251 */
252[[gnu::nonnull(1)]]
253void srn_reactor_consume(srn_reactor_t *reactor, size_t channel);
254
255/**
256 * Whether `channel`'s completion queue has unconsumed completions. The worker
257 * checks this under the scheduler lock before parking, so a completion landing
258 * just before it sleeps is not missed.
259 */
260[[nodiscard]] [[gnu::nonnull(1)]]
261bool srn_reactor_channel_has_completions(srn_reactor_t *reactor, size_t channel);
Error handling for the runtime.
void srn_reactor_activate(srn_reactor_t *reactor, size_t nchannels, srn_reactor_notify_fn notify)
Bring the reactor up, allocate nchannels channels (one per worker) and start the reactor thread.
Definition reactor.c:401
void srn_reactor_consume(srn_reactor_t *reactor, size_t channel)
Drain and act on channel's completions.
Definition io.c:123
void srn_reactor_op_done(srn_reactor_t *reactor, size_t channel)
Drop channel's and the reactor's in-flight counts by one.
Definition reactor.c:167
void(* srn_reactor_notify_fn)(srn_scheduler_t *sched, size_t channel)
The interface that the scheduler supplies so the reactor can announce that a channel has completions,...
Definition reactor.h:180
srn_reactor_io_op_t
The operation a submission asks the reactor to perform.
Definition reactor.h:63
@ SRN_REACTOR_IO_SLEEP
Wait out a deadline.
Definition reactor.h:69
@ SRN_REACTOR_IO_ACCEPT
Accept a pending connection on the listening socket fd, with flags (the accept4 flags).
Definition reactor.h:82
@ SRN_REACTOR_IO_READ
Read from a descriptor into buf. Uses fd, buf, len, offset.
Definition reactor.h:71
@ SRN_REACTOR_IO_SENDTO
Send buf/len on fd to the address in addr/addrlen with flags.
Definition reactor.h:95
@ SRN_REACTOR_IO_NOP
Completes immediately with a zero result.
Definition reactor.h:67
@ SRN_REACTOR_IO_CONNECT
Connect fd to the address in addr/addrlen.
Definition reactor.h:86
@ SRN_REACTOR_IO_CANCEL
Cancel one inflight operation, named by its fiber_data.
Definition reactor.h:77
@ SRN_REACTOR_IO_RECVFROM
Receive a datagram from fd into buf/len with flags.
Definition reactor.h:91
@ SRN_REACTOR_IO_RECVMSG
Receive into the struct msghdr that buf points at, on fd, with flags (scatter/gather and ancillary da...
Definition reactor.h:99
@ SRN_REACTOR_IO_WRITE
Write buf to a descriptor. Uses fd, buf, len, offset.
Definition reactor.h:73
@ SRN_REACTOR_IO_POLL
Wait until fd is ready for the events requested in flags (EPOLLIN/EPOLLOUT) without performing any tr...
Definition reactor.h:108
@ SRN_REACTOR_IO_SENDMSG
Send the struct msghdr that buf points at, on fd, with flags.
Definition reactor.h:103
size_t srn_reactor_reap(srn_reactor_t *reactor, size_t channel, srn_reactor_io_completion_t *out, size_t max)
Drain up to max completions from channel's completion queue into out, returning how many were taken.
Definition reactor.c:229
bool srn_reactor_idle(srn_reactor_t *reactor)
Whether the reactor has no operations in flight.
Definition reactor.c:175
void srn_reactor_shutdown(srn_reactor_t *reactor)
Tear the reactor down, stop and join the reactor thread and release its channels.
Definition reactor.c:66
srn_reactor_t * srn_reactor_init(srn_engine_t *engine)
Allocate the IO reactor from the engine.
Definition reactor.c:34
bool srn_reactor_channel_has_completions(srn_reactor_t *reactor, size_t channel)
Whether channel's completion queue has unconsumed completions.
Definition reactor.c:246
bool srn_reactor_submit(srn_reactor_t *reactor, size_t channel, const srn_reactor_io_request_t *request)
Place a request on channel's submission queue and rouse the reactor.
Definition reactor.c:180
Engine is a structure to own the long living and main pieces of the compiler.
Definition engine.h:51
One async system behind the reactor contract.
Definition backend.h:55
A completion, the result of one submission, echoed back on the channel.
Definition reactor.h:159
void * fiber_data
The fiber_data of the submission this completes.
Definition reactor.h:162
uint32_t addrlen
For SRN_REACTOR_IO_ACCEPT and SRN_REACTOR_IO_RECVFROM, the actual length of the address written into ...
Definition reactor.h:172
size_t result
The result of the operation on success, bytes transferred for the transfers, the accepted descriptor ...
Definition reactor.h:166
A submission, one request a fiber places on its channel.
Definition reactor.h:116
int fd
The descriptor the operation targets.
Definition reactor.h:120
int flags
Operation flags, the MSG_* flags for the datagram and message ops, the accept4 flags for SRN_REACTOR_...
Definition reactor.h:150
void * addr
Socket address.
Definition reactor.h:142
srn_reactor_io_op_t op
Definition reactor.h:117
void * buf
The transfer buffer for READ, WRITE, RECVFROM, and SENDTO, and the struct msghdr * for RECVMSG and SE...
Definition reactor.h:123
size_t len
The transfer length, in bytes, for READ, WRITE, RECVFROM, and SENDTO.
Definition reactor.h:125
uint64_t deadline_ns
The wake deadline for SRN_REACTOR_IO_SLEEP, on the monotonic clock, in nanoseconds.
Definition reactor.h:131
int64_t offset
The file offset for SRN_REACTOR_IO_READ and SRN_REACTOR_IO_WRITE; -1 reads at the current position.
Definition reactor.h:128
void * fiber_data
Opaque token, round tripped to the matching completion.
Definition reactor.h:136
uint32_t addrlen
Length of addr, the exact address length for SRN_REACTOR_IO_CONNECT and SRN_REACTOR_IO_SENDTO,...
Definition reactor.h:146
Platform neutral static tracepoints.