xtc_tnt(3)

---

xtc_tnt(3)

Tina-faithful stackless Isolate layer

XTC_TNT(3) Library Functions Manual XTC_TNT(3)

xtc_tnt_start, xtc_tnt_stop, xtc_tnt_spawn_on, xtc_tnt_spawn, xtc_tnt_send, xtc_tnt_submit_recv, xtc_tnt_io_send, xtc_tnt_submit_close, xtc_tnt_register_timer, xtc_tnt_self, xtc_tnt_shard_id, xtc_tnt_scratch_arenaTina-faithful stackless Isolate layer

#include <xtc.h>
#include <xtc_tnt.h>

int
xtc_tnt_start(const xtc_tnt_spec_t *spec);

void
xtc_tnt_stop(void);

xtc_tnt_spawn_error_t
xtc_tnt_spawn_on(uint8_t shard, uint8_t type_id, const void *args, size_t args_size);

xtc_tnt_spawn_error_t
xtc_tnt_spawn(uint8_t type_id, const void *args, size_t args_size, xtc_tnt_handle_t *out_handle);

xtc_tnt_send_result_t
xtc_tnt_send(xtc_tnt_handle_t to, uint16_t tag, const void *payload, size_t payload_size);

xtc_tnt_io_result_t
xtc_tnt_submit_recv(int fd);

xtc_tnt_io_result_t
xtc_tnt_io_send(int fd, const void *buffer, size_t len);

xtc_tnt_io_result_t
xtc_tnt_submit_close(int fd);

void
xtc_tnt_register_timer(uint64_t duration_ns, uint16_t tag);

xtc_tnt_handle_t
xtc_tnt_self(void);

uint8_t
xtc_tnt_shard_id(void);

void *
xtc_tnt_scratch_arena(size_t size);

tnt is an L4 orchestration layer that reproduces the Isolate programming model on the libxtc runtime. An is a stackless typed state machine: a dense struct in a per-type arena, addressed by a 64-bit generational handle, whose handler runs to completion and returns a instead of blocking. The decisive mapping is : one long-lived xtc_proc per xtc_exec(3) loop owns the typed arenas and runs the dispatch loop, so a task costs the bytes of its struct (hundreds) rather than a full fiber stack (kilobytes).

The layer is configured by an xtc_tnt_spec_t that names the Isolate types (an xtc_tnt_type_t descriptor table giving each type a stride, mailbox capacity, per-tick dispatch budget, and init_fn / handler_fn callbacks), the shard count, and an optional boot Isolate auto-spawned on shard 0.

() validates the spec, carves every arena from boot-time xtc_slab(3) caches, pins one shard proc per loop, and runs until the system stops. It blocks; it returns XTC_OK on clean shutdown or a negative XTC_E_* on a boot failure. () requests shutdown and is safe to call from any thread or a signal handler. () spawns an Isolate from outside any handler (e.g. a listener proc or () before start) and routes it onto the target shard; it is thread-safe.

The remaining functions are : they are valid only during an active handler or init_fn invocation, where they resolve the current shard and turn frame from thread-local state. () creates a child Isolate on the current shard (its init_fn runs immediately) and returns its handle in out_handle. () enqueues a message (with a user tag of at least XTC_TNT_USER_TAG_BASE and a payload of at most XTC_TNT_MAX_PAYLOAD_SIZE bytes) into the target's bounded mailbox; it never grows the mailbox or retries. (), (), and () an I/O operation into the current turn frame; the shard commits it (via a short-lived courier fiber) only if the handler then returns XTC_TNT_WAIT_IO. A staged xtc_tnt_io_send() buffer must live inside the Isolate struct, since it is read at commit time after the handler returns. On completion the Isolate receives a message tagged XTC_TNT_IO_TAG_RECV_COMPLETE, XTC_TNT_IO_TAG_SEND_COMPLETE, or XTC_TNT_IO_TAG_CLOSE_COMPLETE. () arms a one-shot timer that delivers tag back to the current Isolate after duration_ns nanoseconds. () returns the current Isolate's handle and () the current shard's 0-based id. () bump-allocates size bytes from the shard-wide scratch arena, which is reset before every handler call; it is for per-turn temporaries only and must not be retained across a handler return.

A handler returns an xtc_tnt_transition_t telling the shard what the Isolate wants next, not how to schedule it:

clean exit; the slot is torn down and its generation bumped.
run again next tick.
park until the mailbox has a message.
commit the staged I/O and park on it.
voluntary failure; the slot is torn down (let it crash).

xtc_tnt_start() returns XTC_OK on clean shutdown, or a negative XTC_E_* (e.g. XTC_E_INVAL, XTC_E_NOMEM) on a boot failure. xtc_tnt_send() returns XTC_TNT_SEND_OK, XTC_TNT_SEND_MAILBOX_FULL (target at capacity, message dropped), XTC_TNT_SEND_POOL_EXHAUSTED, or XTC_TNT_SEND_STALE_HANDLE (target dead or generation mismatch). xtc_tnt_spawn() and xtc_tnt_spawn_on() return XTC_TNT_SPAWN_OK or one of XTC_TNT_SPAWN_ARENA_FULL, XTC_TNT_SPAWN_TYPE_NOT_ALLOCATED, XTC_TNT_SPAWN_INIT_FAILED. xtc_tnt_submit_recv(), xtc_tnt_io_send(), and xtc_tnt_submit_close() return XTC_TNT_IO_OK, XTC_TNT_IO_TOO_MANY (turn-frame staging slots exhausted), or XTC_TNT_IO_BAD_FD. xtc_tnt_self() returns XTC_TNT_HANDLE_NONE when called outside a handler. xtc_tnt_scratch_arena() returns NULL when the arena is exhausted or no shard is active.

A TCP echo Isolate, one per connection:

typedef struct echo_conn {
    int     fd;
    uint8_t buffer[256];
    int     bytes;
} echo_conn_t;

static xtc_tnt_transition_t
echo_init(void *self_raw, const void *args, size_t n)
{
    echo_conn_t *self = xtc_tnt_self_as(echo_conn_t, self_raw);
    const echo_args_t *a = args;
    (void)n;
    self->fd = a->client_fd;
    if (xtc_tnt_submit_recv(self->fd) != XTC_TNT_IO_OK)
        return xtc_tnt_transition_to_crash(
            XTC_TNT_FAULT_CONTRACT_VIOLATION);
    return XTC_TNT_TRANSITION_WAIT_IO;
}

static xtc_tnt_transition_t
echo_handler(void *self_raw, xtc_tnt_message_t *msg)
{
    echo_conn_t *self = xtc_tnt_self_as(echo_conn_t, self_raw);
    switch (msg->tag) {
    case XTC_TNT_IO_TAG_RECV_COMPLETE:
        if (msg->body.io.result <= 0) {
            (void)xtc_tnt_submit_close(self->fd);
            return XTC_TNT_TRANSITION_WAIT_IO;
        }
        self->bytes = msg->body.io.result;
        memcpy(self->buffer, msg->body.io.buffer, self->bytes);
        (void)xtc_tnt_io_send(self->fd, self->buffer, self->bytes);
        return XTC_TNT_TRANSITION_WAIT_IO;
    case XTC_TNT_IO_TAG_SEND_COMPLETE:
        (void)xtc_tnt_submit_recv(self->fd);
        return XTC_TNT_TRANSITION_WAIT_IO;
    case XTC_TNT_IO_TAG_CLOSE_COMPLETE:
        return XTC_TNT_TRANSITION_DONE;
    default:
        return XTC_TNT_TRANSITION_WAIT_MESSAGE;
    }
}

xtc_svr(3), xtc_proc(3), xtc_exec(3), xtc_slab(3), xtc(7)

Appeared in xtc 0.1.

June 30, 2026 Debian

View the mdoc source