sd-future: add fiber-aware non-blocking I/O wrappers

Add a family of sd_fiber_*() I/O wrappers that, when called from a
fiber, behave like blocking I/O from the caller's perspective but
yield to the event loop instead of blocking the thread:

  sd_fiber_read / sd_fiber_write
  sd_fiber_readv / sd_fiber_writev
  sd_fiber_recv / sd_fiber_send
  sd_fiber_connect
  sd_fiber_recvmsg / sd_fiber_sendmsg
  sd_fiber_recvfrom / sd_fiber_sendto
  sd_fiber_accept
  sd_fiber_ppoll

Most of them share a single helper, fiber_io_operation(), which when
invoked outside a fiber falls through to the underlying syscall
directly, preserving the regular blocking behaviour. Inside a fiber
the helper flips the fd to non-blocking (restoring its original mode
on return), tries the syscall once on the fast path, and on EAGAIN/
EWOULDBLOCK creates an sd-event-backed IO future via future_new_io(),
suspends the fiber, and retries the syscall once the event source
fires.

future_new_io() itself is added to sd-event/event-future.{c,h} as a
new IoFuture kind. It wraps sd_event_add_io() into an sd_future:
oneshot enable, EPOLLERR translated via SO_ERROR (suppressed for
non-sockets), and the fd duplicated with F_DUPFD_CLOEXEC to avoid
EEXIST when multiple sources watch the same descriptor.

Together these let fiber-using code write straight-line socket and
pipe I/O without bundling state into callbacks.
This commit is contained in:
Daan De Meyer
2026-05-21 09:55:04 +00:00
parent 8533513aff
commit cf4c65afa8
8 changed files with 2020 additions and 0 deletions
+9
View File
@@ -3,6 +3,7 @@
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h> /* IWYU pragma: keep */
#include <time.h>
#include <unistd.h>
@@ -10,6 +11,14 @@
#include "io-util.h"
#include "time-util.h"
/* EPOLL_POLL_COMMON_MASK in io-util.h treats POLL* and EPOLL* as interchangeable; verify it. */
assert_cc((uint32_t) POLLIN == EPOLLIN);
assert_cc((uint32_t) POLLOUT == EPOLLOUT);
assert_cc((uint32_t) POLLERR == EPOLLERR);
assert_cc((uint32_t) POLLHUP == EPOLLHUP);
assert_cc((uint32_t) POLLPRI == EPOLLPRI);
assert_cc((uint32_t) POLLRDHUP == EPOLLRDHUP);
int flush_fd(int fd) {
int count = 0;
+6
View File
@@ -3,6 +3,12 @@
#include "basic-forward.h"
/* The intersection of poll() and epoll_wait() event masks. Linux defines POLL* and EPOLL* with the
* same numeric values for these — see the assert_cc()s in io-util.c — so this mask can be used
* interchangeably as a `revents` (poll) or `events` (epoll) bitset. */
#define EPOLL_POLL_COMMON_MASK \
(EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLPRI | EPOLLRDHUP)
int flush_fd(int fd);
ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);