libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
xtc_chan.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_chan.h
6 * The L3 channel taxonomy. M7 ships three:
7 *
8 * oneshot: one sender, one receiver, exactly-one message.
9 * mpsc: bounded multi-producer single-consumer queue.
10 * watch: many-to-many "latest value wins" slot (one frame).
11 *
12 * mpmc and broadcast arrive in M7.5. All channels have explicit
13 * bounded capacity; out-of-capacity behaviour is documented per
14 * type and tracked via xtc_res so callers cannot exhaust memory.
15 *
16 * Each channel exposes both a blocking variant (caller parks on
17 * a waker if the queue is full/empty) and a non-blocking variant
18 * (returns XTC_E_AGAIN immediately).
19 */
20
21#ifndef XTC_CHAN_H
22#define XTC_CHAN_H
23
24#include <stddef.h>
25#include <stdint.h>
26
27#include "xtc.h"
28#include "xtc_loop.h"
29#include "xtc_res.h"
30
31/* ----- oneshot ----------------------------------------------------- */
32
33typedef struct xtc_chan_oneshot xtc_chan_oneshot_t;
34
35/*
36 * PUBLIC: int xtc_chan_oneshot_create __P((xtc_res_t *, xtc_chan_oneshot_t **));
37 * PUBLIC: void xtc_chan_oneshot_destroy __P((xtc_chan_oneshot_t *));
38 * PUBLIC: int xtc_chan_oneshot_send __P((xtc_chan_oneshot_t *, void *));
39 * PUBLIC: int xtc_chan_oneshot_try_recv __P((xtc_chan_oneshot_t *, void **));
40 * PUBLIC: int xtc_chan_oneshot_set_waker __P((xtc_chan_oneshot_t *, const xtc_waker_t *));
41 */
42int xtc_chan_oneshot_create(xtc_res_t *res, xtc_chan_oneshot_t **out);
43void xtc_chan_oneshot_destroy(xtc_chan_oneshot_t *c);
44
45/*
46 * Send the single message. Idempotent on a closed channel: a second
47 * send returns XTC_E_INVAL. Always succeeds in the open case (the
48 * slot has capacity 1). If a waker has been registered, fires it.
49 */
50int xtc_chan_oneshot_send(xtc_chan_oneshot_t *c, void *msg);
51
52/* Non-blocking receive. Returns XTC_E_AGAIN if nothing yet. */
53int xtc_chan_oneshot_try_recv(xtc_chan_oneshot_t *c, void **out);
54
55/*
56 * Register a waker to fire when send happens. Replaces any prior
57 * waker. The receiver pattern: register waker, return PENDING from
58 * the task; when waker fires, try_recv.
59 */
60int xtc_chan_oneshot_set_waker(xtc_chan_oneshot_t *c, const xtc_waker_t *w);
61
62/* ----- mpsc bounded ------------------------------------------------ */
63
64typedef struct xtc_chan_mpsc xtc_chan_mpsc_t;
65
66/*
67 * PUBLIC: int xtc_chan_mpsc_create __P((xtc_res_t *, size_t, xtc_chan_mpsc_t **));
68 * PUBLIC: void xtc_chan_mpsc_destroy __P((xtc_chan_mpsc_t *));
69 * PUBLIC: int xtc_chan_mpsc_try_send __P((xtc_chan_mpsc_t *, void *));
70 * PUBLIC: int xtc_chan_mpsc_try_recv __P((xtc_chan_mpsc_t *, void **));
71 * PUBLIC: int xtc_chan_mpsc_set_waker __P((xtc_chan_mpsc_t *, const xtc_waker_t *));
72 * PUBLIC: int xtc_chan_mpsc_close __P((xtc_chan_mpsc_t *));
73 * PUBLIC: size_t xtc_chan_mpsc_len __P((const xtc_chan_mpsc_t *));
74 */
75int xtc_chan_mpsc_create(xtc_res_t *res, size_t capacity,
76 xtc_chan_mpsc_t **out);
77void xtc_chan_mpsc_destroy(xtc_chan_mpsc_t *c);
78
79/*
80 * Try to send. Returns:
81 * XTC_OK on success
82 * XTC_E_AGAIN channel full (caller is responsible for backpressure;
83 * register a waker via _set_waker if you want to
84 * wait for capacity, or use the wrapper xtc_chan_send)
85 * XTC_E_INVAL closed channel
86 * XTC_E_RESOURCE global slot cap (xtc_res XTC_RES_CHAN_SLOTS) hit
87 */
88int xtc_chan_mpsc_try_send(xtc_chan_mpsc_t *c, void *msg);
89
90/* Non-blocking receive. Returns XTC_E_AGAIN on empty. */
91int xtc_chan_mpsc_try_recv(xtc_chan_mpsc_t *c, void **out);
92
93/* Register the consumer's waker. Fired on every successful send. */
94int xtc_chan_mpsc_set_waker(xtc_chan_mpsc_t *c, const xtc_waker_t *w);
95
96/*
97 * Close the channel. Subsequent sends fail; pending receives drain
98 * the buffer then return XTC_E_INVAL on the next call.
99 */
100int xtc_chan_mpsc_close(xtc_chan_mpsc_t *c);
101
102size_t xtc_chan_mpsc_len(const xtc_chan_mpsc_t *c);
103
104/* ----- watch (latest-value-wins) ----------------------------------- */
105
106typedef struct xtc_chan_watch xtc_chan_watch_t;
107
108/*
109 * PUBLIC: int xtc_chan_watch_create __P((xtc_res_t *, xtc_chan_watch_t **));
110 * PUBLIC: void xtc_chan_watch_destroy __P((xtc_chan_watch_t *));
111 * PUBLIC: int xtc_chan_watch_send __P((xtc_chan_watch_t *, void *));
112 * PUBLIC: int xtc_chan_watch_recv __P((xtc_chan_watch_t *, void **));
113 */
114int xtc_chan_watch_create(xtc_res_t *res, xtc_chan_watch_t **out);
115void xtc_chan_watch_destroy(xtc_chan_watch_t *c);
116int xtc_chan_watch_send(xtc_chan_watch_t *c, void *value);
117int xtc_chan_watch_recv(xtc_chan_watch_t *c, void **out);
118
119/* ----- mpmc bounded ----------------------------------------------- */
120
121typedef struct xtc_chan_mpmc xtc_chan_mpmc_t;
122
123/*
124 * Bounded multi-producer multi-consumer queue. M7.5 ships a
125 * mutex-protected ring; the lock-free Vyukov variant is a future
126 * optimisation. Out-of-capacity behaviour matches mpsc:
127 * try_send returns XTC_E_AGAIN, callers register a waker if they
128 * want to wait.
129 *
130 * PUBLIC: int xtc_chan_mpmc_create __P((xtc_res_t *, size_t, xtc_chan_mpmc_t **));
131 * PUBLIC: void xtc_chan_mpmc_destroy __P((xtc_chan_mpmc_t *));
132 * PUBLIC: int xtc_chan_mpmc_try_send __P((xtc_chan_mpmc_t *, void *));
133 * PUBLIC: int xtc_chan_mpmc_try_recv __P((xtc_chan_mpmc_t *, void **));
134 * PUBLIC: int xtc_chan_mpmc_close __P((xtc_chan_mpmc_t *));
135 * PUBLIC: size_t xtc_chan_mpmc_len __P((const xtc_chan_mpmc_t *));
136 */
137int xtc_chan_mpmc_create(xtc_res_t *res, size_t capacity,
138 xtc_chan_mpmc_t **out);
139void xtc_chan_mpmc_destroy(xtc_chan_mpmc_t *c);
140int xtc_chan_mpmc_try_send(xtc_chan_mpmc_t *c, void *msg);
141int xtc_chan_mpmc_try_recv(xtc_chan_mpmc_t *c, void **out);
142int xtc_chan_mpmc_close(xtc_chan_mpmc_t *c);
143size_t xtc_chan_mpmc_len(const xtc_chan_mpmc_t *c);
144
145/* ----- broadcast --------------------------------------------------- */
146
147typedef struct xtc_chan_broadcast xtc_chan_broadcast_t;
148typedef struct xtc_chan_broadcast_recv xtc_chan_broadcast_recv_t;
149
150/*
151 * Broadcast channel: every subscribed receiver sees every message
152 * (best-effort). Each receiver carries its own cursor; if a
153 * receiver lags more than `capacity` messages it observes a
154 * "lagged" indicator and skips ahead to the latest available. This
155 * is Tokio's broadcast semantics.
156 *
157 * Senders never block; the ring is lossy on slow consumers, by
158 * design. For lossless multi-receiver use mpmc + a per-consumer
159 * filter, or supervise the slow consumer.
160 *
161 * PUBLIC: int xtc_chan_broadcast_create __P((xtc_res_t *, size_t, xtc_chan_broadcast_t **));
162 * PUBLIC: void xtc_chan_broadcast_destroy __P((xtc_chan_broadcast_t *));
163 * PUBLIC: int xtc_chan_broadcast_send __P((xtc_chan_broadcast_t *, void *));
164 * PUBLIC: int xtc_chan_broadcast_subscribe __P((xtc_chan_broadcast_t *, xtc_chan_broadcast_recv_t **));
165 * PUBLIC: void xtc_chan_broadcast_unsubscribe __P((xtc_chan_broadcast_recv_t *));
166 * PUBLIC: int xtc_chan_broadcast_recv __P((xtc_chan_broadcast_recv_t *, void **, int *));
167 */
168int xtc_chan_broadcast_create(xtc_res_t *res, size_t capacity,
169 xtc_chan_broadcast_t **out);
170void xtc_chan_broadcast_destroy(xtc_chan_broadcast_t *c);
171int xtc_chan_broadcast_send(xtc_chan_broadcast_t *c, void *msg);
172int xtc_chan_broadcast_subscribe(xtc_chan_broadcast_t *c,
173 xtc_chan_broadcast_recv_t **out_recv);
174void xtc_chan_broadcast_unsubscribe(xtc_chan_broadcast_recv_t *r);
175
176/*
177 * Receive the next message visible to this subscriber. On success
178 * returns XTC_OK and writes *out plus *lagged (count of skipped
179 * messages because we fell behind). XTC_E_AGAIN if the cursor is
180 * caught up; XTC_E_INVAL if r is NULL.
181 */
182int xtc_chan_broadcast_recv(xtc_chan_broadcast_recv_t *r,
183 void **out, int *lagged);
184
185#endif /* XTC_CHAN_H */