xtc_tnt(3)
---xtc_tnt(3)
Tina-faithful stackless Isolate layer
| XTC_TNT(3) | Library Functions Manual | XTC_TNT(3) |
NAME
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_arena —
Tina-faithful stackless Isolate layer
SYNOPSIS
#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);
DESCRIPTION
tnt is an L4 orchestration layer that
reproduces the
Tina
Isolate programming model on the libxtc runtime. An
Isolate
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
transition
instead of blocking. The decisive mapping is
shard-to-proc:
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.
Lifecycle
xtc_tnt_start()
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.
xtc_tnt_stop()
requests shutdown and is safe to call from any thread or a signal handler.
xtc_tnt_spawn_on()
spawns an Isolate from outside any handler (e.g. a listener proc or
main()
before start) and routes it onto the target shard; it is thread-safe.
Ambient handler API
The remaining functions are
ambient:
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.
xtc_tnt_spawn()
creates a child Isolate on the current shard (its
init_fn runs immediately) and returns its handle in
out_handle.
xtc_tnt_send()
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.
xtc_tnt_submit_recv(),
xtc_tnt_io_send(),
and
xtc_tnt_submit_close()
stage 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.
xtc_tnt_register_timer()
arms a one-shot timer that delivers tag back to the
current Isolate after duration_ns nanoseconds.
xtc_tnt_self()
returns the current Isolate's handle and
xtc_tnt_shard_id()
the current shard's 0-based id.
xtc_tnt_scratch_arena()
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.
Transitions
A handler returns an xtc_tnt_transition_t telling the shard what the Isolate wants next, not how to schedule it:
XTC_TNT_DONE- clean exit; the slot is torn down and its generation bumped.
XTC_TNT_YIELD- run again next tick.
XTC_TNT_WAIT_MESSAGE- park until the mailbox has a message.
XTC_TNT_WAIT_IO- commit the staged I/O and park on it.
XTC_TNT_CRASH- voluntary failure; the slot is torn down (let it crash).
RETURN VALUES
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.
EXAMPLES
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;
}
}
SEE ALSO
HISTORY
Appeared in xtc 0.1.
| June 30, 2026 | Debian |