# Runtime

`runtime/` is the C platform layer. Generated programs link against it. `bin/giz` builds
it as `libgizlang_runtime.a` and `libgizlang_runtime.so`. A program an installed toolchain builds on Linux or macOS loads
the release's `lib/gizlang/TARGET/libgizlang_runtime.so` (`.dylib` on macOS: the runtime, libuv and OpenSSL) from its versions folder,
so a runtime fix reaches it without a relink and the programs on a host share one copy; a fast (-O0) source build
loads the build folder's. `GIZLANG_RUNTIME_STATIC=1` links the archive, for a program copied to a machine without
the toolchain. The toolchain's own programs link the archive (tools/cli/build.giz runtimeShared). Windows: see [Windows](#windows).

The runtime owns no language semantics. The compiler checks types, effects and borrows. The
runtime gives storage, handles and I/O through a narrow `gizlang_*` C ABI.

## Files

| File | Owns |
| --- | --- |
| `resources.c` | One handle table of files, sockets and processes: handles checked by generation, leases, borrows, deferred disposal, owner thread. |
| `heap.c` | The precise, non-moving managed heap of each World, for `Ref<T>`: its own handles, typed roots, holds for values owned outside frames (task boxes, channel items, live state), collection. [MEMORY](memory.md) owns the model. |
| `value_layout.c` | Walks the layout descriptors that the compiler emits (scalars, Refs, slices, products, variants): traces Refs, counts buffers as generated dup and drop do, and encodes a value independently of its position, for replay. |
| `replay.c` | Records the inputs of a run (declared I/O results, `Task.now`, batches of woken tasks) and replays them with no I/O (`giz run --record/--replay LOG`). A different call is a divergence (status 76). |
| `services.c` | Binds one heap, one handle table and one I/O loop to a call context. |
| `refs.c` | Ref create, load, store and collect. Holds and roots over the heap of the calling context (`gizlang_services_heap`). |
| `io.c` | The only event loop, on pinned libuv 1.53.1 (commit `eb497c58`). Timers, files, TCP connect, listen and accept, DNS, cancellation tokens. |
| `fs.c` | Files scoped by capability: attenuating grants, directory-relative opens that do not follow links, file size and time, directory creation, remove and list. |
| `text.c` | Console output (with the console grant of the call). |
| `base64.c` | Base64. It is freestanding: the native and the Wasm runtime both link it. Refer to [Base64 in C](#base64-in-c). |
| `isolate.c` | A process spawned `isolated` confines itself at start: namespaces, its folder as root, no capabilities, seccomp, no new machine code. |
| `uring.c` | The C part of `System.ring`: one io_uring of 128-byte entries that carries `URING_CMD` (the ublk driver of `fs/ublk.giz`), and the read-only map of a descriptor area (`System.mapShared`). It is the only io_uring of the runtime. Refer to [SYSTEM](system.md#block-devices). |
| `levels.c` | `gizlang_cpu_level`: the x86-64 level (baseline, v3 or v4) that the vector functions of a program run at (`tools/cli/levels.giz` gives each one a copy for each level). It reads the CPU once, or the level that `GIZLANG_CPU` names, and stops a program with a wrong `GIZLANG_CPU` before `main`. |
| `machine.c` | `gizlang:machine`, the one owner of the host's CPU facts: the instruction-set features, capped at the `levels.c` level (no AVX-512 under `GIZLANG_CPU=v3`), the CPUs of the process's affinity, each cache instance and the CPUs that share it, and the memory nodes. It reads them once a process (sysfs on Linux, sysctl on macOS). |
| `glibc235.c` | Linked into every program, so that it runs on glibc 2.35 and later. It gives the `__isoc23_*` functions that glibc 2.38 headers call under `_GNU_SOURCE`. It is compiled without `_GNU_SOURCE`, so it calls the older functions. `tests/tools/levels` checks that no symbol is newer than 2.35.  On Windows it holds the thread's marked bytes instead (a DLL cannot share a thread-local variable). |
| `win32.c` | Windows only (empty elsewhere): the POSIX file, directory, descriptor and process calls that `win32.h` declares, on Win32 handles, so the other files keep one code path. Refer to [Windows](#windows). |
| `format.c` | Value text, and UTF-8 validation of the C strings that native calls return. It uses memory only, so Wasm shares it. `string(bytes)` validation and byte search are Gizlang SIMD (`utf8Valid` in `sdk/core/runtime.giz`, `bytes.indexOf` and `indexOfAny` in `sdk/core/text.giz`). `gizlang_text_utf8`, `_find` and `_find_any` stay only for a CLI that the seed builds, until the next new seed. |
| `task.c` | Structured tasks: stackful coroutines (x86-64 and AArch64 switch, guarded `MAP_NORESERVE` stacks, ASan fiber hooks), one ready queue, the `io.c` loop when nothing is ready, cancellation and deadlock faults, and bounded channels whose values are checked by layout key. |
| `system.c` | The services behind `gizlang:system`: one handle table of directories, files, TCP and pipe streams, listeners, datagram sockets, `Ready` sets and child processes. The task that opened each one owns it. It operates over `fs.c` and `io.c`. |
| `state.c` | Named live state (`gizlang:live`): objects held by World and name, migrated by exact schema keys. Process and thread teardown release only the bindings of that World. |
| `process.c` | Runs an entry. The process `main`, the test runners and the live host share the World, grants, memory limit and leak check. |
| `live.c` | The `giz live` host. A builder builds generations when sources change. A forked host loads a complete immutable function table and publishes it through the execution owner. |
| `compiler_driver.c`, `compiler_transport.c` | The file transport of the compiler executable, linked with `memory.c` and `format.c` (string conversion and search). |
| `wasm.c`, `wasm/`, `wasm.mjs` | The Wasm platform layer (wasm64, no C library). `wasm/` declares the C functions that it defines. `format.c`, `refs.c`, `state.c`, `heap.c`, `resources.c` and `value_layout.c` are built for it. Refer to [Wasm](#wasm). |

### Base64 in C

Base64 stays in C. Three Gizlang versions were measured against the C version (the limit is 1.3 times):

| Date | Gizlang version | Result |
| --- | --- | --- |
| 2026-10-10 | Scalar | 33 ms (encode) and 38 ms (decode) for 30 MB, against 9 ms and 7 ms for C with AVX2. |
| 2026-10-11 | SIMD types | 2.0 times (encode) and 1.8 times (decode) the time of C. `string()` validates its output, and a new buffer starts with zeros. |
| 2026-10-11 | The C algorithm with `shuffle`, `mul_high` and `lookup16` | 1.15 times at 30 MB, but 2.2 times (encode) and 1.4 times (decode) at 64 KB. There, the copy and check of `string()` on output that is ASCII by construction took 13 of the 31 ms of the encoder. |

### Wasm

Besides a process `main`, the Wasm layer runs host calls. Each entry-module function
`export function NAME(input: bytes): bytes` (the table that `compiler/entry.giz` emits) runs
in one World that is kept between calls. Thus `gizlang:live` state persists. `wasm.mjs` is the
host for Node and browsers. It grants only console output.

## Contracts

### One owner for each concern

There is one handle table, one heap for each World and one event loop. Language adapters
never get their own file, socket or scheduler stack.

### Handles, not pointers

Every resource is a handle that is checked by generation and bound to a World and an owner
thread. A stale or foreign handle fails. Pointers never leave the runtime.

### Owned values

- A string, bytes or Vec value is `{address, length}`. A nonzero address points immediately
  after a 16-byte header `{count, capacity}` (`Gizlang_BufferHeader`).
- Generated code owns the counting. It allocates, shares, appends in place when the count is
  1, and frees.
- A negative count marks an immortal buffer. Literals are immortal, and so is any owned value
  that a host passes in. The compiler driver does this for its input.
- Every allocation is charged to `memory_remaining` of the context. Every free returns it.

### Managed memory

[MEMORY](memory.md) owns the model: the owners, the budget, roots and collection.

- Layouts come from the compiler as LLVM constant expressions. Thus there is no second layout
  calculator in C.
- Collection validates every edge before it sweeps.
- A root slot of an expression temporary ends where the temporary ends (`dropTemporaries` in
  `compiler/emit.giz`). An if, while or for condition drops its temporaries before the body runs.
- A dead or foreign handle in a root, hold or traced edge fails collection with `-40207`
  (`GIZLANG_HEAP_ROOT`). This is a defect of the compiler or runtime, not of the Ref that the caller gave.
- Trace callbacks cannot allocate or run user code.
- A `Ref` load gives the caller one more owner of the value's buffers (copy on write). Later
  stores or collections cannot change it. A stale `Ref` fails with `-40208`.
- `GIZLANG_GC_STRESS=1` collects at every `Ref` allocation and store while 64 objects or fewer
  live, and at one in live/64 past that. A `Ref` that generated code holds without a root then
  fails at once. It is the default of `giz test` for programs and suites
  (`GIZLANG_GC_STRESS=0` turns it off). The compiler itself always runs without it. A test pass
  under stress is stored apart from a normal pass.

### Tasks

- The scheduler is in context word 3. It runs on the owner thread.
- Each task keeps its own root chain. While the task is suspended, the chain is parked in the
  heap, so collection traces every task.
- Each task keeps its own stack floor: the end of its stack plus the reserve
  (`gizlang_stack_floor` for the own stack of a thread).
- The box of a task holds its closure, then its result. The layout key of the result is
  checked when the result is taken.
- When a task ends, it cancels and waits for its unfinished children. The process root does
  this before the leak check.

### Pure parallel frames

A pool worker owns a shared computation budget and no World.

- Before dispatch, the initialized capture is checked one time through the same typed transfer
  owner as threads and crossing channels. This includes erased callable environments.
- Nonzero Refs are refused. The check does not borrow the heap of the caller.
- Frame registration alone cannot certify slots that are filled later.
- Vectors without references, including recursive types, are skipped by their immutable
  element layout. The cost of tracing does not increase with their population.
- Retirement requires the original frame and context. Ordinary unbound calls still refuse registration.
- The worker never borrows a World.

Verification:

- Twelve callable-transfer fixtures pass without cached verdicts, with one core and with two
  cores, in an isolated compiler build at a fixed point. They include owned BLOB rows built in
  pure parallel bodies, and owned BLOB temporaries in scalar fill bodies, also with a short
  final chunk.
- Three repeated parallel and fill failures keep fault 4, and a later dispatch in the same
  process succeeds.
- Ordinary recursive copy-on-write page directories keep captured BLOB versions across a
  parallel transfer. A callable that hides a Ref-bearing page payload still refuses with fault 14.
- The checks include rejection of an erased Ref. Compiler SHA-256:
  `3dee5edae234d94386c2ea5019e214c11bb72ff9a49e37e5500e74bdff5daf73`. Runtime archive:
  `711ddf6d94701e2235e864f7276f1801861bb1aef1d6be54e333317d008559a8`.
- `tests/runtime/tasks/owned-blob-kernels` uses the same fixtures as a scenario that always
  runs, at `-O2`. It passed with two-core admission, independently of the `-O0` checks of the
  normal test-file runner. The two-core admission of the runtime was checked independently.
- These fixtures do not prove multicore SQL throughput.

### I/O lifetime

- A buffer lives longer than its operation.
- Cancellation is a request. Only the completion callback releases the storage of the operation.
- Cancellation from a different thread goes through retained tokens and one wakeup queue on
  the owner loop.

### Live code swap

- One publication shows the complete function table.
- Named dispatch and indirect callable invocation pin one table for their execution boundary.
- The scheduler saves the table across suspension, and gives it to spawned tasks and threads.
- Pure parallel workers inherit it without a World and without a change to their allocation budget.
- Dispatch restores the previous token before a fault propagates.
- This contract does not include transactional migration of state or schema, or safe early
  reclamation. Libraries that loaded successfully stay loaded, for their data layouts,
  callbacks and literals.

### Explicit durability

- A write is durable only after `Stream.sync` returns. `Stream.sync` runs fdatasync on the
  thread pool of the loop.
- The file calls of a process (read, write, sync) share one pool of 64 threads (libuv's
  default is 4: four fsyncs that wait stopped every other file call). `UV_THREADPOOL_SIZE` sets
  another size. A process whose cgroup allows fewer than 1024 tasks keeps 4 (`runtime/io.c`).
- A created, renamed or removed entry is durable only after `Dir.sync` of its directory.
- `Dir.syncFilesystem` makes all data written to the file system of a directory durable, with
  the entries of its directories, in one syncfs on the thread pool (sync where there is no
  syncfs). It needs write authority. Use it for many files: 950 kept blobs took 3 s of fsyncs
  4 at a time on backup.
- `readAt`, `writeAt` and `truncate` address a file by offset. `readMany` reads many offsets in one io_uring batch (runtime/uring.c).

### Teardown never guesses

- A World with active calls, pending operations, roots or borrows refuses release, and
  reports the reason.
- It never drains, cancels or rolls back implicitly.
- Nothing undoes completed external effects (files written, bytes sent).

## Limits

| Limit | Value | Fault |
| --- | --- | --- |
| Owned values and heap objects of a World | `GIZLANG_MEMORY_LIMIT` bytes. The default is 80% of the machine's memory, or of its cgroup's `memory.max` when that is lower (4 GiB where neither is known). Tests run with 256 MiB. | `R011` |
| Call depth | 10,000 | `R009` |
| Wall-clock time | No limit. | |

When `main` returns, every buffer must be free. If bytes are still taken, the process entry
reports them on stderr and exits with status 70. Thus each test run is also a leak check.

> **Warning:** Isolation is per process (`SpawnOptions.isolated`, [SYSTEM](system.md)), not
> in the process. Do not run code that you do not trust in the process and call it isolated.
> The compiler, LLVM and the C runtime are trusted. None of them has had an independent
> security review.

## Windows

`x86_64-pc-windows-gnu` (Windows 10 1709+ x64, UCRT) builds with llvm-mingw:
`bin/giz tools/windows/runtime.giz build ARCHIVES WORK OUT` writes `libgizlang_runtime.a`,
`libgizlang_runtime.dll` with its import library `libgizlang_runtime.dll.a`, and `target.o` from pinned archives.
The DLL holds the runtime, libuv, OpenSSL and winpthreads, and imports only Windows' own DLLs (the tool refuses
anything else). The compiler's IR has no target triple and passes only scalars to the runtime, so the same IR is
compiled for Windows. A program links `target.o` and the import library, and finds the DLL beside it or on `PATH`.
There is no native construction yet (`tools/distribution/builder.giz`), so no Windows toolchain release links it.

- One code path: `win32.h` gives the other files the POSIX names with their POSIX meaning (`openat` under a
  directory handle, `unlinkat` and `renameat` with POSIX semantics, `flock`, `posix_spawnp` handing the child only its
  three standard handles); `win32.c` implements them. Paths the runtime returns separate with `/`, and a capability
  path with `\` or `:` is refused (`fs.c valid_path`). TLS trusts the system ROOT store (`org.openssl.winstore://`).
- The task switch also saves `rdi`, `rsi`, `xmm6`–`xmm15` and the thread block's stack bounds (`task.c`).
- Not on Windows (`UV_ENOSYS`, or exit 2 for `GIZLANG_WORKERS`): `exec`, stream pairs, descriptor handoff over Unix
  sockets, the parent lifeline and worker processes. Tests run one after another in the process. Signals are `INT`
  and `TERM` only.
- Error codes: the SDK names Linux numbers (`sdk/core/system.giz`). An error from the runtime goes through
  `osError` (`runtime/system.c gizlang_error_portable`), which maps libuv's codes (`UV_E*`, such as -4058) and the C
  runtime's `-errno` (`ENOTEMPTY` is 41, not 39) to those numbers, so `NOT_FOUND` and the other names match on every OS.
- Host and process facts (memory, CPUs, caches, NUMA nodes, environment, usage), terminals, users, priorities and network
  interfaces come from libuv or the OS API in the runtime (`memory.c`, `machine.c`, `system.c`), not from `/proc` or
  Linux record layouts. User and group ids are -1, and setting them is `UV_ENOSYS`, on Windows.
- Open: before a Windows release ships the DLL, `licenses/third-party` needs its rows: libuv, OpenSSL 3.6.5 (MSYS2
  clang64, not the Linux 3.0.13), winpthreads and the mingw-w64 runtime.
- Checked on the `win11` guest: hello, files,
  directories, rename, removal, refused escapes, temporary folders, sleep, tasks, child processes and TLS to a public host.

## Status

- Source programs reach `heap.c`, `value_layout.c` and `refs.c` through `gizlang:memory`
  (`tests/language/programs/program-memory*.giz`).
- They reach `io.c` and `fs.c` through `gizlang:system`: files, directories, TCP, UDP,
  `Ready`, timers, standard streams and child processes (`tests/runtime/system/`).
