Skip to content
v1.0.0-zig0.15.2

Introduction

Volt is an async I/O runtime for Zig. It provides a work-stealing task scheduler, synchronization primitives, channels, networking, filesystem, timers, and process management — everything you need to build concurrent servers and services, in one package.

Volt brings the same architecture that powers Tokio in Rust to Zig, adapted for Zig’s value semantics and comptime specialization. Zero-allocation waiters, lock-free channels, and O(1) worker waking make it fast. An explicit Io handle (no hidden globals) makes it predictable. It is the I/O counterpart to Blitz (CPU parallelism), the same way Tokio complements Rayon.

const volt = @import("volt");
pub fn main() !void {
try volt.run(serve);
}
fn serve(io: volt.Io) void {
var listener = volt.net.listen("0.0.0.0:8080") catch return;
defer listener.close();
while (listener.tryAccept() catch null) |conn| {
_ = io.@"async"(echo, .{conn.stream}) catch continue;
}
}
fn echo(stream: volt.net.TcpStream) void {
var s = stream;
defer s.close();
var buf: [4096]u8 = undefined;
while (true) {
const n = s.tryRead(&buf) catch return orelse continue;
if (n == 0) return;
s.writeAll(buf[0..n]) catch return;
}
}

Work-Stealing Scheduler

LIFO slot + local ring buffer + global injection queue. Cooperative budgeting (128 polls/tick), O(1) bitmap worker waking, and EWMA-based adaptive scheduling. Spawn thousands of tasks across all cores without manual thread management.

Zero-Allocation Primitives

Mutex, RwLock, Semaphore, Barrier, Notify, OnceCell — waiters are embedded directly in futures on the stack. No heap allocation per contended wait. Every primitive offers tryX() (non-blocking, no runtime needed) and x(io) (async, requires runtime).

Lock-Free Channels

MPSC/MPMC (Vyukov ring buffer), Oneshot, Broadcast, Watch, and Select. Bounded channels provide backpressure. 48 B/op total vs 217 B/op in Tokio across channel benchmarks.

Cross-Platform I/O

io_uring (Linux), kqueue (macOS), IOCP (Windows), epoll (fallback) — auto-detected at startup. TCP, UDP, Unix domain sockets, filesystem, process spawning, and signal handling.

Timers and Timeouts

Nanosecond-precision Duration and Instant types. Hierarchical timer wheel, async Sleep, recurring Interval, and Deadline/Timeout combinators. Wrap any operation with a timeout in one line.

Lightweight Tasks

Each task is a state machine at ~256-512 bytes, not a coroutine stack (16-64 KB). Predictable memory, cache-friendly layout, millions of concurrent tasks. Same tradeoff Tokio made — stackless wins where density matters.

FeatureVoltzioZig std.Io (in dev)
Work-stealing schedulerYes (Tokio-style)Multi-threaded schedulerNo (OS threads)
Sync primitives6 types, zero-allocYes, incl. channelsMutex, Condition
Channels4 types + SelectYesQueue (MPMC bounded)
Platform backendsio_uring, kqueue, IOCP, epollio_uring, kqueue, IOCP, epollThreaded + proof-of-concept io_uring, kqueue
Cooperative budgetingYes (128 polls/tick)NoNo
Blocking poolYes (auto-scaling, 512 threads)NoN/A (threaded model)
Graceful shutdownBuilt-in signal handlingCancellation supportFirst-class cancellation
Coroutine modelStackless by choice (~256-512 B/task)Stackful (growable stacks)Explicit I/O interface

Every async primitive provides two access patterns, and they have different runtime requirements:

// Tier 1: No runtime needed -- works anywhere, even in main()
var mutex = volt.sync.Mutex.init();
if (mutex.tryLock()) {
defer mutex.unlock();
// critical section
}
// Tier 2: Requires the runtime (volt.run or Io.init)
mutex.lock(io); // blocks this task until acquired
defer mutex.unlock();

The tryX() tier (tryLock, tryAcquire, trySend, tryRecv) and all filesystem/networking operations work without starting a runtime. Use them in CLI tools, scripts, or libraries that don’t need async. The convenience tier (mutex.lock(io), ch.send(io, val), sem.acquire(io, n)) requires the io: volt.Io handle — the type system enforces this at compile time, so you can’t accidentally call async APIs without a runtime. See Basic Concepts for the full breakdown.

All measurements on Apple M3 Pro, nanoseconds per operation, 4 worker threads.

BenchmarkVoltTokioWinner
Channel send11.1 ns16.3 nsVolt +1.5x
Channel recv11.4 ns22.3 nsVolt +2.0x
Channel roundtrip23.1 ns37.8 nsVolt +1.6x
Channel MPMC (4P + 4C)73.3 ns132.8 nsVolt +1.8x
Oneshot27.1 ns51.5 nsVolt +1.9x
Broadcast (4 recv)95.2 ns143.8 nsVolt +1.5x
Watch45.7 ns145.4 nsVolt +3.2x

Overall: Volt wins 16 of 21 benchmarks. Tokio wins 4 (mutex uncontended, OnceCell get, spawn+await, blocking spawn), 1 tie. Memory: 284 B/op total vs Tokio’s 1,868 B/op — 6.6x less. Allocations: 3.0/op vs 17.1/op. Volt’s architecture is derived from Tokio — we would not be here without their work. See Comparing with Tokio for methodology and full results.

ModuleDescription
volt.IoRuntime handle: @"async", concurrent, passed explicitly like Allocator
volt.Future(T)Async result handle: .@"await"(io), .cancel(io)
volt.GroupStructured concurrency: .spawn(), .wait(), .cancel()
volt.syncSynchronization: Mutex, RwLock, Semaphore, Notify, Barrier, OnceCell
volt.channelMessage passing: Channel, Oneshot, Broadcast, Watch
volt.netNetworking: TCP, UDP, Unix sockets, DNS resolution
volt.fsFilesystem operations
volt.streamI/O streams: Reader, Writer, buffered I/O
volt.timeDuration, Instant, Sleep, Interval, Deadline
volt.signalSignal handling (SIGINT, SIGTERM)
volt.processProcess spawning and piped I/O
volt.shutdownGraceful shutdown coordination

Installation

Add Volt to your project in under a minute.

Install now

Quick Start

Build a TCP echo server in 30 lines.

Get started

Basic Concepts

Understand futures, the runtime, and cooperative scheduling.

Learn the model