xtc_sync(3)

---

xtc_sync(3)

async synchronisation primitives

XTC_SYNC(3) Library Functions Manual XTC_SYNC(3)

xtc_amutex, xtc_amutex_create, xtc_amutex_create_ex, xtc_amutex_static, xtc_amutex_destroy, xtc_amutex_lock, xtc_amutex_try_lock, xtc_amutex_unlock, xtc_arwlock, xtc_arwlock_create, xtc_arwlock_destroy, xtc_arwlock_rdlock, xtc_arwlock_wrlock, xtc_arwlock_unlock, xtc_rwlock, xtc_rwlock_create, xtc_rwlock_destroy, xtc_rwlock_rdlock, xtc_rwlock_wrlock, xtc_rwlock_unlock, xtc_notify, xtc_notify_create, xtc_notify_destroy, xtc_notify_signal, xtc_notify_wait, xtc_sem, xtc_sem_create, xtc_sem_destroy, xtc_sem_post, xtc_sem_acquire, xtc_sem_try_acquire, xtc_sem_count, xtc_barrier, xtc_barrier_create, xtc_barrier_destroy, xtc_barrier_wait, xtc_gate, xtc_gate_create, xtc_gate_destroy, xtc_gate_enter, xtc_gate_leave, xtc_gate_close, xtc_gate_drain, xtc_gate_count, xtc_abort_source, xtc_abort_source_create, xtc_abort_source_destroy, xtc_abort_source_fire, xtc_abort_source_token, xtc_abort_token_is_aborted, xtc_abort_token_reason — async synchronisation primitives

#include <xtc.h>
#include <xtc_sync.h>

int
xtc_amutex_create(xtc_amutex_t **out);

int
xtc_amutex_create_ex(xtc_amutex_t **out, unsigned flags);

xtc_amutex_t *
xtc_amutex_static(unsigned flags);

void
xtc_amutex_destroy(xtc_amutex_t *m);

int
xtc_amutex_lock(xtc_amutex_t *m, int64_t timeout_ns);

int
xtc_amutex_try_lock(xtc_amutex_t *m);

int
xtc_amutex_unlock(xtc_amutex_t *m);

int
xtc_arwlock_create(xtc_arwlock_t **out);

void
xtc_arwlock_destroy(xtc_arwlock_t *rw);

int
xtc_arwlock_rdlock(xtc_arwlock_t *rw, int64_t timeout_ns);

int
xtc_arwlock_wrlock(xtc_arwlock_t *rw, int64_t timeout_ns);

int
xtc_arwlock_unlock(xtc_arwlock_t *rw);

int
xtc_rwlock_create(xtc_rwlock_t **out);

void
xtc_rwlock_destroy(xtc_rwlock_t *rw);

int
xtc_rwlock_rdlock(xtc_rwlock_t *rw, int64_t timeout_ns);

int
xtc_rwlock_wrlock(xtc_rwlock_t *rw, int64_t timeout_ns);

int
xtc_rwlock_unlock(xtc_rwlock_t *rw);

int
xtc_notify_create(xtc_notify_t **out);

void
xtc_notify_destroy(xtc_notify_t *n);

int
xtc_notify_signal(xtc_notify_t *n);

int
xtc_notify_wait(xtc_notify_t *n, int64_t timeout_ns);

int
xtc_sem_create(unsigned initial, xtc_sem_t **out);

void
xtc_sem_destroy(xtc_sem_t *s);

int
xtc_sem_post(xtc_sem_t *s, unsigned n);

int
xtc_sem_acquire(xtc_sem_t *s, unsigned n, int64_t timeout_ns);

int
xtc_sem_try_acquire(xtc_sem_t *s, unsigned n);

int
xtc_sem_count(const xtc_sem_t *s);

int
xtc_barrier_create(unsigned n, xtc_barrier_t **out);

void
xtc_barrier_destroy(xtc_barrier_t *b);

int
xtc_barrier_wait(xtc_barrier_t *b);

int
xtc_gate_create(xtc_gate_t **out);

void
xtc_gate_destroy(xtc_gate_t *g);

int
xtc_gate_enter(xtc_gate_t *g);

int
xtc_gate_leave(xtc_gate_t *g);

int
xtc_gate_close(xtc_gate_t *g);

int
xtc_gate_drain(xtc_gate_t *g, int64_t timeout_ns);

int
xtc_gate_count(const xtc_gate_t *g);

int
xtc_abort_source_create(xtc_abort_source_t **out);

void
xtc_abort_source_destroy(xtc_abort_source_t *src);

int
xtc_abort_source_fire(xtc_abort_source_t *src, int reason);

int
xtc_abort_source_token(xtc_abort_source_t *src, xtc_abort_token_t *tok);

int
xtc_abort_token_is_aborted(const xtc_abort_token_t *tok);

int
xtc_abort_token_reason(const xtc_abort_token_t *tok);

xtc_sync provides async-aware synchronisation primitives that integrate with xtc_loop(3) scheduling: blocked acquires park the calling fiber rather than blocking the OS thread.

xtc_amutex
Async mutex. Blocked acquires park the fiber. Use for critical sections held across xtc_recv(3) or other async I/O. Compare with xtc_lwlock(3) which uses pthread mutex underneath (blocks the OS thread). () allocates a mutex; () takes a flags word; () returns a process-static instance. () parks until acquired or the timeout_ns deadline elapses; () never parks; () releases; and () frees it. A proc that exits while holding an amutex leaves it held forever (there is no robust-mutex recovery, and a later proc never inherits a recursive amutex's ownership), so release it in an () hook (see xtc_proc(3)) if the proc can die inside the critical section.
xtc_arwlock
Async reader-writer latch (shared / exclusive). Blocked acquires park the fiber, exactly as xtc_amutex, so a holder may park on I/O while latched and lock coupling may hold a parent latch across a child fix without wedging a cooperative loop. FIFO fairness keeps a read stream from starving a waiting writer. This is the reader-writer primitive a concurrent B-tree latches its pages with. () and () manage the object; xtc_arwlock_rdlock() and xtc_arwlock_wrlock() take it shared or exclusive with a timeout_ns deadline; () releases the current hold.
xtc_rwlock
Reader-writer lock with writer priority, built on a pthread mutex and condition variable: a blocked acquire blocks the OS thread (it does NOT park the fiber). Use xtc_arwlock instead on the hot path of an event loop. (), (), (), (), and () mirror the async variant.
xtc_notify
A 0-or-1 latch. () and () manage the object. Producers (); consumers xtc_notify_wait() until signalled. Each signal is handed to exactly one waiting fiber, oldest first; with no fiber waiting it is stored (and wakes any OS-thread waiter), and signals that arrive before the next wait collapse into one. Useful for one-shot ready signals.
xtc_sem
Counting semaphore. () takes an initial count; () frees it. () adds n permits; xtc_sem_acquire() takes n permits, parking until available or the timeout_ns deadline. A post wakes the parked fibers the new count can satisfy, oldest first, not every waiter; a waiter stays queued until it takes its permits, times out or is killed, so no post is lost. Admission is not strictly FIFO: a caller that finds enough permits takes them without queueing; () never parks; and () reports the current permit count.
xtc_barrier
Synchronisation point: n participants created by () call xtc_barrier_wait() until all have arrived; then all proceed. () frees it.
xtc_gate
A counting gate. () and () manage the object. () and () bracket in-flight work; () rejects new entrants; xtc_gate_drain() parks until the in-flight count reaches zero or the timeout_ns deadline, whether or not the gate is closed; and () reports the number of active occupants.
xtc_abort_source
Cancellation token. () and () manage the source. Initiator calls () with a reason to signal cancellation; () hands out a token participants poll with () and (). Used to wire timeout / shutdown into long-running operations cooperatively.

All primitives are thread-safe. A blocked acquire from a fiber parks the fiber on the primitive's own wait queue, so the OS thread is free to run other work; a caller that is not on a loop blocks on a condition variable.

A timed acquire that is granted before its deadline cancels its deadline timer, so a later plain () of the same fiber does not wait for the old deadline.

A proc killed with () while parked in (), (), xtc_amutex_lock(), (), (), () or () leaves the wait cleanly before it unwinds: it is removed from the wait queue, a lock, signal or permit already handed to it is passed on to the next waiter, and its deadline timer is cancelled. The call does not return. A killed barrier party still counts as arrived for the current generation. Inside an () region the kill is deferred and the wait continues. The kill is noticed when the parked proc next runs (a wake, its own deadline, or the kill itself when the proc layer wakes parked targets).

xtc_rwlock is not covered: it blocks the OS thread and cannot be interrupted.

  • For database-style transactional locking with deadlock detection: xtc_lockmgr(3).
  • For short critical sections inside one xtc_loop: xtc_lwlock(3).
  • For read-mostly state with wait-free readers: xtc_lrlock(3) or xtc_rcu(3).
  • For "wait until N producers have all signalled": xtc_barrier or xtc_sem.
  • For cancellation propagation: xtc_abort_source.

xtc_async(3), xtc_proc(3), xtc_lwlock(3), xtc_lrlock(3), xtc_lockmgr(3), xtc_rcu(3), xtc(7)

Appeared in xtc 0.1.

May 28, 2026 Debian

View the mdoc source