llam
llam is the single-crate Rust binding for the LLAM C runtime.
- Rust crate repository: https://github.com/Feralthedogg/LLAM-rs
- C runtime repository: https://github.com/Feralthedogg/LLAM
- Crate: https://crates.io/crates/llam
- Documentation: https://docs.rs/llam
LLAM-rs gives Rust code Go-style synchronous concurrency on top of LLAM's
stackful task scheduler. You write ordinary blocking-looking Rust code with
tasks, channels, select!, sleeps, socket I/O, mutexes, condition variables,
blocking offload, and diagnostics. Underneath, managed LLAM tasks park
cooperatively so the runtime can keep OS worker threads busy.
No async, no await, no executor handles.
Install
Add the crate:
Or edit Cargo.toml:
[]
= "0.1.2"
Build normally:
By default, the build script downloads and installs the LLAM C SDK into Cargo's
private build output, then links libllam_runtime statically.
Use a preinstalled LLAM SDK instead:
LLAM_SYS_PREFIX="/.local/llam"
Disable automatic installation in locked-down CI:
LLAM_SYS_NO_INSTALL=1 \
LLAM_SYS_PREFIX="/.local/llam" \
Platform Support
| Platform | C runtime backend | Rust support |
|---|---|---|
| Linux x86_64 | io_uring/liburing | Supported |
| Linux aarch64 | io_uring/liburing | Supported |
| macOS arm64 | kqueue | Supported |
| macOS x86_64 | kqueue | Supported |
| Windows x86_64 | IOCP | Supported |
Unix file wrappers are Unix-only. Windows sockets are Winsock SOCKETs, not
POSIX file descriptors.
Runtime Model
Every LLAM-aware operation should run inside llam::run(...) or
llam::run_with_profile(...).
For I/O-heavy applications:
The closure passed to run becomes the first managed task. Tasks spawned from
that closure are scheduled by the LLAM runtime.
Why There Is No #[llam::main]
LLAM-rs intentionally publishes one crate only: llam.
Rust attribute macros require a separate proc-macro crate. Publishing a
#[llam::main] attribute would force an extra crates.io package, so the public
entrypoint is explicit:
This keeps install and import simple:
use llam;
Key Features
- Stackful LLAM tasks from Rust closures.
- Go-like
spawn!andtry_spawn!macros. JoinHandle<T>with typed task results.TaskGroupandTaskBatchhelpers for related work.- Typed bounded channels over LLAM's C channel primitive.
select!over receive, send, close, timeout, and default arms.- LLAM-aware
Mutex<T>andCondvar. - Cooperative sleep and monotonic deadlines.
- Blocking offload for filesystem, CPU, and foreign blocking work.
- TCP, UDP, Unix socket, raw descriptor, and owned-buffer I/O wrappers.
- Task-local storage.
- ABI/runtime diagnostics.
- Raw C ABI access through
llam::sysfor advanced integration.
Minimal Example
Tasks
Use spawn! when spawn failure should panic:
Use try_spawn! when spawn failure should be returned:
Tune task class and stack size:
Detach a fire-and-forget task:
Yield explicitly:
yield_now;
Inspect the current managed task:
println!;
println!;
println!;
Task Groups
Use TaskGroup when all child tasks must finish before the scope continues:
Use TaskBatch<T> when each task returns a typed result:
Channels
Channels are typed and bounded. Values are moved into LLAM and moved back out on receive.
Close-aware receive:
Timeouts and nonblocking attempts:
use Duration;
Useful channel methods:
tx.send
tx.try_send
tx.send_timeout
tx.close
rx.recv
rx.try_recv
rx.recv_timeout
rx.recv_option
rx.try_recv_option
rx.recv_timeout_option
recv() returns EPIPE on close. recv_option() returns Ok(None) on close.
Select
llam::select! waits on multiple channel operations with Go-like syntax.
Receive with timeout:
use Duration;
Closed-channel arm:
let done = select! ;
Send arm:
let selected = select! ;
println!;
Default arm:
let ready = select! ;
select! accepts either one after(...) arm or one default arm, not both.
Time
Sleep cooperatively:
sleep?;
Use absolute LLAM deadlines:
let deadline = deadline_after;
sleep_until?;
Read the monotonic nanosecond clock:
let now = now_ns;
println!;
Mutex And Condvar
Use LLAM-aware synchronization primitives for waits inside managed tasks.
Condition variable with a predicate loop:
use Arc;
Like standard condition variables, wait in a predicate loop.
Blocking Work
Use llam::blocking::call for work that should run outside the cooperative
scheduler path.
Use BlockingRegion for manual enter/leave around foreign blocking code:
let _region = enter?;
// Call a blocking C API here.
TCP Echo Server
This is the typical LLAM-rs shape: synchronous-looking accept, read, and
write_all, with one LLAM task per connection.
use ;
TCP Client
use ;
UDP
Connected UDP sockets use LLAM's read/write path.
Raw I/O
llam::io exposes LLAM-aware descriptor/socket operations for integrations
that do not want the higher-level network wrappers.
let mut buf = ;
let n = read?;
let written = write?;
let revents = poll_fd?;
println!;
Owned buffers:
if let Some = read_owned?
EOF or zero-byte reads return Ok(None) for owned-buffer reads.
Files
Unix file wrappers are available through llam::fs::File.
Task-Local Storage
Task-local values are scoped to the current managed LLAM task.
If task-local values own resources, call take() or clear() before task exit.
The C runtime stores raw pointers and does not provide a destructor hook.
Diagnostics
ABI information:
let abi = load?;
println!;
println!;
println!;
println!;
Runtime stats:
Write stats JSON on Unix:
Build Environment
| Variable | Meaning |
|---|---|
LLAM_SYS_PREFIX |
Installed LLAM SDK prefix. Must contain include/ and lib/. |
LLAM_SYS_LIB_DIR |
Directory containing libllam_runtime. |
LLAM_SYS_INCLUDE_DIR |
Include directory to use with LLAM_SYS_LIB_DIR. |
LLAM_SYS_LIB_NAME |
Link name. Default: llam_runtime. |
LLAM_SYS_LINK_KIND |
Cargo link kind. Default: static. |
LLAM_SYS_INSTALL_PREFIX |
Override the automatic install prefix. |
LLAM_SYS_INSTALL_VERSION |
LLAM release version. Default: 1.0.0. |
LLAM_SYS_INSTALL_TARGET |
Explicit release target. Examples: macos-aarch64, macos-x86_64, linux-x86_64, linux-aarch64, windows-x86_64. |
LLAM_SYS_INSTALL_BASE_URL |
Release asset base URL. |
LLAM_SYS_INSTALL_SCRIPT |
Local path or URL for install.sh / install.ps1. |
LLAM_SYS_FORCE_INSTALL=1 |
Reinstall even if the build prefix already looks valid. |
LLAM_SYS_NO_INSTALL=1 |
Do not run the installer. Require LLAM_SYS_PREFIX or LLAM_SYS_LIB_DIR. |
Examples
Examples are Cargo targets, not standalone rustc inputs:
LLAM_CHAT_QUIET=1
The chat server mirrors the C LLAM chat server shape: each client gets a bounded
outbox channel, reader/writer tasks run per connection, full input lines are
broadcast as [client N] ..., and slow receivers shed queued messages instead
of blocking global fanout.
Checks
From a LLAM-rs checkout:
Bench and stress helpers:
Troubleshooting
The build downloads LLAM every time
Set a stable install prefix:
LLAM_SYS_INSTALL_PREFIX="/.local/llam"
Or install LLAM once and reuse it:
LLAM_SYS_PREFIX="/.local/llam"
Automatic install is not allowed in CI
Provide an SDK and disable automatic install:
LLAM_SYS_NO_INSTALL=1 \
LLAM_SYS_PREFIX="/.local/llam" \
Cross-building
Automatic installation is host-oriented. For cross builds, provide a matching prebuilt SDK:
LLAM_SYS_INCLUDE_DIR="/path/to/target/include" \
LLAM_SYS_LIB_DIR="/path/to/target/lib" \
Dynamic linking
Static linking is the default:
LLAM_SYS_LINK_KIND=static
Dynamic linking requires a matching dynamic LLAM runtime discoverable by the loader at runtime:
LLAM_SYS_LINK_KIND=dylib LLAM_SYS_PREFIX="/.local/llam"
Safety
llam::sys is raw and unsafe by design. The safe llam layer owns heap
payloads across channels, task trampolines, task-local values, and owned I/O
buffers. Direct C handles are hidden behind RAII wrappers where the C API
provides a lifetime contract.
See the repository SAFETY.md for the unsafe boundary audit.
License
Apache-2.0