xtc_chan(3)

---

xtc_chan(3)

typed channels for cross-task message passing

XTC_CHAN(3) Library Functions Manual XTC_CHAN(3)

xtc_chan_oneshot, xtc_chan_oneshot_create, xtc_chan_oneshot_destroy, xtc_chan_oneshot_send, xtc_chan_oneshot_try_recv, xtc_chan_oneshot_set_waker, xtc_chan_mpsc, xtc_chan_mpsc_create, xtc_chan_mpsc_destroy, xtc_chan_mpsc_try_send, xtc_chan_mpsc_try_recv, xtc_chan_mpsc_set_waker, xtc_chan_mpsc_close, xtc_chan_mpsc_len, xtc_chan_mpmc, xtc_chan_mpmc_create, xtc_chan_mpmc_destroy, xtc_chan_mpmc_try_send, xtc_chan_mpmc_try_recv, xtc_chan_mpmc_close, xtc_chan_mpmc_len, xtc_chan_watch, xtc_chan_watch_create, xtc_chan_watch_destroy, xtc_chan_watch_send, xtc_chan_watch_recv, xtc_chan_broadcast, xtc_chan_broadcast_create, xtc_chan_broadcast_destroy, xtc_chan_broadcast_send, xtc_chan_broadcast_subscribe, xtc_chan_broadcast_unsubscribe, xtc_chan_broadcast_recvtyped channels for cross-task message passing

#include <xtc.h>
#include <xtc_chan.h>

int
xtc_chan_oneshot_create(xtc_res_t *res, xtc_chan_oneshot_t **out);

void
xtc_chan_oneshot_destroy(xtc_chan_oneshot_t *c);

int
xtc_chan_oneshot_send(xtc_chan_oneshot_t *c, void *msg);

int
xtc_chan_oneshot_try_recv(xtc_chan_oneshot_t *c, void **out);

int
xtc_chan_oneshot_set_waker(xtc_chan_oneshot_t *c, const xtc_waker_t *w);

int
xtc_chan_mpsc_create(xtc_res_t *res, size_t capacity, xtc_chan_mpsc_t **out);

void
xtc_chan_mpsc_destroy(xtc_chan_mpsc_t *c);

int
xtc_chan_mpsc_try_send(xtc_chan_mpsc_t *c, void *msg);

int
xtc_chan_mpsc_try_recv(xtc_chan_mpsc_t *c, void **out);

int
xtc_chan_mpsc_set_waker(xtc_chan_mpsc_t *c, const xtc_waker_t *w);

int
xtc_chan_mpsc_close(xtc_chan_mpsc_t *c);

size_t
xtc_chan_mpsc_len(const xtc_chan_mpsc_t *c);

int
xtc_chan_mpmc_create(xtc_res_t *res, size_t capacity, xtc_chan_mpmc_t **out);

void
xtc_chan_mpmc_destroy(xtc_chan_mpmc_t *c);

int
xtc_chan_mpmc_try_send(xtc_chan_mpmc_t *c, void *msg);

int
xtc_chan_mpmc_try_recv(xtc_chan_mpmc_t *c, void **out);

int
xtc_chan_mpmc_close(xtc_chan_mpmc_t *c);

size_t
xtc_chan_mpmc_len(const xtc_chan_mpmc_t *c);

int
xtc_chan_watch_create(xtc_res_t *res, xtc_chan_watch_t **out);

void
xtc_chan_watch_destroy(xtc_chan_watch_t *c);

int
xtc_chan_watch_send(xtc_chan_watch_t *c, void *value);

int
xtc_chan_watch_recv(xtc_chan_watch_t *c, void **out);

int
xtc_chan_broadcast_create(xtc_res_t *res, size_t capacity, xtc_chan_broadcast_t **out);

void
xtc_chan_broadcast_destroy(xtc_chan_broadcast_t *c);

int
xtc_chan_broadcast_send(xtc_chan_broadcast_t *c, void *msg);

int
xtc_chan_broadcast_subscribe(xtc_chan_broadcast_t *c, xtc_chan_broadcast_recv_t **out_recv);

void
xtc_chan_broadcast_unsubscribe(xtc_chan_broadcast_recv_t *r);

int
xtc_chan_broadcast_recv(xtc_chan_broadcast_recv_t *r, void **out, int *lagged);

xtc_chan provides typed channel primitives for cross-task / cross-process / cross-thread message passing. Each channel kind has different fan-in / fan-out semantics:

oneshot
A single message; one sender, one receiver. Used for request / reply patterns, one-shot futures. () and () manage the channel; () delivers the single message; () polls for it; and () registers an xtc_waker_t for async readiness notification.
mpsc
Multiple producers, single consumer. Bounded ring buffer. Lock- free producer side using sequence-number CAS; consumer needs no synchronisation. () takes a capacity; () and () move messages without parking; () arms an async waker; () marks the channel closed; and () reports the queued count. () frees it.
mpmc
Multiple producers, multiple consumers. Bounded ring buffer serialising both sides via per-slot sequence numbers (Vyukov-style queue). (), (), (), (), (), and () mirror the MPSC entry points.
watch
Single-producer, multi-consumer of latest-value-only state. Useful for config that may change while readers want the current view. () and () manage the channel; () publishes a new latest value; and () reads the current value.
broadcast
Multi-producer, multi-consumer fan-out. Each subscribed consumer sees every message. Per-consumer lag tracking; slow consumers see their queue fill and may drop. () and () manage the channel; () fans a message out; () and () add and remove a consumer cursor; and () reads the next message for a subscriber, reporting a lagged count of messages skipped when it fell behind.

All channels are heap-allocated and reference-managed via the optional xtc_res_t accountant; passing one charges memory against the cap.

: the sender transfers ownership of the message buffer to the receiver. After () returns XTC_OK, the sender must not free or further mutate the buffer. The receiver is responsible for freeing.

For BEAM-style mailboxes use xtc_proc(3) directly: every process has its own mailbox without any explicit channel allocation. Channels are for fan-in / fan-out patterns that don't fit the per-proc-mailbox model:

  • A pool of N workers consuming from one MPSC queue.
  • N producers feeding M consumers via MPMC.
  • Config-change broadcasts via watch channels.

MPSC work queue:

xtc_chan_mpsc_t *q;
xtc_chan_mpsc_create(NULL, /* cap */ 1024, &q);

/* producer */
xtc_chan_mpsc_try_send(q, work_item);

/* consumer */
void *item;
if (xtc_chan_mpsc_try_recv(q, &item) == XTC_OK) {
    handle(item);
    free(item);
}

xtc_proc(3), xtc_res(3), xtc(7)

Appeared in xtc 0.1.

May 28, 2026 Debian

View the mdoc source