|
libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
|
A concurrency runtime for serious C programs.
Read the documentation → (mirror: https://gregburd.codeberg.page/libxtc/)
xtc gives C the same asynchronous, fault-tolerant, predictable-latency foundation that Tokio gives Rust, that the BEAM gives Erlang, and that Seastar gives C++. It is a single library you can link against to write network servers, databases, queues, schedulers, and any other long-lived service that needs to handle thousands of connections, recover from faults, and stay inside a fixed resource budget on commodity hardware.
one_for_one, one_for_all, rest_for_one, simple_one_for_one), and a gen_server-shaped server abstraction. Crashes are caught and restarted by a tree, not by your shell script.xtc_res) with high-water alert callbacks so you can hold bounded RSS, file descriptors, in-flight tasks, and bandwidth under stress. Backpressure is built in; OOM-spirals are not.docs/M_WINDOWS_MATRIX.md, docs/M_LIBC_MATRIX.md, and PLAN.md for the per-platform status.roff_t pointer-into-region works across processes). Small messages (payload up to 256 bytes) are served from a per-thread envelope pool, so the common send path takes no allocator round-trip; larger messages fall back to malloc. Reads on the read-mostly primitive (xtc_lrlock) are wait-free.Compile:
That's a one-process actor system in 25 lines.
examples/05_rexis/ is a working Redis-protocol server in ~4,800 LOC (command handling, storage, connections, expiry, metrics; excludes tests). It uses every major xtc subsystem and stays inside hard --max-memory, --max-keys, --max-clients, --max-iops, and --cores caps under load. Run it with:
Then talk to it with redis-cli like any Redis server.
Other examples in examples/:
| Example | What it shows |
|---|---|
01_hello_async/ | A single async task with a timer |
02_proc_pingpong/ | Two BEAM processes bouncing messages |
03_supervised_app/ | Crash a worker, watch the supervisor restart it |
04_lockmgr_demo/ | The 9-mode transactional lock manager |
05_rexis/ | Networked, budgeted, multi-command Redis-compat server |
xtc owes a lot to three runtimes that came before:
You either start with BEAM or you build it over the years in your stack and in your infra – as detailed in "You Built an Erlang": https://vereis.com/posts/you_built_an_erlang
Where these conflict, xtc picks the choice that's most idiomatic in C and explains why in PLAN.md. Read that file when you want to understand the why; read the man pages and headers when you want the what.
xtc is 1.0. The public API surface is stable and semver applies from here: no breaking change to a documented public API without a major version bump. The semver / deprecation policy is documented in docs/abi-stability.md.
What's working today:
| Layer | Status |
|---|---|
| L0 OS substrate | Linux, FreeBSD, illumos runtime-verified; Windows (MinGW/Clang64/MSVC) and macOS OS-layer ports build. (An AIX/ppc64 OS-layer port compiles in-tree but AIX is NOT supported/maintained – unverified, off the roadmap.) |
| L1 I/O | io_uring, epoll, kqueue, poll, select, and illumos event-ports (port_*) runtime-verified (the last on big-endian sparcv9, including its native SIGEV_PORT file-AIO path). IOCP (Windows) runtime-verified on a host with MinGW (loop/task/timer/wakeup/socket-poll/file-AIO); AIX pollset COMPILES and is code-reviewed but not yet runtime-verified. Per-commit CI runs Linux + macOS at runtime; Windows CI is a build-only smoke. |
| L2 event runtime | Done. Single + multi-loop, work stealing, hand-written x86_64 fcontext (~7.6 ns/swap) + 7 more arches + ucontext fallback. |
| L3 primitives | Done. Channels, processes, sync, RCU, lwlock, lrlock, lockmgr, slab, resource caps, observability. |
| L4 orchestration | Done. Supervisors (4 strategies), gen_server, registry, app bringup, hierarchical mctx. |
| L5 PG adapter | Designed; not yet implemented. |
| TLS | OpenSSL, GnuTLS, wolfSSL, Mbed TLS, and BoringSSL backends build and pass the m18 suite in CI (docs/M_TLS_MATRIX.md); SChannel (Windows) is compile-only. |
Test coverage today: 610 munit test cases on Linux, clean under AddressSanitizer and UBSan in CI, plus 36 hegel properties across 17 suites that all pass when the tier is enabled with --with-hegel.
The property tier is opt-in because it needs libhegel (hegeldev/hegel-rust), an in-process C-ABI shared library. nix develop provides it, so configure --with-hegel finds it via pkg-config with no other setup. Without it, make check prints a loud per-suite SKIP and a count of unverified properties rather than passing silently – a green run without --with-hegel does NOT mean those properties hold. See ADR-0002. GitHub CI also runs the full C munit suite on macOS (Apple Silicon: kqueue + ucontext + GCD dispatch semaphores) and an MSVC xtc.lib + smoke build on Windows every commit. FreeBSD 15 (clang, kqueue) was re-verified against the current tree (full gmake check passes, including the native kqueue file-AIO path); the Windows IOCP runtime was runtime-verified on a host with MinGW. illumos (SunOS 5.11, UltraSPARC v9 / big-endian sparcv9, gcc) was also re-verified against the current tree (full gmake check, OpenSSL 3, native event-port file-AIO). None of FreeBSD/illumos/Windows-runtime is in per-commit CI yet. Windows also passes ~233 munit under MinGW and 48/48 of the buildable binaries under Clang64 in prior runs.
Honest gaps and known issues live in docs/KNOWN_ISSUES.md. The original design plan (a historical bring-up document) is in PLAN.md; it predates the implementation and does not track current status – the live status is this table plus KNOWN_ISSUES.
xtc is BSD-style C11. No external deps beyond libc, pthreads, and optionally liburing/OpenSSL.
make check builds and runs the unit/PBT/shell-gate suite (fast, minutes). It does NOT include the DST suite – that needs a separate --with-io-backend=sim build. Run it explicitly:
Configure flags worth knowing:
| Flag | What it does |
|---|---|
--with-io-backend=AUTO | Pick io_uring, epoll, kqueue, IOCP, poll, select; defaults are sensible per-OS |
| --with-tls=openssl|none|auto | Build TLS support (OpenSSL only today) | | --with-liburing=PATH | Use a specific liburing install | | --with-hegel[=PREFIX] | Property-based tests via libhegel (in-process C ABI). With no PREFIX, found by pkg-config; nix develop supplies it |
The meson build (meson.build + meson_options.txt) is at parity with the autotools build: it compiles the full static (and, with -Dshared=true, shared) library from the same source list, with the same io-backend / coroutine / TLS selection, and meson test runs the same C munit suite (109 tests) as make check's C tier. The exported xtc_* / __xtc_* symbol set is byte-for-byte identical to the autotools libxtc.a at the same optimization level. Its options mirror the ./configure flags: -Dio-backend= (auto/poll/epoll/uring/kqueue/ iocp/solaris/aix/select/sim), -Dtls= (auto/openssl/libressl/boringssl/ mbedtls/gnutls/wolfssl/schannel/none), -Daccel= (auto/yes/no), -Dliburing=PATH, -Dshared=, and -Ddiagnostic=. configure.ac / dist/Makefile.in remain the reference for WHAT is built; meson tracks them. Not yet wired here: the shell-gate tests (TESTS_SH), the property-based tier (TESTS_PBT, needs --with-hegel), and the deterministic-simulation tier (make check-dst) still run via the autotools make check; and meson is not yet exercised in CI (a follow-up).
examples/ – start here. Five working programs from "hello async" to "Redis-compat server with budgets".docs/getting-started.md – step-by-step beginner walkthrough, from "just cloned" to an async TCP server.man/man3/ and man/man7/ – per-API reference. Every public xtc_* symbol has a man page (coverage is gate-enforced in make check).PLAN.md – the full design rationale. Long but exhaustive.docs/ARCHITECTURE.md – the layer diagram, the principles, the why.docs/abi-stability.md – semver and deprecation policy.docs/KNOWN_ISSUES.md – everything I know about that's not perfect.ISC. See [LICENSE](LICENSE).
Issues and patches welcome. Code style is BSD KNF as encoded in .clang-format. All contributions must be ASCII-only in source, docs, comments, and commit messages. Run dist/s_async and dist/s_cfg lints before submitting. Property-based tests (via hegel-c) are encouraged for any new primitive.
xtc_lrlock (a C implementation of it)