Some helpers in src/basic — ppoll_usec_full() (used by fd_wait_for_event()), loop_read(), loop_read_exact(), loop_write_full() and pidref_wait_for_terminate_full() — block the calling thread. That's the right behaviour outside a fiber but not inside one, where blocking the thread also stalls every other fiber running on the same event loop. Rewriting every caller to pick a fiber or non-fiber variant explicitly would be a lot of churn and would split otherwise-shared code paths in two. Instead, the helpers detect at runtime whether they're running on a fiber and dispatch to a suspending variant when they are. FiberOps in fiber-ops.h holds five function pointers (ppoll, read, write, timeout, cancel_wait_unref); a fiber_ops global constant is populated whenever we enter a fiber with functions that delegate to suspending variants of common syscalls. With this approach, the variants themselves stay in libsystemd which is required because they make use of sd-event. - loop_read()/loop_read_exact() take the fiber read hook on a fiber unless the caller asked for a non-blocking attempt (do_poll=false) and the fd is already non-blocking — in that case we fall through to read() to preserve the existing return-EAGAIN-immediately semantic. The hook itself suspends on EAGAIN until data is available, so neither the do_poll knob nor the explicit fd_wait_for_event() retry loop are needed on the fiber path. - loop_write_full() likewise takes the fiber write hook on a fiber, except when timeout=0 with an already-non-blocking fd (preserving the fast-return-EAGAIN semantic). The fiber path runs inside a FIBER_OPS_TIMEOUT() scope so the caller's timeout is honoured via a deadline future, mirroring SD_FIBER_TIMEOUT() but reachable from src/basic without pulling in sd-future.h. - pidref_wait_for_terminate_full() polls the pidfd via fd_wait_for_event() before each waitid() when either a finite timeout is set or we're on a fiber, and requires pidref->fd >= 0 in those cases (returning -ENOMEDIUM otherwise — extending the rule that already applied to finite timeouts). The poll suspends the fiber via the ppoll hook above; the subsequent waitid() doesn't block because the pidfd is already signalled.
35 lines
1.7 KiB
C
35 lines
1.7 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
#pragma once
|
|
|
|
#include "basic-forward.h"
|
|
|
|
typedef struct sd_future sd_future;
|
|
|
|
/* Hooks installed on a fiber so that functions in src/basic can transparently defer to the suspending
|
|
* variants in sd-future when invoked from a running fiber. Populated by sd_fiber_new() with pointers to the
|
|
* implementations in fiber-ops.c. */
|
|
typedef struct FiberOps {
|
|
int (*ppoll)(struct pollfd *fds, size_t n_fds, const struct timespec *timeout, const sigset_t *sigmask);
|
|
ssize_t (*read)(int fd, void *buf, size_t count);
|
|
ssize_t (*write)(int fd, const void *buf, size_t count);
|
|
sd_future* (*timeout)(uint64_t timeout);
|
|
sd_future* (*cancel_wait_unref)(sd_future *f);
|
|
} FiberOps;
|
|
|
|
bool fiber_ops_is_set(void);
|
|
void fiber_ops_set(const FiberOps *fiber_ops);
|
|
|
|
int fiber_ops_ppoll(struct pollfd *fds, size_t n_fds, const struct timespec *timeout, const sigset_t *sigmask);
|
|
ssize_t fiber_ops_read(int fd, void *buf, size_t count);
|
|
ssize_t fiber_ops_write(int fd, const void *buf, size_t count);
|
|
|
|
/* Mirror of SD_FIBER_TIMEOUT() for code under src/basic that doesn't include sd-future.h: dispatches
|
|
* through FiberOps so the actual sd_fiber_timeout() implementation lives in libsystemd. */
|
|
sd_future* fiber_ops_timeout(uint64_t timeout);
|
|
sd_future* fiber_ops_cancel_wait_unref(sd_future *f);
|
|
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_future*, fiber_ops_cancel_wait_unref);
|
|
|
|
#define FIBER_OPS_TIMEOUT(timeout) _FIBER_OPS_TIMEOUT(UNIQ, (timeout))
|
|
#define _FIBER_OPS_TIMEOUT(uniq, timeout) \
|
|
_unused_ _cleanup_(fiber_ops_cancel_wait_unrefp) sd_future *UNIQ_T(_fot_, uniq) = fiber_ops_timeout(timeout)
|