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