libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
xtc_loop.h
1/*-
2 * Copyright (c) 2026, The XTC Project
3 * Use of this source code is governed by the ISC License.
4 *
5 * src/inc/xtc_loop.h
6 * The L2 single-thread event loop, run-queue, timer-heap, and
7 * task state machine. See M3_CLAIMS.md.
8 */
9
10#ifndef XTC_LOOP_H
11#define XTC_LOOP_H
12
13#include "xtc_export.h"
14
15#include <stdint.h>
16
17#include "xtc_io.h"
18
19typedef struct xtc_loop xtc_loop_t;
20typedef struct xtc_task xtc_task_t;
21typedef struct xtc_timer xtc_timer_t;
22
23/*
24 * A task's main function. Called once per "step" by the loop.
25 * Returns one of the XTC_TASK_* codes below to tell the loop what to
26 * do next.
27 * XTC_TASK_DONE The task is finished; the loop reaps it.
28 * XTC_TASK_RESCHED Re-queue me at the back of the run queue.
29 * XTC_TASK_PENDING I am waiting on a waker; do not run me again
30 * until xtc_waker_wake (or a timer / fd that
31 * I parked on) fires.
32 */
33typedef int (*xtc_task_fn)(xtc_task_t *self, void *user);
34
35#define XTC_TASK_DONE 0
36#define XTC_TASK_RESCHED 1
37#define XTC_TASK_PENDING 2
38
39/*
40 * The waker is a value-type cookie (loop, task) used to re-schedule
41 * a parked task. In M3 wakers are valid only on the owning thread;
42 * cross-thread wake arrives in M5.
43 */
44typedef struct xtc_waker {
45 xtc_loop_t *loop;
46 xtc_task_t *task;
48
49/* Timer callback. */
50typedef void (*xtc_timer_fn)(void *user);
51
52/*
53 * PUBLIC: int xtc_loop_init __P((xtc_loop_t **));
54 * PUBLIC: int xtc_loop_fini __P((xtc_loop_t *));
55 * PUBLIC: int xtc_loop_run __P((xtc_loop_t *));
56 * PUBLIC: int xtc_loop_stop __P((xtc_loop_t *));
57 * PUBLIC: int xtc_loop_wake __P((xtc_loop_t *));
58 *
59 * PUBLIC: int xtc_task_spawn __P((xtc_loop_t *, xtc_task_fn, void *, xtc_task_t **));
60 * PUBLIC: int xtc_task_waker __P((xtc_task_t *, xtc_waker_t *));
61 * PUBLIC: int xtc_task_park_on_timer __P((xtc_task_t *, int64_t));
62 * PUBLIC: int xtc_task_park_on_fd __P((xtc_task_t *, int, uint32_t));
63 *
64 * PUBLIC: int xtc_waker_wake __P((const xtc_waker_t *));
65 *
66 * PUBLIC: int xtc_timer_set __P((xtc_loop_t *, int64_t, xtc_timer_fn, void *, xtc_timer_t **));
67 * PUBLIC: int xtc_timer_cancel __P((xtc_timer_t *));
68 */
69XTC_API int xtc_loop_init(xtc_loop_t **out);
70XTC_API int xtc_loop_fini(xtc_loop_t *loop);
71XTC_API int xtc_loop_run(xtc_loop_t *loop);
72XTC_API int xtc_loop_stop(xtc_loop_t *loop);
73
74/*
75 * xtc_loop_wake --
76 * Nudge a loop's poller out of its I/O wait from ANY OS thread, so
77 * it re-polls its registered fds and re-checks runnability. This is
78 * the loop-level companion to xtc_proc_wake(): use it when a foreign
79 * thread has made a condition true for a task parked on THIS loop
80 * (e.g. written a self-pipe / eventfd the loop's poll watches, or set
81 * an embedder latch a task re-checks on resume) and the consumer
82 * holds a loop handle rather than a pid -- as an xtc_exec carrier
83 * scheduler does when it marks a session runnable on a sibling loop.
84 *
85 * CONTRACT (important): relying on raw fd readiness alone to wake a
86 * loop is only safe for a condition libxtc itself produces on that
87 * loop's thread. When the readiness is produced by a DIFFERENT
88 * thread, that write races the target loop's park/re-arm window and
89 * can be missed -- the loop sleeps in xtc_io_poll while the fd is
90 * ready. The producer MUST pair the readiness with an explicit
91 * nudge: xtc_loop_wake(target_loop) (or xtc_proc_wake(pid) for a
92 * parked proc). The nudge is lost-wake-free against the pre-sleep
93 * window -- it writes the loop's wakeup fd, which the backends keep
94 * armed across the drain (io_uring re-arm-before-drain; epoll/kqueue
95 * level-triggered) -- so a wake issued at any time surfaces on the
96 * next poll. It delivers no event; the woken loop just re-polls, so
97 * a spurious wake is always safe.
98 *
99 * Returns XTC_OK (including when the loop is already awake/running),
100 * XTC_E_INVAL for a NULL loop. Safe to call from any thread.
101 */
102XTC_API int xtc_loop_wake(xtc_loop_t *loop);
103
104/*
105 * Borrow the loop's resource accountant. Non-NULL after init.
106 * The pointer is owned by the loop and must not be freed by the
107 * caller; it is reset on xtc_loop_fini.
108 *
109 * PUBLIC: struct xtc_res *xtc_loop_res __P((xtc_loop_t *));
110 */
111struct xtc_res;
112XTC_API struct xtc_res *xtc_loop_res(xtc_loop_t *loop);
113
114XTC_API int xtc_task_spawn(xtc_loop_t *loop, xtc_task_fn fn, void *user,
115 xtc_task_t **out_task);
116XTC_API int xtc_task_waker(xtc_task_t *task, xtc_waker_t *out);
117
118/*
119 * Park the calling task on a timer. The task's fn returned PENDING
120 * (or is about to); after delay_ns elapses, the loop will re-run fn.
121 */
122XTC_API int xtc_task_park_on_timer(xtc_task_t *self, int64_t delay_ns);
123
124/*
125 * Park the calling task on fd readiness. When the fd becomes ready
126 * for any of the requested interest bits, the loop unregisters the
127 * fd and re-runs the task fn.
128 *
129 * A task may have at most one outstanding park (timer or fd) at any
130 * moment; a second park while the first is in flight returns
131 * XTC_E_INVAL.
132 */
133XTC_API int xtc_task_park_on_fd(xtc_task_t *self, int fd, uint32_t interest);
134
135XTC_API int xtc_waker_wake(const xtc_waker_t *w);
136
137XTC_API int xtc_timer_set(xtc_loop_t *loop, int64_t delay_ns,
138 xtc_timer_fn fn, void *user, xtc_timer_t **out_timer);
139XTC_API int xtc_timer_cancel(xtc_timer_t *timer);
140
141#endif /* XTC_LOOP_H */