Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
io.c
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 The fiber side of the reactor: the only layer that knows both fibers and the
21 reactor.
22
23 A fiber issues IO through a blocking looking wrapper here. The wrapper builds the
24 request on the fiber's own stack, suspends, and the suspend's commit submits it
25 to the reactor -- after the fiber has fully parked, so the completion can never
26 race the park. Each completion carries a pointer to the fiber's on-stack
27 waiter. The owning worker drains its channel (srn_reactor_consume) and, per
28 completion, copies the outcome into the waiter, readies the fiber, and drops the
29 inflight count. The reactor thread only posts completions and wakes the worker;
30 it never touches fibers.
31 */
32
33#include "serene/rt/fiber/io.h"
34
35#include <assert.h>
36
37#include "rt/reactor/backend.h"
38#include "rt/reactor/internal.h"
39#include "serene/rt/context.h"
40#include "serene/rt/engine.h"
41#include "serene/rt/errors.h"
42#include "serene/rt/fiber.h"
43#include "serene/rt/reactor.h"
44#include "serene/utils.h"
45
46#ifdef __linux__
47# include <errno.h>
48# include <fcntl.h>
49# include <sys/socket.h>
50# include <sys/stat.h>
51# include <unistd.h>
52#else
53# error "io.c needs to be patched to support this platform"
54#endif
55
56#define MICROSEC 1000ULL
57#define MILLISEC 1000ULL * MICROSEC
58#define SEC 1000ULL * MILLISEC
59
60/**
61 * Lives on the suspended fiber's stack; the worker fills it from the completion
62 * and the fiber reads it on resume. It mirrors the completion's payload minus
63 * the routing token, and is itself a fallible value (`maybe_error` null on
64 * success).
65 */
72
73/**
74 * Carries the submission across the suspend, on the fiber's stack.
75 */
83
84/**
85 * Runs on the worker's loop once the fiber has switched out -- the race free
86 * point to hand the request to the reactor. Returns true to stay suspended; the
87 * worker readies the fiber when it consumes the completion.
88 *
89 * If the scheduler is winding down (`srn_sched_drain`/`srn_sched_stop`), the
90 * submission is fenced, the request is never handed to the reactor, the waiter
91 * is set to the CANCELED error, and the commit returns false so the worker
92 * readies the fiber at once. The fiber then sees a cancelled result from its IO
93 * call and unwinds, instead of parking on an op the wind-down would wait out.
94 */
95static bool reactor_submit_commit(srn_fiber_t *fiber, void *arg) {
96 UNUSED(fiber);
97 submit_ctx_t *s = (submit_ctx_t *)arg;
98
100 FIBER_TRACEPOINT(fiber_io_canceled, s->channel, (int)s->request.op);
101 s->waiter->maybe_error = srn_reactor_errors_static(CANCELED);
102 return false; // decline the park; the worker readies us with the error
103 }
104
105 if (!srn_reactor_submit(s->reactor, s->channel, &s->request)) {
106 // The channel is at its in-flight cap. Fail the op back to the fiber; the
107 // condition is transient, so the fiber may retry once the channel drains.
108 FIBER_TRACEPOINT(fiber_io_busy, s->channel, (int)s->request.op);
110 return false;
111 }
112 return true; // stay parked; the worker wakes us on the completion
113}
114
115/**
116 * Runs on the worker loop who owns the channel. Drains this worker's channel
117 * and, for each completion, copies the outcome into the waiting fiber's waiter,
118 * readies it, and only then drops the inflight count, so quiescence never fires
119 * in the gap between a result being produced and its fiber being made runnable.
120 * `srn_fiber_ready` here pushes onto the running worker's own deque, keeping the
121 * woken fiber local.
122 */
123void srn_reactor_consume(srn_reactor_t *reactor, size_t channel) {
124 // The drain batch is the configured reap batch -- "most completions a worker
125 // drains from its channel in a single pass", which is exactly this loop.
126 size_t batch = reactor->ctx->engine->config.reactor.reap_batch;
127 srn_reactor_io_completion_t comps[batch];
128 size_t n;
129
130 while ((n = srn_reactor_reap(reactor, channel, comps, batch)) > 0) {
131 for (size_t i = 0; i < n; i++) {
132 io_waiter_t *w = (io_waiter_t *)comps[i].fiber_data;
133 w->maybe_error = comps[i].maybe_error;
134 w->result = comps[i].result;
135 w->addrlen = comps[i].addrlen;
136 // Emit before readying: once srn_fiber_ready runs, the woken fiber may
137 // resume on another worker and unwind the stack that `w` points into, so
138 // `w->fiber` is only safe to read here. `comps[i]` is a local copy and
139 // stays valid regardless. Pairs with fiber_io_wait on the fiber pointer.
141 fiber_io_complete, channel, comps[i].result, comps[i].maybe_error != nullptr,
142 (void *)w->fiber
143 );
145 srn_reactor_op_done(reactor, channel);
146 }
147 FIBER_TRACEPOINT(fiber_io_drain, channel, n);
148 }
149}
150
151/**
152 * Whether `fd` must be served by a synchronous syscall on the calling worker
153 * rather than the reactor. A readiness backend (e.g. epoll, kqueue) cannot wait
154 * on a regular file, block device, or directory -- they are never "not ready"
155 * -- so a backend that declares SRN_REACTOR_BACKEND_FEAT_FILE_OFFLOAD has those
156 * fds served inline here instead. A backend without the flag (io_uring) handles
157 * every fd type itself, so this is always false for it. A failed fstat (such as
158 * a bad descriptor) returns false, leaving the reactor path to report the
159 * error.
160 */
161static bool fd_needs_blocking(srn_reactor_t *reactor, int fd) {
162#ifdef __linux__
163 assert(reactor != nullptr);
165 return false;
166 }
167
168 struct stat st;
169 if (fstat(fd, &st) != 0) {
170 return false;
171 }
172 return S_ISREG(st.st_mode) || S_ISBLK(st.st_mode) || S_ISDIR(st.st_mode);
173#else
174# error "You need to patch fd_needs_blocking for this platform"
175#endif
176}
177
178/**
179 * Perform a READ or WRITE synchronously on the calling worker and return its outcome. This blocks
180 * the worker os thread for the duration of the syscall; with a single worker it blocks the whole
181 * pool. An `offset` of -1 reads or writes at the descriptor's current position. This is the one
182 * place this layer maps a raw errno to an error tag, since the syscall runs here rather than in a
183 * backend.
184 */
186#ifdef __linux__
187 assert(req != nullptr);
188 ssize_t n;
189
190 if (req->op == SRN_REACTOR_IO_READ) {
191 n = req->offset < 0 ? read(req->fd, req->buf, req->len)
192 : pread(req->fd, req->buf, req->len, (off_t)req->offset);
193 } else {
194 n = req->offset < 0 ? write(req->fd, req->buf, req->len)
195 : pwrite(req->fd, req->buf, req->len, (off_t)req->offset);
196 }
197
198 io_waiter_t w = {.fiber = nullptr, .maybe_error = nullptr, .result = 0, .addrlen = 0};
199 if (n < 0) {
201 w.maybe_error = srn_reactor_errors_static(err_tag);
202 } else {
203 w.result = (size_t)n;
204 }
205 return w;
206#else
207# error "You need to patch blocking_io for this platform"
208#endif
209}
210
211/**
212 * The general submit path, build the request on the calling fiber's stack,
213 * submit it as the fiber parks, and return the waiter once the worker consumes
214 * the completion and wakes it. Each public wrapper projects the returned waiter
215 * into its own result type.
216 */
219 PANIC_IF_NULL(self);
220
221 srn_reactor_t *reactor = self->ctx->engine->reactor;
222
223 // A READ or WRITE on an fd the backend cannot poll (a regular file on a
224 // readiness backend) runs synchronously on this worker. It never reaches the
225 // reactor, so it neither suspends nor touches the in-flight count -- the
226 // worker is simply busy in the syscall, like a compute-bound fiber.
227 if (
228 (request.op == SRN_REACTOR_IO_READ || request.op == SRN_REACTOR_IO_WRITE) &&
229 fd_needs_blocking(reactor, request.fd)
230 ) {
231 FIBER_TRACEPOINT(fiber_io_blocking, request.fd, (int)request.op);
232 return blocking_io(&request);
233 }
234
236 PANIC_IF(wid == (srn_worker_id_t)-1, "reactor IO requested outside a worker");
237
238 // The fiber-side start of a reactor op, keyed on the fiber pointer. Pairs
239 // with fiber_io_complete in srn_reactor_consume to measure the fiber's
240 // perceived IO latency, which spans the suspend, the reactor round trip, and
241 // the wake, not just the reactor thread's portion.
242 FIBER_TRACEPOINT(fiber_io_wait, (int)request.op, request.fd, (void *)self);
243
244 // The waiter and the submit context live on this fiber's stack; the fiber
245 // stays parked (its stack preserved) until the completion comes back, so both
246 // outlive the suspend.
247 io_waiter_t waiter = {.fiber = self, .maybe_error = nullptr, .result = 0, .addrlen = 0};
248 request.fiber_data = &waiter;
249
250 submit_ctx_t s = {
251 .reactor = reactor,
252 .scheduler = self->ctx->engine->scheduler,
253 .waiter = &waiter,
254 .channel = (size_t)wid,
255 .request = request,
256 };
258 return waiter;
259}
260
262 if (e == nullptr || e->tag != CHANNEL_BUSY) {
263 return false;
264 }
265 FIBER_TRACEPOINT(fiber_io_retry, (void *)srn_fiber_current());
267 return true;
268}
269
270srn_error_t *srn_fiber_sleep_ns(uint64_t duration_ns) {
271 uint64_t now = srn_now_ns();
272
273 // Saturate instead of wrapping, an absurd duration means "sleep past any
274 // horizon", not "wake at once".
275 uint64_t deadline = duration_ns > UINT64_MAX - now ? UINT64_MAX : now + duration_ns;
276
279 .deadline_ns = deadline,
280 };
281 return io_wait(req).maybe_error;
282}
283
284/**
285 * Convert a coarser duration to nanoseconds, saturating instead of wrapping.
286 * A wrapped product would turn an absurdly long sleep into a short one,
287 * srn_fiber_sleep_ns saturates its deadline for the same reason.
288 */
289static uint64_t to_ns(uint64_t duration, uint64_t ns_per_unit) {
290 return duration > UINT64_MAX / ns_per_unit ? UINT64_MAX : duration * ns_per_unit;
291}
292
293srn_error_t *srn_fiber_sleep_us(uint64_t duration_us) {
294 return srn_fiber_sleep_ns(to_ns(duration_us, MICROSEC));
295}
296
297srn_error_t *srn_fiber_sleep_ms(uint64_t duration_ms) {
298 return srn_fiber_sleep_ns(to_ns(duration_ms, MILLISEC));
299}
300
301srn_error_t *srn_fiber_sleep(uint64_t duration) { return srn_fiber_sleep_ns(to_ns(duration, SEC)); }
302
303srn_io_size_result_t srn_fiber_read(int fd, void *buf, size_t len, int64_t offset) {
306 .fd = fd,
307 .buf = buf,
308 .len = len,
309 .offset = offset,
310 };
311 io_waiter_t w = io_wait(req);
312 return (srn_io_size_result_t){.maybe_error = w.maybe_error, .count = w.result};
313}
314
315srn_io_size_result_t srn_fiber_write(int fd, const void *buf, size_t len, int64_t offset) {
318 .fd = fd,
319 .buf = (void *)buf,
320 .len = len,
321 .offset = offset,
322 };
323 io_waiter_t w = io_wait(req);
324 return (srn_io_size_result_t){.maybe_error = w.maybe_error, .count = w.result};
325}
326
327srn_io_accept_result_t srn_fiber_accept(int fd, struct sockaddr *addr, socklen_t addr_cap) {
328#ifdef __linux__
329 // Force the accepted socket non-blocking and close-on-exec, so further fiber
330 // IO on it never blocks the reactor thread and it does not leak across exec.
333 .fd = fd,
334 .addr = addr,
335 .addrlen = (uint32_t)addr_cap,
336 .flags = SOCK_NONBLOCK | SOCK_CLOEXEC,
337 };
338 io_waiter_t w = io_wait(req);
339 return (srn_io_accept_result_t){
340 .maybe_error = w.maybe_error,
341 .fd = (int)w.result,
342 .addrlen = (socklen_t)w.addrlen,
343 };
344#else
345# error "You need to patch srn_fiber_accept for this platform"
346#endif
347}
348
349srn_error_t *srn_fiber_connect(int fd, const struct sockaddr *addr, socklen_t addrlen) {
352 .fd = fd,
353 .addr = (void *)addr,
354 .addrlen = (uint32_t)addrlen,
355 };
356 return io_wait(req).maybe_error;
357}
358
360 int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t addr_cap
361) {
364 .fd = fd,
365 .buf = buf,
366 .len = len,
367 .flags = flags,
368 .addr = addr,
369 .addrlen = (uint32_t)addr_cap,
370 };
371 io_waiter_t w = io_wait(req);
373 .maybe_error = w.maybe_error,
374 .count = w.result,
375 .addrlen = (socklen_t)w.addrlen,
376 };
377}
378
380 int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen
381) {
384 .fd = fd,
385 .buf = (void *)buf,
386 .len = len,
387 .flags = flags,
388 .addr = (void *)addr,
389 .addrlen = (uint32_t)addrlen,
390 };
391 io_waiter_t w = io_wait(req);
392 return (srn_io_size_result_t){.maybe_error = w.maybe_error, .count = w.result};
393}
394
395srn_io_size_result_t srn_fiber_recvmsg(int fd, struct msghdr *msg, int flags) {
398 .fd = fd,
399 .buf = msg,
400 .flags = flags,
401 };
402 io_waiter_t w = io_wait(req);
403 return (srn_io_size_result_t){.maybe_error = w.maybe_error, .count = w.result};
404}
405
406srn_io_size_result_t srn_fiber_sendmsg(int fd, const struct msghdr *msg, int flags) {
409 .fd = fd,
410 .buf = (void *)msg,
411 .flags = flags,
412 };
413 io_waiter_t w = io_wait(req);
414 return (srn_io_size_result_t){.maybe_error = w.maybe_error, .count = w.result};
415}
416
417// ---------------------------------------------------------------------------
418// Non-suspending helpers. Thin wrappers over the plain syscalls, they map a -1
419// return to the same static IO error the reactor uses, so callers see one
420// uniform convention. No reactor, no suspend, callable from any context.
421// ---------------------------------------------------------------------------
422
423// Turn a 0/-1 syscall return into null or error.
424static srn_error_t *io_status(int rc) {
425#ifdef __linux__
426 return rc < 0 ? srn_reactor_errors_static(srn_errno_to_tag(errno, UNKNOWN_IO_ERROR)) : nullptr;
427#else
428# error "You need to patch io_status for this platform"
429#endif
430}
431
432// Turn an fd-or-(-1) syscall return into an fd result.
433static srn_io_fd_result_t io_fd(int fd) {
434#ifdef __linux__
435 if (fd < 0) {
436 return (srn_io_fd_result_t){
438 };
439 }
440 return (srn_io_fd_result_t){.fd = fd};
441#else
442# error "You need to patch io_fd for this platform"
443#endif
444}
445
446srn_io_fd_result_t srn_fiber_socket(int domain, int type, int protocol) {
447#ifdef __linux__
448 return io_fd(socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol));
449#else
450# error "You need to patch srn_fiber_socket for this platform"
451#endif
452}
453
454srn_error_t *srn_fiber_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
455#ifdef __linux__
456 return io_status(bind(fd, addr, addrlen));
457#else
458# error "You need to patch srn_fiber_bind for this platform"
459#endif
460}
461
462srn_error_t *srn_fiber_listen(int fd, int backlog) {
463#ifdef __linux__
464 return io_status(listen(fd, backlog));
465#else
466# error "You need to patch srn_fiber_listen for this platform"
467#endif
468}
469
471#ifdef __linux__
472 return io_status(close(fd));
473#else
474# error "You need to patch srn_fiber_close for this platform"
475#endif
476}
477
479#ifdef __linux__
480 return io_status(shutdown(fd, how));
481#else
482# error "You need to patch srn_fiber_shutdown for this platform"
483#endif
484}
485
486srn_error_t *srn_fiber_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen) {
487#ifdef __linux__
488 return io_status(getsockopt(fd, level, optname, optval, optlen));
489#else
490# error "You need to patch srn_fiber_getsockopt for this platform"
491#endif
492}
493
495srn_fiber_setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) {
496#ifdef __linux__
497 return io_status(setsockopt(fd, level, optname, optval, optlen));
498#else
499# error "You need to patch srn_fiber_setsockopt for this platform"
500#endif
501}
502
503srn_io_fd_result_t srn_fiber_open(const char *path, int flags, mode_t mode) {
504#ifdef __linux__
505 // O_NONBLOCK keeps the reactor thread safe, a pollable descriptor (FIFO,
506 // character device) opened here must never make the reactor block in a
507 // transfer. Regular files ignore the flag and take the synchronous worker
508 // path anyway.
509 return io_fd(open(path, flags | O_NONBLOCK | O_CLOEXEC, mode));
510#else
511# error "You need to patch srn_fiber_open for this platform"
512#endif
513}
static srn_fiber_result_t waiter(srn_context_t *ctx, void *arg)
Definition 03_wait_for.c:51
int n
Definition acutest.h:525
Internal reactor backend interface.
#define SRN_REACTOR_BACKEND_FEAT_FILE_OFFLOAD
The backend cannot wait on regular file readiness, so the core serves regular file READ and WRITE off...
Definition backend.h:49
Error handling for the runtime.
srn_error_tag_t
The kind of a failure.
Definition errors.h:75
@ CHANNEL_BUSY
The channel already has a full ring's worth of operations in flight.
Definition errors.h:117
@ UNKNOWN_IO_ERROR
Definition errors.h:118
@ CANCELED
Definition errors.h:97
srn_error_tag_t srn_errno_to_tag(int errno_value, srn_error_tag_t default_tag)
Map a POSIX errno to error tag.
AI Generated (🤦) Fiber subsystem overview.
size_t srn_worker_id_t
Definition fiber.h:153
#define FIBER_TRACEPOINT(...)
Definition fiber.h:146
srn_error_t * srn_reactor_errors_static(srn_error_tag_t tag)
Return the shared, immutable error for an IO failure tag.
Definition reactor.c:125
srn_io_size_result_t srn_fiber_recvmsg(int fd, struct msghdr *msg, int flags)
Receive into the struct msghdr msg on fd with flags, suspending the calling fiber until data arrives.
Definition io.c:395
void srn_reactor_consume(srn_reactor_t *reactor, size_t channel)
Runs on the worker loop who owns the channel.
Definition io.c:123
srn_io_size_result_t srn_fiber_sendmsg(int fd, const struct msghdr *msg, int flags)
Send the struct msghdr msg on fd with flags, suspending the calling fiber until the send completes.
Definition io.c:406
#define MILLISEC
Definition io.c:57
srn_error_t * srn_fiber_listen(int fd, int backlog)
Mark fd as a listening socket with the given backlog, like listen(2).
Definition io.c:462
srn_error_t * srn_fiber_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen)
Read a socket option into optval/optlen, like getsockopt(2).
Definition io.c:486
#define MICROSEC
Definition io.c:56
static io_waiter_t blocking_io(const srn_reactor_io_request_t *req)
Perform a READ or WRITE synchronously on the calling worker and return its outcome.
Definition io.c:185
srn_error_t * srn_fiber_close(int fd)
Close fd, like close(2).
Definition io.c:470
srn_error_t * srn_fiber_sleep_us(uint64_t duration_us)
Suspend the calling fiber for at least duration_us microseconds, letting its worker run other fibers ...
Definition io.c:293
static bool reactor_submit_commit(srn_fiber_t *fiber, void *arg)
Runs on the worker's loop once the fiber has switched out – the race free point to hand the request t...
Definition io.c:95
static bool fd_needs_blocking(srn_reactor_t *reactor, int fd)
Whether fd must be served by a synchronous syscall on the calling worker rather than the reactor.
Definition io.c:161
srn_error_t * srn_fiber_setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen)
Set a socket option from optval/optlen, like setsockopt(2).
Definition io.c:495
#define SEC
Definition io.c:58
srn_io_size_result_t srn_fiber_write(int fd, const void *buf, size_t len, int64_t offset)
Write len bytes from buf to fd, suspending the calling fiber until the operation completes rather tha...
Definition io.c:315
srn_error_t * srn_fiber_sleep_ms(uint64_t duration_ms)
Suspend the calling fiber for at least duration_ms miliseconds, letting its worker run other fibers m...
Definition io.c:297
static srn_error_t * io_status(int rc)
Definition io.c:424
srn_io_recvfrom_result_t srn_fiber_recvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t addr_cap)
Receive a datagram from fd into buf/len with flags (the MSG_* flags), suspending the calling fiber un...
Definition io.c:359
srn_error_t * srn_fiber_connect(int fd, const struct sockaddr *addr, socklen_t addrlen)
Connect the socket fd to addr/addrlen, suspending the calling fiber until the connection completes ra...
Definition io.c:349
static io_waiter_t io_wait(srn_reactor_io_request_t request)
The general submit path, build the request on the calling fiber's stack, submit it as the fiber parks...
Definition io.c:217
srn_io_fd_result_t srn_fiber_socket(int domain, int type, int protocol)
Create a socket, like socket(2), additionally forcing SOCK_NONBLOCK and SOCK_CLOEXEC so the descripto...
Definition io.c:446
static srn_io_fd_result_t io_fd(int fd)
Definition io.c:433
srn_io_size_result_t srn_fiber_sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen)
Send buf/len on fd to addr/addrlen with flags (the MSG_* flags), suspending the calling fiber until t...
Definition io.c:379
bool srn_fiber_io_retry(srn_error_t *e)
Whether a suspending IO call should be retried, true exactly when e is the transient CHANNEL_BUSY err...
Definition io.c:261
srn_error_t * srn_fiber_bind(int fd, const struct sockaddr *addr, socklen_t addrlen)
Bind fd to addr/addrlen, like bind(2).
Definition io.c:454
srn_io_accept_result_t srn_fiber_accept(int fd, struct sockaddr *addr, socklen_t addr_cap)
Accept a pending connection on the listening socket fd, suspending the calling fiber until one arrive...
Definition io.c:327
srn_error_t * srn_fiber_shutdown(int fd, int how)
Shut down part or all of the connection on fd (how is SHUT_RD/SHUT_WR/ SHUT_RDWR),...
Definition io.c:478
srn_error_t * srn_fiber_sleep_ns(uint64_t duration_ns)
Suspend the calling fiber for at least duration_ns nanoseconds, letting its worker run other fibers m...
Definition io.c:270
srn_error_t * srn_fiber_sleep(uint64_t duration)
Suspend the calling fiber for at least duration seconds, letting its worker run other fibers meanwhil...
Definition io.c:301
srn_io_fd_result_t srn_fiber_open(const char *path, int flags, mode_t mode)
Open path, like open(2), additionally forcing O_NONBLOCK and O_CLOEXEC so the descriptor is always sa...
Definition io.c:503
static uint64_t to_ns(uint64_t duration, uint64_t ns_per_unit)
Convert a coarser duration to nanoseconds, saturating instead of wrapping.
Definition io.c:289
srn_io_size_result_t srn_fiber_read(int fd, void *buf, size_t len, int64_t offset)
Read up to len bytes from fd into buf, suspending the calling fiber until the operation completes rat...
Definition io.c:303
The fiber facing IO API: blocking looking calls a fiber uses to do IO.
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
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_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
Reactor overview.
@ 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_CONNECT
Connect fd to the address in addr/addrlen.
Definition reactor.h:86
@ 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_SENDMSG
Send the struct msghdr that buf points at, on fd, with flags.
Definition reactor.h:103
void srn_fiber_ready(srn_fiber_t *fiber)
Mark a suspended fiber runnable again, waking it when the event it awaited occurs.
Definition scheduler.c:1083
srn_worker_id_t srn_sched_current_worker_id()
Return the id of the worker that the calling os thread is running, or SIZE_MAX when the calling threa...
Definition scheduler.c:1157
bool srn_sched_accepting_submissions(srn_scheduler_t *sched)
Whether the scheduler still accepts new IO submissions.
Definition scheduler.c:1163
srn_fiber_t * srn_fiber_current(void)
The fiber currently running on this os thread, or null when the calling thread is not a worker or the...
Definition scheduler.c:1097
void srn_fiber_suspend(srn_fiber_park_fn commit, void *arg)
A suspended fiber is on no scheduler queue, and the scheduler does not track what it waits on – whoev...
Definition scheduler.c:1063
void srn_fiber_yield(void)
Yield cooperatively, re-enqueue the running fiber and run the next ready one.
Definition scheduler.c:1039
Lives on the suspended fiber's stack; the worker fills it from the completion and the fiber reads it ...
Definition io.c:66
uint32_t addrlen
Definition io.c:70
size_t result
Definition io.c:69
ERROR_HEADER
Definition io.c:67
srn_fiber_t * fiber
Definition io.c:68
srn_reactor_config_t reactor
srn_engine_t * engine
Long term state of the compiler.
Definition context.h:49
srn_configuration_t config
The runtime's tunable knobs, the single source for every configurable value (see configuration....
Definition engine.h:62
srn_scheduler_t * scheduler
The fiber scheduler, that is the entry point of the fiber subsystem.
Definition engine.h:75
srn_reactor_t * reactor
The I/O reactor, that is in charge of handling everything I/O.
Definition engine.h:78
A runtime error, a tag classifying the failure and a human-readable message.
Definition errors.h:125
srn_error_tag_t tag
Definition errors.h:126
srn_fiber_result_t result
Set when state reaches SRN_FIBER_DONE.
Definition fiber.h:283
srn_context_t * ctx
Definition fiber.h:278
The result of srn_fiber_accept.
Definition io.h:86
The result of an op that yields a descriptor (srn_fiber_socket, srn_fiber_open).
Definition io.h:66
The result of srn_fiber_recvfrom.
Definition io.h:97
The result of a byte transfer op (read/write/sendto/recvmsg/sendmsg).
Definition io.h:76
uint32_t features
Bitmap of SRN_REACTOR_BACKEND_FEAT_* flags this backend requires from the reactor.
Definition backend.h:61
size_t reap_batch
Most completions a worker drains from its channel in a single pass.
A completion, the result of one submission, echoed back on the channel.
Definition reactor.h:159
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
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
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
srn_context_t * ctx
Definition internal.h:44
const srn_reactor_backend_t * backend
The chosen kernel mechanism and its private state, set at activation.
Definition internal.h:47
Carries the submission across the suspend, on the fiber's stack.
Definition io.c:76
srn_reactor_t * reactor
Definition io.c:77
size_t channel
Definition io.c:80
io_waiter_t * waiter
Definition io.c:79
srn_reactor_io_request_t request
Definition io.c:81
srn_scheduler_t * scheduler
Definition io.c:78
#define PANIC_IF_NULL(ptr)
Definition utils.h:66
uint64_t srn_now_ns(void)
The current time on the monotonic clock, in nanoseconds.
Definition utils.c:46
#define PANIC_IF(cond, msg)
Definition utils.h:59
#define UNUSED(x)
Definition utils.h:45