|
Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
|
The fiber side of the reactor: the only layer that knows both fibers and the reactor. More...
#include "serene/rt/fiber/io.h"#include <assert.h>#include "rt/reactor/backend.h"#include "rt/reactor/internal.h"#include "serene/rt/context.h"#include "serene/rt/engine.h"#include "serene/rt/errors.h"#include "serene/rt/fiber.h"#include "serene/rt/reactor.h"#include "serene/utils.h"Go to the source code of this file.
Data Structures | |
| struct | io_waiter_t |
| Lives on the suspended fiber's stack; the worker fills it from the completion and the fiber reads it on resume. More... | |
| struct | submit_ctx_t |
| Carries the submission across the suspend, on the fiber's stack. More... | |
Macros | |
| #define | MICROSEC 1000ULL |
| #define | MILLISEC 1000ULL * MICROSEC |
| #define | SEC 1000ULL * MILLISEC |
Typedefs | |
| typedef struct io_waiter_t | io_waiter_t |
| Lives on the suspended fiber's stack; the worker fills it from the completion and the fiber reads it on resume. | |
| typedef struct submit_ctx_t | submit_ctx_t |
| Carries the submission across the suspend, on the fiber's stack. | |
Functions | |
| 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 to the reactor. | |
| void | srn_reactor_consume (srn_reactor_t *reactor, size_t channel) |
| Runs on the worker loop who owns the channel. | |
| 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. | |
| 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. | |
| 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, and return the waiter once the worker consumes the completion and wakes it. | |
| 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 error, in which case the calling fiber yields first so its worker can consume completions (which is what reopens the channel's admission window) and run other fibers before the retry. | |
| 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 meanwhile. | |
| static uint64_t | to_ns (uint64_t duration, uint64_t ns_per_unit) |
| Convert a coarser duration to nanoseconds, saturating instead of wrapping. | |
| 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 meanwhile. | |
| 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 meanwhile. | |
| srn_error_t * | srn_fiber_sleep (uint64_t duration) |
| Suspend the calling fiber for at least duration seconds, letting its worker run other fibers meanwhile. | |
| 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 rather than blocking its worker. | |
| 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 than blocking its worker. | |
| 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 arrives. | |
| 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 rather than blocking its worker. | |
| 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 until one arrives. | |
| 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 the send completes rather than blocking its worker. | |
| 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. | |
| 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. | |
| static srn_error_t * | io_status (int rc) |
| static srn_io_fd_result_t | io_fd (int fd) |
| 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 descriptor is safe to hand to the suspending calls (a blocking socket would stall the reactor thread). | |
| srn_error_t * | srn_fiber_bind (int fd, const struct sockaddr *addr, socklen_t addrlen) |
| Bind fd to addr/addrlen, like bind(2). | |
| srn_error_t * | srn_fiber_listen (int fd, int backlog) |
| Mark fd as a listening socket with the given backlog, like listen(2). | |
| srn_error_t * | srn_fiber_close (int fd) |
| Close fd, like close(2). | |
| 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), like shutdown(2). | |
| 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). | |
| 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). | |
| 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 safe to hand to the suspending calls above. | |
The fiber side of the reactor: the only layer that knows both fibers and the reactor.
A fiber issues IO through a blocking looking wrapper here. The wrapper builds the request on the fiber's own stack, suspends, and the suspend's commit submits it to the reactor – after the fiber has fully parked, so the completion can never race the park. Each completion carries a pointer to the fiber's on-stack waiter. The owning worker drains its channel (srn_reactor_consume) and, per completion, copies the outcome into the waiter, readies the fiber, and drops the inflight count. The reactor thread only posts completions and wakes the worker; it never touches fibers.
Definition in file io.c.
| typedef struct io_waiter_t io_waiter_t |
Lives on the suspended fiber's stack; the worker fills it from the completion and the fiber reads it on resume.
It mirrors the completion's payload minus the routing token, and is itself a fallible value (maybe_error null on success).
| typedef struct submit_ctx_t submit_ctx_t |
Carries the submission across the suspend, on the fiber's stack.
|
static |
Perform a READ or WRITE synchronously on the calling worker and return its outcome.
This blocks the worker os thread for the duration of the syscall; with a single worker it blocks the whole pool. An offset of -1 reads or writes at the descriptor's current position. This is the one place this layer maps a raw errno to an error tag, since the syscall runs here rather than in a backend.
Definition at line 174 of file io.c.
|
static |
Whether fd must be served by a synchronous syscall on the calling worker rather than the reactor.
A readiness backend (e.g. epoll, kqueue) cannot wait on a regular file, block device, or directory – they are never "not ready" – so a backend that declares SRN_REACTOR_BACKEND_FEAT_FILE_OFFLOAD has those fds served inline here instead. A backend without the flag (io_uring) handles every fd type itself, so this is always false for it. A failed fstat (such as a bad descriptor) returns false, leaving the reactor path to report the error.
Definition at line 150 of file io.c.
|
static |
Definition at line 414 of file io.c.
|
static |
|
static |
The general submit path, build the request on the calling fiber's stack, submit it as the fiber parks, and return the waiter once the worker consumes the completion and wakes it.
Each public wrapper projects the returned waiter into its own result type.
Definition at line 206 of file io.c.
|
static |
Runs on the worker's loop once the fiber has switched out – the race free point to hand the request to the reactor.
Returns true to stay suspended; the worker readies the fiber when it consumes the completion.
If the scheduler is winding down (srn_sched_drain/srn_sched_stop), the submission is fenced, the request is never handed to the reactor, the waiter is set to the CANCELED error, and the commit returns false so the worker readies the fiber at once. The fiber then sees a cancelled result from its IO call and unwinds, instead of parking on an op the wind-down would wait out.
Definition at line 95 of file io.c.
| 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 arrives.
When addr is non-null the peer address is written into it, up to addr_cap bytes, and the result's addrlen reports how many were written. The accepted socket is created non-blocking and close on exec, so it is ready for further fiber IO. On success the result's fd is the accepted descriptor. Must run on a fiber that is on a worker.
Definition at line 308 of file io.c.
| srn_error_t * srn_fiber_bind | ( | int | fd, |
| const struct sockaddr * | addr, | ||
| socklen_t | addrlen ) |
| srn_error_t * srn_fiber_close | ( | int | fd | ) |
| 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 rather than blocking its worker.
Returns null on success, or the error (such as CONNECTION_REFUSED). Must run on a fiber that is on a worker.
Definition at line 330 of file io.c.
| srn_error_t * srn_fiber_getsockopt | ( | int | fd, |
| int | level, | ||
| int | optname, | ||
| void * | optval, | ||
| socklen_t * | optlen ) |
| 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 error, in which case the calling fiber yields first so its worker can consume completions (which is what reopens the channel's admission window) and run other fibers before the retry.
Any other error, and success, return false, so a do/while over this helper (see the file docs) retries precisely the backpressure case and hands every real outcome through. A retrying fiber with no runnable peers and no due completions degenerates to a paced busy loop; if that shows up in practice the upgrade is suspending on the channel until a slot frees, instead of polling.
Definition at line 243 of file io.c.
| srn_error_t * srn_fiber_listen | ( | int | fd, |
| int | backlog ) |
Mark fd as a listening socket with the given backlog, like listen(2).
Returns null on success, or the error.
Definition at line 443 of file io.c.
| 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 safe to hand to the suspending calls above.
mode applies only when flags includes O_CREAT. The open runs synchronously on the calling thread. On success fd is the new descriptor. Regular files ignore O_NONBLOCK (they are served on the worker, not the reactor). FIFOs follow the open(2) O_NONBLOCK semantics: a read end opens at once with no writer present, and a write end with no reader fails instead of blocking until one appears.
Definition at line 484 of file io.c.
| 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 rather than blocking its worker.
offset is the file offset, or -1 to read at the descriptor's current position. On success count is the number of bytes read (0 at end of file).
Definition at line 284 of file io.c.
| 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 until one arrives.
When addr is non-null the sender address is written into it, up to addr_cap bytes, and the result's addrlen reports how many. On success count is the number of bytes received. Must run on a fiber that is on a worker.
Definition at line 340 of file io.c.
| 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.
Scatters across msg's iovecs and fills its msg_name/msg_control as requested. On success count is the number of bytes received. Must run on a fiber that is on a worker.
Definition at line 376 of file io.c.
| 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.
Gathers from msg's iovecs. On success count is the number of bytes sent. Must run on a fiber that is on a worker.
Definition at line 387 of file io.c.
| 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 the send completes rather than blocking its worker.
On success count is the number of bytes sent. Must run on a fiber that is on a worker.
Definition at line 360 of file io.c.
| srn_error_t * srn_fiber_setsockopt | ( | int | fd, |
| int | level, | ||
| int | optname, | ||
| const void * | optval, | ||
| socklen_t | optlen ) |
| 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), like shutdown(2).
Returns null on success, or the error.
Definition at line 459 of file io.c.
| srn_error_t * srn_fiber_sleep | ( | uint64_t | duration | ) |
Suspend the calling fiber for at least duration seconds, letting its worker run other fibers meanwhile.
Returns null on a normal wake, or the CANCELED error if the sleep was cut short by a scheduler wind down.
Definition at line 282 of file io.c.
| 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 meanwhile.
Returns null on a normal wake, or the CANCELED error if the sleep was cut short by a scheduler wind down.
Definition at line 278 of file io.c.
| 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 meanwhile.
Returns null on a normal wake, or the CANCELED error if the sleep was cut short by a scheduler wind down.
Definition at line 251 of file io.c.
| 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 meanwhile.
Returns null on a normal wake, or the CANCELED error if the sleep was cut short by a scheduler wind down.
Definition at line 274 of file io.c.
| 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 descriptor is safe to hand to the suspending calls (a blocking socket would stall the reactor thread).
On success fd is the new descriptor.
Definition at line 427 of file io.c.
| 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 than blocking its worker.
offset is the file offset, or -1 to write at the descriptor's current position. On success count is the number of bytes written.
Definition at line 296 of file io.c.
| void srn_reactor_consume | ( | srn_reactor_t * | reactor, |
| size_t | channel ) |
Runs on the worker loop who owns the channel.
Drain and act on channel's completions.
Drains this worker's channel and, for each completion, copies the outcome into the waiting fiber's waiter, readies it, and only then drops the inflight count, so quiescence never fires in the gap between a result being produced and its fiber being made runnable. srn_fiber_ready here pushes onto the running worker's own deque, keeping the woken fiber local.
Definition at line 121 of file io.c.
|
static |
Convert a coarser duration to nanoseconds, saturating instead of wrapping.
A wrapped product would turn an absurdly long sleep into a short one, srn_fiber_sleep_ns saturates its deadline for the same reason.
Definition at line 270 of file io.c.