zigroutines — explicit stackful M:N concurrency for Zig: Go-shaped tasks, channels, and select, with a Runtime you construct yourself.
| Version | 1.0.0 (build.zig.zon · zr.version) |
| License | LGPL-3.0-or-later (LICENSE) |
| Platforms | x86_64 / aarch64 · Windows, Linux, macOS, FreeBSD |
| Zig | 0.16 and 0.17-dev |
zig build test
zig build bench
zig build run
zig build examples
zig build example -Dexample=01_minimal_spawnzigroutines is a library for explicit stackful concurrency in Zig. It gives you a Go-shaped model (tasks, channels, select, cancellation, timers) with Zig’s philosophy: nothing global, nothing hidden, pay only for what you enable.
Unlike a “runtime baked into the language,” you construct a Runtime, choose the scheduler policy, I/O backend, and metrics. Stacks are fixed 2 KiB (no growth, no per-task dial). No GC. No netpoller until you plug one in.
At a glance:
| Property | Value |
|---|---|
| Model | M:N stackful tasks on fixed 2 KiB stacks (no growth, no per-task size choice) + opt-in leaf (stackless run-to-completion) |
| Schedulers | FIFO · work-stealing · priority · 1 OS thread per task |
| CSP | Channel(T), rendezvous / buffered, multi-arm select (up to 32 arms per kind) |
| Cancellation | Cooperative, hierarchical CancelToken, Scope / Nursery |
| I/O | Plugin: none / poll / iocp / io_uring |
| Synchronization | Mutex (adaptive), Semaphore, RwLock, RateLimiter, Notify, Watch(T) — park the task, not the OS thread |
| Timers | Hierarchical wheel (256×1 ms) + heap overflow |
| Cost | Pay-for-what-you-use; stack pool + task freelist on; channel createPooled for hot create |
-
Write top-down “sync-looking” code —
send/recv/sleep/yieldlook like ordinary calls; under the hood the task parks and yields the CPU to other fibers. No function coloring (async/awaitdoes not infect the call tree). -
Predictable resources — every fiber has a fixed 2 KiB stack (no size dial). Heavy buffers go on the heap. Stack pool on by default. Optional guard page / canary.
-
Swap scheduling policy without changing the API — the same
spawnworks on FIFO (1 worker), work-stealing (many cores), priority, and thread-per-task. -
Go-style CSP — channels, rendezvous, backpressure policies (
block,drop_newest,drop_oldest,error_full),selectwith timeout and cancel. -
Structured concurrency —
Scope/Nurserywith deadline,cancel_on_leave,cancel_on_first_done, typedspawnResult+JoinHandle. -
Pluggable I/O — I/O off by default (the core stays quiet); when needed — poll / IOCP / io_uring, TCP/UDP helpers, bridge to
std.Io. -
Observability — optional atomic metrics, tracing hooks, preemption checkpoints.
-
Actors and C ABI —
Actor(Message)(mailbox = channel + loop), optional C surface.
| libxev | zigcoro / zio | zigroutines | |
|---|---|---|---|
| Primary job | Event loop / completions | Coroutines + some I/O | Full concurrency stack |
| Stackful sync style | No (callback / future) | Yes | Yes |
| Channels + multi-select | No | Partial / ad-hoc | First-class CSP |
| Pluggable scheduler policies | N/A | Limited | FIFO / WS / priority / 1:1 |
| Fixed stack + pool + guard | N/A | Varies | Explicit and default |
| Structured cancel / nursery | No | Limited | Scope + Nursery |
| Quiet netpoller by default | You wire it | Often coupled to the runtime | Off until you configure it |
| Go | C++20 / fiber libs | async Rust + Tokio | zigroutines | |
|---|---|---|---|---|
| DX | Excellent (go, chan, select) |
Assemble the pieces | async/await, Pin/Send |
Same shape, native Zig |
| Runtime | Hidden M:N | Zoo of executors | Multiple runtimes | Explicit Runtime |
| Stack | Grows (copy) | Stackless or 3rd-party fibers | Stackless state machines | Fixed only |
| GC | Yes, STW risk | No | No | No |
| Netpoller | Built-in, not optional | Whatever you build | Usually in the runtime | Off by default |
| Function coloring | No | co_await |
async everywhere |
No |
Go wins “ship a service in an afternoon.” zigroutines wins when you cannot accept GC, silent stack growth, or an immovable runtime. C++ can match the control if you are an expert and assemble the stack yourself. Tokio is world-class for Rust; on Zig, zigroutines keeps the mental model “task + stack + channel” without async coloring.
- One coherent surface — scheduler, I/O, cancel, CSP, stacks, metrics: policies you enable, not a hidden runtime.
- Honest to Zig — no process-global runtime, no GC, no silent growth; pay only for enabled features.
- Go-shaped DX — write top-down; park on channels/timers/I/O without rewriting the call tree.
- Measurable cost — fixed stacks, optional metrics/canary, explicit park/wake rules.
Not the best choice for “absolute max pps on one echo path” (a specialized loop may win) or full BEAM/OTP-style supervision. Best choice for one concurrency stack on Zig that does not betray the language.
Package version: 1.0.0. Repository: Apanazar/zigroutines.
Pin a release (hash is filled by zig fetch --save):
zig fetch --save=zigroutines https://github.com/Apanazar/zigroutines/archive/refs/tags/v1.0.0.tar.gzOr from a local checkout:
zig fetch --save=zigroutines ./path/to/zigroutinesThen in build.zig.zon you will have something like:
.{
.name = .my_app,
.version = "0.0.1",
.dependencies = .{
.zigroutines = .{
.url = "https://github.com/Apanazar/zigroutines/archive/refs/tags/v1.0.0.tar.gz",
.hash = "…", // written by zig fetch --save
},
},
.paths = .{""},
}Wire the module in build.zig:
const zr_dep = b.dependency("zigroutines", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("zigroutines", zr_dep.module("zigroutines"));const zr = @import("zigroutines");
var rt = try zr.Runtime.init(allocator, .{ .workers = 1 });
defer rt.deinit();
_ = try rt.spawn(.{}, myFn, .{});
try rt.run(); your code (sync-looking style)
spawn · channel · select · nursery · sleep
│
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌────────────┐
│ Task │◄──park/── │ Channel │ │ Scope / │
│ (fiber) │ wake │ + select │ │ Nursery │
└────┬────┘ └──────────┘ └─────┬──────┘
│ │
▼ ▼
┌──────────────────────────────────────────────────────────┐
│ Runtime (you construct it - not process-global) │
│ scheduler · timers · metrics · cancel · I/O backend │
└──────────────┬───────────────────────────┬───────────────┘
│ │
┌────────┴────────┐ ┌────────┴────────┐
▼ ▼ ▼ ▼
FIFO / work-steal fixed poll / IOCP / std.Io
priority / 1:1 stacks io_uring adapter
+ pool
Defaults: 1 worker · FIFO · fixed 2 KiB stacks · stack pool on · task freelist on · I/O off · metrics off · preemption off. (Channel recycle is opt-in via createPooled so test allocators stay leak-free.)
- Application layer — your functions invoked via
Runtime.spawn/spawnResult/Nursery.spawn/Actor. - CSP / sync —
Channel,select, Mutex/Semaphore/…: on block, callparkFromRunningon the current executor. - Task — unit of execution: id, state, fixed stack, context (registers), join-waiters, optional result slot.
- Scheduler backend — ready queue, worker↔task context swap, live/dead accounting, timer/I/O poll in idle.
- Runtime — facade: config, backend creation, stack pool, timer queue, I/O ownership, TLS “current runtime”.
- Context / arch — assembly stack switch (x86_64 / aarch64).
- Utils — lock-free / intrusive structures (Chase-Lev deque, ring queue, lists).
Below is a full catalog of “what exists in the system” and where the code lives.
| Entity | Role | Files |
|---|---|---|
| Runtime | Owns scheduler, timers, optional I/O/metrics; API init / spawn / run / sleep |
src/core/runtime.zig |
| Config | workers, policy, stack options, io, metrics, preempt | src/core/runtime.zig |
| Task | Fiber: stack, context, state machine, join | src/core/task.zig |
| JoinHandle / TypedJoinHandle | Wait for completion; typed result | src/core/task.zig |
| Executor | VTable: enqueue, yield, park, finish | src/core/executor.zig |
| yield / current / sleep | Cooperative CPU yield; TLS current task; sleep via timers | task.zig, runtime.zig |
Rules: yield / channel / sync are allowed only inside a task. Outside a task — panic with a clear message. sleep requires a TLS runtime (set by run).
| Policy | Behavior | Files |
|---|---|---|
| auto | 1 worker → FIFO; ≥2 → work-stealing | runtime.zig |
| single_thread_fifo | One queue, one thread, strict FIFO | src/scheduler/fifo_scheduler.zig |
| work_stealing | N workers, local deques, steal | src/scheduler/work_stealing_scheduler.zig + utils/chase_lev.zig |
| priority | 1 worker, min-heap / priority queue by SpawnOptions.priority (0 = highest) |
src/scheduler/priority_scheduler.zig + utils/priority_queues.zig |
| thread_per_task | 1 OS thread per task (1:1), for comparison / blocking FFI | src/scheduler/thread_per_task_scheduler.zig |
| Shared facade | src/scheduler/scheduler.zig |
| Mechanism | Role | Files |
|---|---|---|
| Stack | Fixed buffer; usable region | src/stack/stack.zig |
| Pool | Size-class reuse across tasks | stack.zig |
| guard_page | PROT_NONE / PAGE_NOACCESS under the stack → fault on overflow | stack.zig |
| paint_canary | Pattern fill; highWaterUsed() |
stack.zig |
Rule: the stack does not grow and is not sized per task — every stackful fiber is fiber_stack_size (2 KiB). Overflow is a guard-page fault (if enabled) or silent corruption. Keep deep frames and large buffers on the heap; use spawnLeaf for run-to-completion work with no private stack.
| Entity | Role | Files |
|---|---|---|
| Context | Saved registers + entry trampoline | src/context/context.zig |
| x86_64 / aarch64 asm | make / swap |
src/context/arch/x86_64_assembly.zig, aarch64_assembly.zig |
| supported | comptime flag for the target platform | context.zig |
| Entity | Role | Files |
|---|---|---|
| Channel(T) | Buffered or rendezvous (capacity 0); MPMC | src/csp/channel.zig |
| FullPolicy | block · drop_newest · drop_oldest · error_full |
channel.zig |
| send / recv / try* | Blocking and non-blocking ops | channel.zig |
| select.multi / recv / recvAny | Up to 32 recv + 32 send arms; timeout; cancel; default | src/csp/select.zig |
| Fairness | random (default) / fifo when several arms are ready |
select.zig |
Scenarios: pipeline, fan-in/fan-out, rendezvous handoff, backpressure telemetry (drop_*), non-blocking poll (try / default arm).
| Entity | Role | Files |
|---|---|---|
| CancelToken | Atomic flag, parent→children link, waiters | src/core/cancellation.zig |
| Scope | Group of children + token; joinAll; optional cancel-on-leave |
src/core/structured_concurrency.zig |
| Nursery | Scope + deadline/timeout + cancel_on_first_done + tryJoin |
structured_concurrency.zig |
Cooperative rule: cancel does not interrupt machine code. Children must poll the token (or wait on select/channel with cancel). A deliberate “explicit over implicit” choice.
| Entity | Role | Files |
|---|---|---|
| TimerQueue | Near-term wheel (256×1 ms) + min-heap for long deadlines; sleep, fireExpired |
src/core/timer_queue.zig |
| Integration | Scheduler in idle fires expired timers | fifo_scheduler.zig, work_stealing_scheduler.zig, … |
| Primitive | Behavior | Files |
|---|---|---|
| SpinLock | Short spin + OS yield | src/core/synchronization.zig |
| ParkingLot | Park/wake a waiter list | synchronization.zig |
| Semaphore / Mutex | Atomic/CAS uncontended; multi-worker short spin; else park | synchronization.zig |
| RwLock | Shared/exclusive; writer preference | synchronization.zig |
| RateLimiter | Token bucket; sleep + wake waiters | synchronization.zig |
| Notify | One/all wake of parked tasks | synchronization.zig |
| Watch(T) | Broadcast latest value + version | synchronization.zig |
Invariant: blocking does not hold an OS worker in a useless wait (except a short multi-worker spin). Single-worker runtimes never spin on mutex/sem (would starve the holder).
| Entity | Role | Files |
|---|---|---|
| Backend (vtable) | poll / register / readiness | src/io/io_backend.zig |
| PollReactor | Linux epoll · Windows select · POSIX poll; cancelAll |
src/io/poll_reactor.zig |
| IocpBackend | Windows IOCP | src/io/iocp_backend.zig |
| IoUringBackend | Linux io_uring | src/io/io_uring_backend.zig |
| MockBackend | Tests without a real network | src/io/mock_backend.zig |
| TcpListener / TcpStream / UdpSocket | Fiber-friendly sockets; park on readiness | src/io/network.zig |
| Backend.cancelAll | Drop all I/O waiters with Closed (hang safety) |
src/io/io_backend.zig, backends |
| IoAdapter | Bridge to std.Io (async/await-style I/O from a fiber) |
src/io/std_io_adapter.zig, async_future.zig |
| Re-exports | src/io/io.zig |
| Entity | Role | Files |
|---|---|---|
| Actor(Message) | Mailbox channel + loop; optional link/on_stop | src/actors/actor.zig |
| Metrics | Atomic counters: spawns, yields, parks, steals, … | src/core/metrics.zig |
| Preemption / checkpoint | Cooperative quantum yield | src/core/preemption.zig |
| Tracing / RingTracer | Opt-in event emit | src/core/tracing.zig |
| C bindings | Foreign surface | src/abi/c_bindings.zig |
| Structure | Role | Files |
|---|---|---|
| Chase-Lev deque | Work-stealing queues | src/utils/chase_lev.zig |
| Intrusive list | Wait queues (channel, sync, join) | src/utils/intrusive_list.zig |
| Ring queue | FIFO ready queue | src/utils/ring_queue.zig |
| Priority queues | Priority scheduler | src/utils/priority_queues.zig |
| task_wake / worker_wake | Safe wake with on_cpu spin | src/utils/task_wake.zig, worker_wake.zig |
| windows_api | Windows helpers | src/utils/windows_api.zig |
- Park/wake: waiter is marked
parkedunder the lock beforeparkFromRunning; the waker waits for!on_cpuand only thenenqueue. - Channel close: all send/recv waiters get
Closedand are woken. - Destroy channel: assert empty wait queues (no dangling parked waiters).
- Runtime deinit: drain pool, destroy I/O, collect unjoined dead tasks.
- Cancellation: hierarchical, but cooperative.
Full runnable files live under examples/. Build all:
zig build examplesOne example:
zig build example -Dexample=01_minimal_spawn
zig build example -Dexample=04_work_stealing| File | Demonstrates |
|---|---|
examples/01_minimal_spawn.zig |
Runtime, spawn, yield |
examples/02_channel_pipeline.zig |
Buffered channel pipeline |
examples/03_select_timeout.zig |
select + timeout |
examples/04_work_stealing.zig |
WS scheduler, spawnResult, metrics |
examples/05_nursery_cancel.zig |
Nursery deadline + cooperative cancel |
examples/06_actor_mailbox.zig |
Actor mailbox |
examples/07_sync_and_backpressure.zig |
Mutex/Semaphore + drop_oldest |
examples/08_priority_scheduler.zig |
Priority order |
const std = @import("std");
const zr = @import("zigroutines");
pub fn main() !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer _ = gpa.deinit();
const alloc = gpa.allocator();
var rt = try zr.Runtime.init(alloc, .{ .workers = 1 });
defer rt.deinit();
_ = try rt.spawn(.{}, struct {
fn hello(name: []const u8) void {
std.debug.print("hello from {s}\n", .{name});
zr.yield();
}
}.hello, .{"A"});
try rt.run();
}const Ch = zr.Channel(usize);
const ch = try Ch.create(alloc, 16);
defer ch.destroy();
_ = try rt.spawn(.{}, struct {
fn producer(c: *Ch, n: usize) void {
var i: usize = 0;
while (i < n) : (i += 1) c.send(i) catch return;
c.close();
}
}.producer, .{ ch, 5 });
_ = try rt.spawn(.{}, struct {
fn consumer(c: *Ch) void {
while (true) {
const v = c.recv() catch break;
std.debug.print("got {d}\n", .{v});
}
}
}.consumer, .{ch});
try rt.run();var rt = try zr.Runtime.init(alloc, .{
.workers = 4,
.policy = .work_stealing,
.stack_pool = true,
.metrics = true,
});
const h = try rt.spawnResult(.{}, add, .{ 20, 22 });
const sum = h.join();const r = zr.select.recv(u32, ch, .{
.timeout_ns = 1 * std.time.ns_per_ms,
.timers = &rt.timers,
});
switch (r) {
.value => |v| ...,
.timeout => ...,
.closed => ...,
.canceled => ...,
}var nursery = zr.Nursery.init(rt, .{
.timeout_ns = 10 * std.time.ns_per_ms,
.cancel_on_leave = true,
});
defer nursery.deinit();
_ = try nursery.spawn(.{}, child, .{nursery.token()});
_ = nursery.join() catch |err| { ... };zig build test| Layer | Files | Cases |
|---|---|---|
| Unit | stack_pool, channel_lifecycle, cancellation_token, metrics, preemption, stack_guard_and_canary, synchronization, rwlock_exclusive, actor_lifecycle |
alloc/free, policies construct, cancel flag, metrics on/off, checkpoint, canary/guard, mutex/sem/park, exclusive RwLock, actor destroy/cancel |
| Integration | context_switch, fifo_scheduler, work_stealing_scheduler, channel_messaging, select_cancellation_scope, advanced_features, drop_newest_and_nursery |
swap/deep stack, spawn/yield, multi-worker, buffered/rendezvous/close/try/mpmc, select timeout/cancel/recvAny, multi-select, drop_*, nursery timeout/tryJoin/cancel_on_first_done, spawnResult error, priority, thread_per_task, actor, tracer, I/O backend create |
| I/O | mock_and_poll_backend, std_io_adapter, tcp_echo |
reactor, mock park/wake, IoAdapter, TCP echo loopback, UDP loopback, cancelAll watchdog |
| Stress | multi_worker_channels |
WS pipeline, spawnResult, priority order |
Covered and important:
- Context switch (including deep stack)
- FIFO / WS / priority / thread_per_task
- Channel: buffered, rendezvous, close, try, mpmc, drop_oldest, drop_newest, error_full
- Select: timeout, value, recvAny, cancel, multi + default
- Hierarchical cancel, Scope join, Nursery timeout / tryJoin / cancel_on_first_done
- Sync: mutex, semaphore handoff, rwlock shared+exclusive, rate limiter
- Stack pool, guard, canary
- Metrics, preemption, tracing
- Actor lifecycle + cancel
- spawnResult / error union
- I/O mock, poll adapter; create IOCP/io_uring backends
- TCP echo loopback (poll backend, select-based readiness on Windows) + UDP loopback
Backend.cancelAll— safe abort of stuck I/O waiters (tests / shutdown)- Multi-arm select without double-enqueue (idempotent schedule)
Intentionally weaker (future hardening):
- Exhaustive multi-thread races on every primitive (mpmc + stress exist, not model checking)
- Full e2e echo over IOCP / io_uring (create/destroy +
supports_asynccovered; round-trip on poll) - C ABI integration tests
- RateLimiter multi-waiter fairness under load
| Peer | Product | Measured in harness |
|---|---|---|
| zigcoro | thin stackful fibers + executor Channel | bounce, n_tasks_1k/10k, chan pipeline / cap-1 |
| libxev | event loop (not fibers) | timer 100k, async notify, TCP/UDP ping |
| zio | Runtime + Channel + sync | yield, spawn, nursery≈Group, channels, mutex/sem/rwlock excl, sleep |
Host: Windows x86_64 · 12 CPUs
| Workload | zigroutines | Go | Rust/Tokio | C++ | zigcoro | libxev | zio |
|---|---|---|---|---|---|---|---|
ctx_switch_bounce |
4.5 ns | n/a | n/a | n/a | 16.4 ns | n/a | n/a |
yield_pingpong |
6.7 ns | 97.2 ns | 357 ns | 45.9 ns | n/a | n/a | 8.9 ns |
yield_single |
49 ns | 175 ns | 32 ns | 99 ns | n/a | n/a | 4.9 ns |
yield_ws_4w |
127 ns | 234 ns | 177 ns | 66 ns | n/a | n/a | n/a |
leaf_spawn_batch |
300 ns | n/a | n/a | n/a | n/a | n/a | 170 ns |
spawn_join |
209 ns | 800 ns | 1678 ns | ~97k | n/a | n/a | 620 ns |
spawn_result_join |
15k ns | 800 ns | 1459 ns | ~94k | n/a | n/a | n/a |
nursery_join |
864 ns | 1500 ns | 685 ns | ~46k | n/a | n/a | 184 ns (Group) |
priority_dispatch |
188 ns | 400 ns | 805 ns | ~96k | n/a | n/a | n/a |
skynet_join_10k |
14k ns | 900 ns | 43k | 48k | n/a | n/a | n/a |
n_tasks_1000 |
30 ns | 300 ns | 74 ns | 2224 | 28 ns | n/a | 41 ns |
n_tasks_10000 |
69 ns | 400 ns | 71 ns | 2215 | 104 ns | n/a | 36 ns |
n_tasks_50000 |
97 ns | 477 ns | 67 ns | n/a | skipped | n/a | skipped |
| density / stack | 2 KiB fixed | growable | stackless | OS thr | 4 KiB default | n/a | growable |
| Workload | zigroutines | Go | Rust | C++ | zigcoro | libxev | zio |
|---|---|---|---|---|---|---|---|
chan_pipeline_buf256 |
16 ns | 67.7 | 167 | 195 | ~3 ns (executor Chan) | n/a | 36.7 ns |
chan_rendezvous |
86 ns | 340 | 508 | 24k | 68 ns (cap-1) | n/a | 86.9 ns |
chan_mpmc_4x4 |
181 ns | 100 ns | 96 | 287 | n/a | n/a | skipped |
chan_try_uncontended |
15.4 ns | 60 | 34.5 | 38.9 | n/a | n/a | 31.9 ns |
chan_create_buf8 cold |
6.5k | 140 | 282 | 168 | n/a | n/a | n/a |
chan_create_buf8_pooled |
34.3 ns | 140 | 282 | 168 | n/a | n/a | n/a |
chan_closed_drain |
22.8 | 20 | 18.2 | 22.3 | n/a | n/a | 15.4 ns |
chan_prodcons_work |
24.6 ns | 220 | 32.7 | 278 | n/a | n/a | skipped |
chan_popular_256 |
87.5 ns | 618 | 2015 | 192k | n/a | n/a | n/a |
chan_sem |
11.8 ns | 40 | 27.2 | 43 | n/a | n/a | n/a |
actor_mailbox |
15.4 ns | 100 | 22.1 | 164 | n/a | n/a | n/a |
| Workload | zigroutines | Go | Rust/Tokio | C++ | zigcoro | libxev | zio |
|---|---|---|---|---|---|---|---|
select_fanin_2 |
47.5 ns | 220 | 234 | 257 | n/a | n/a | skipped |
select_uncontended |
20.7 ns | 116 | 155 | 48.5 | n/a | n/a | skipped |
select_nonblock |
18.9 ns | 85 | 25.3 | 34.5 | n/a | n/a | skipped |
select_sync_contended |
75.4 ns | 367 | 283 | 307 | n/a | n/a | skipped |
| Workload | zigroutines | Go | Rust | C++ | zigcoro | libxev | zio |
|---|---|---|---|---|---|---|---|
mutex_uncontended |
15 ns | 15.0 | 16.0 | 16.5 | n/a | n/a | 14.3 ns |
mutex_contended_4 |
537 ns | 30 | 128 | 46 | n/a | n/a | skipped |
sem_handoff |
17.7 ns | 80 | 95.5 | 149 | n/a | n/a | 48.1 ns |
rwlock_shared_4 |
152 | 40 | 131 | 643 | n/a | n/a | skipped |
rwlock_exclusive |
16.6 ns | 30 | 15.5 | 96.8 | n/a | n/a | 27.7 ns |
timer_sleep_batch |
~500 ns | 804 | 5105 | 68k | n/a | n/a | 2060 ns |
timer_many_100k_dispatch |
~670 ns | 558 | 811 | 214k ‖ | n/a | 1000 ns | n/a |
| Workload | zigroutines | Go | Rust | C++ | zigcoro | libxev | zio |
|---|---|---|---|---|---|---|---|
tcp_pingpong |
~100k RT/s | n/a | n/a | n/a | n/a | ~68k RT/s · 14.7 µs/RT | skipped |
udp_ping |
~350k pkt/s | n/a | n/a | n/a | n/a | ~204k pkt/s · 4.9 µs/pkt | skipped |
var rt = try zr.Runtime.init(alloc, .{
.workers = 4, // 0 = CPU count
.policy = .work_stealing, // auto | single_thread_fifo | work_stealing | priority | thread_per_task
.stack_pool = true, // 2 KiB stack freelist (default)
.task_freelist = true, // recycle Task control blocks (default)
.stack_guard_page = false, // opt-in overflow fault
.stack_paint_canary = false, // opt-in high-water measurement
.io = .none, // none | poll | iocp | io_uring
.metrics = false,
.preempt = .{ .enabled = false, .quantum_ns = ... },
});
// Fixed 2 KiB stack for all stackful tasks
_ = try rt.spawn(.{}, work, .{});
// Opt-in stackless leaf (no private stack; must run to completion — no park/yield/channel):
_ = try rt.spawnLeaf(.{}, pureCompute, .{});Optional features (all opt-in):
Runtime.workers / policy : multi-core WS, priority, 1:1
Runtime.io : poll | iocp | io_uring
Runtime.stack_pool : reuse
Runtime.stack_guard_page : overflow fault
Runtime.stack_paint_canary : high-water scan
Runtime.metrics : counters
Runtime.preempt / checkpoint : cooperative quantum
Channel full_policy : block | drop_newest | drop_oldest | error_full
select timeout / cancel : timer + CancelToken
Scope / Nursery : structured join, deadline
spawnResult : typed JoinHandle
TcpListener / TcpStream / UdpSocket
IoAdapter (std.Io)
Actor(Message)
C ABI / tracing hooks