49# include <sys/socket.h>
53# error "io.c needs to be patched to support this platform"
56#define MICROSEC 1000ULL
57#define MILLISEC 1000ULL * MICROSEC
58#define SEC 1000ULL * MILLISEC
129 for (
size_t i = 0; i <
n; i++) {
131 w->maybe_error = comps[i].maybe_error;
152 assert(reactor !=
nullptr);
158 if (fstat(fd, &st) != 0) {
161 return S_ISREG(st.st_mode) || S_ISBLK(st.st_mode) || S_ISDIR(st.st_mode);
163# error "You need to patch fd_needs_blocking for this platform"
176 assert(req !=
nullptr);
187 io_waiter_t w = {.fiber =
nullptr, .maybe_error =
nullptr, .result = 0, .addrlen = 0};
196# error "You need to patch blocking_io for this platform"
236 .channel = (size_t)wid,
256 uint64_t deadline = duration_ns > UINT64_MAX - now ? UINT64_MAX : now + duration_ns;
260 .deadline_ns = deadline,
262 return io_wait(req).maybe_error;
270static uint64_t
to_ns(uint64_t duration, uint64_t ns_per_unit) {
271 return duration > UINT64_MAX / ns_per_unit ? UINT64_MAX : duration * ns_per_unit;
316 .addrlen = (uint32_t)addr_cap,
317 .flags = SOCK_NONBLOCK | SOCK_CLOEXEC,
321 .maybe_error = w.maybe_error,
323 .addrlen = (socklen_t)w.
addrlen,
326# error "You need to patch srn_fiber_accept for this platform"
334 .addr = (
void *)addr,
335 .addrlen = (uint32_t)addrlen,
337 return io_wait(req).maybe_error;
341 int fd,
void *buf,
size_t len,
int flags,
struct sockaddr *addr, socklen_t addr_cap
350 .addrlen = (uint32_t)addr_cap,
354 .maybe_error = w.maybe_error,
356 .addrlen = (socklen_t)w.
addrlen,
361 int fd,
const void *buf,
size_t len,
int flags,
const struct sockaddr *addr, socklen_t addrlen
369 .addr = (
void *)addr,
370 .addrlen = (uint32_t)addrlen,
409# error "You need to patch io_status for this platform"
423# error "You need to patch io_fd for this platform"
429 return io_fd(socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol));
431# error "You need to patch srn_fiber_socket for this platform"
437 return io_status(bind(fd, addr, addrlen));
439# error "You need to patch srn_fiber_bind for this platform"
447# error "You need to patch srn_fiber_listen for this platform"
455# error "You need to patch srn_fiber_close for this platform"
463# error "You need to patch srn_fiber_shutdown for this platform"
469 return io_status(getsockopt(fd, level, optname, optval, optlen));
471# error "You need to patch srn_fiber_getsockopt for this platform"
478 return io_status(setsockopt(fd, level, optname, optval, optlen));
480# error "You need to patch srn_fiber_setsockopt for this platform"
490 return io_fd(open(path, flags | O_NONBLOCK | O_CLOEXEC, mode));
492# error "You need to patch srn_fiber_open for this platform"
static srn_fiber_result_t waiter(srn_context_t *ctx, void *arg)
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...
Error handling for the runtime.
srn_error_tag_t
The kind of a failure.
@ CHANNEL_BUSY
The channel already has a full ring's worth of operations in flight.
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.
srn_error_t * srn_reactor_errors_static(srn_error_tag_t tag)
Return the shared, immutable error for an IO failure tag.
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.
void srn_reactor_consume(srn_reactor_t *reactor, size_t channel)
Runs on the worker loop who owns the channel.
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.
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_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen)
Read a socket option into optval/optlen, like getsockopt(2).
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.
srn_error_t * srn_fiber_close(int fd)
Close fd, like close(2).
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 ...
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...
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.
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_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...
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...
static srn_error_t * io_status(int rc)
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...
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...
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...
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...
static srn_io_fd_result_t io_fd(int fd)
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...
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...
srn_error_t * srn_fiber_bind(int fd, const struct sockaddr *addr, socklen_t addrlen)
Bind fd to addr/addrlen, like bind(2).
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...
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),...
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...
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...
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...
static uint64_t to_ns(uint64_t duration, uint64_t ns_per_unit)
Convert a coarser duration to nanoseconds, saturating instead of wrapping.
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...
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.
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.
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.
@ SRN_REACTOR_IO_SLEEP
Wait out a deadline.
@ SRN_REACTOR_IO_ACCEPT
Accept a pending connection on the listening socket fd, with flags (the accept4 flags).
@ SRN_REACTOR_IO_READ
Read from a descriptor into buf. Uses fd, buf, len, offset.
@ SRN_REACTOR_IO_SENDTO
Send buf/len on fd to the address in addr/addrlen with flags.
@ SRN_REACTOR_IO_CONNECT
Connect fd to the address in addr/addrlen.
@ SRN_REACTOR_IO_RECVFROM
Receive a datagram from fd into buf/len with flags.
@ SRN_REACTOR_IO_RECVMSG
Receive into the struct msghdr that buf points at, on fd, with flags (scatter/gather and ancillary da...
@ SRN_REACTOR_IO_WRITE
Write buf to a descriptor. Uses fd, buf, len, offset.
@ SRN_REACTOR_IO_SENDMSG
Send the struct msghdr that buf points at, on fd, with flags.
void srn_fiber_ready(srn_fiber_t *fiber)
Mark a suspended fiber runnable again, waking it when the event it awaited occurs.
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...
bool srn_sched_accepting_submissions(srn_scheduler_t *sched)
Whether the scheduler still accepts new IO submissions.
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...
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...
void srn_fiber_yield(void)
Yield cooperatively, re-enqueue the running fiber and run the next ready one.
Lives on the suspended fiber's stack; the worker fills it from the completion and the fiber reads it ...
srn_reactor_config_t reactor
srn_engine_t * engine
Long term state of the compiler.
srn_configuration_t config
The runtime's tunable knobs, the single source for every configurable value (see configuration....
srn_scheduler_t * scheduler
The fiber scheduler, that is the entry point of the fiber subsystem.
srn_reactor_t * reactor
The I/O reactor, that is in charge of handling everything I/O.
A runtime error, a tag classifying the failure and a human-readable message.
srn_fiber_result_t result
Set when state reaches SRN_FIBER_DONE.
The result of srn_fiber_accept.
The result of an op that yields a descriptor (srn_fiber_socket, srn_fiber_open).
The result of srn_fiber_recvfrom.
The result of a byte transfer op (read/write/sendto/recvmsg/sendmsg).
uint32_t features
Bitmap of SRN_REACTOR_BACKEND_FEAT_* flags this backend requires from the reactor.
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.
uint32_t addrlen
For SRN_REACTOR_IO_ACCEPT and SRN_REACTOR_IO_RECVFROM, the actual length of the address written into ...
size_t result
The result of the operation on success, bytes transferred for the transfers, the accepted descriptor ...
A submission, one request a fiber places on its channel.
int fd
The descriptor the operation targets.
void * buf
The transfer buffer for READ, WRITE, RECVFROM, and SENDTO, and the struct msghdr * for RECVMSG and SE...
size_t len
The transfer length, in bytes, for READ, WRITE, RECVFROM, and SENDTO.
int64_t offset
The file offset for SRN_REACTOR_IO_READ and SRN_REACTOR_IO_WRITE; -1 reads at the current position.
void * fiber_data
Opaque token, round tripped to the matching completion.
const srn_reactor_backend_t * backend
The chosen kernel mechanism and its private state, set at activation.
Carries the submission across the suspend, on the fiber's stack.
srn_reactor_io_request_t request
srn_scheduler_t * scheduler
#define PANIC_IF_NULL(ptr)
uint64_t srn_now_ns(void)
The current time on the monotonic clock, in nanoseconds.
#define PANIC_IF(cond, msg)