The project consists of a few high-level components:
- Runtime for executing stackful coroutines (fibers, green threads) on one or more CPU threads.
- Asynchronous I/O layer that makes it look like operations are blocking for easy state management, but using event-driven OS APIs under the hood.
- Synchronization primitives that cooperate with this runtime.
- Integration with standard library interfaces, like
std.Io.Readerandstd.Io.Writer.
It's similar to goroutines in Go, but with the pros and cons of being implemented in a language with manual memory management and without compiler support.
- Support for Linux (
io_uring,epoll), Windows (iocp), macOS (kqueue), most BSDs (kqueue), and many other systems (poll). - User-mode coroutine context switching for
x86_64,aarch64,arm,thumb,riscv32,riscv64andloongarch64architectures. - Growable stacks for the coroutines implemented by auto-extending virtual memory reservations.
- Single-threaded or multi-threaded coroutine scheduler.
- Fully asynchronous network I/O on all systems. Supports TCP, UDP, Unix sockets, raw IP sockets. DNS lookups.
- Fully asynchronous file I/O on Linux, partially (read/write) on Windows, simulated using auxiliary thread pool on other systems.
- Safe cancelation support for all operations.
- Structured concurrency using task groups.
- Synchronization primitives, including more advanced ones, like channels.
- Low-level event loop access for integrating with existing C libraries.
- Add zio as a dependency in your
build.zig.zon:
zig fetch --save "git+https://github.com/lalinsky/zio#v0.8.0"- In your
build.zig, add theziomodule as a dependency to your program:
const zio = b.dependency("zio", .{
.target = target,
.optimize = optimize,
});
// the executable from your call to b.addExecutable(...)
exe.root_module.addImport("zio", zio.module("zio"));Basic TCP echo server:
const std = @import("std");
const zio = @import("zio");
fn handleClient(stream: zio.net.Stream) !void {
defer stream.close();
var read_buffer: [1024]u8 = undefined;
var reader = stream.reader(&read_buffer);
var write_buffer: [1024]u8 = undefined;
var writer = stream.writer(&write_buffer);
while (true) {
const line = reader.interface.takeDelimiterInclusive('\n') catch |err| switch (err) {
error.EndOfStream => break,
else => return err,
};
try writer.interface.writeAll(line);
try writer.interface.flush();
}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const rt = try zio.Runtime.init(gpa.allocator(), .{});
defer rt.deinit();
const addr = try zio.net.IpAddress.parseIp4("127.0.0.1", 8080);
const server = try addr.listen(.{});
defer server.close();
std.log.info("Listening on {f}", .{server.socket.address});
var group: zio.Group = .init;
defer group.cancel();
while (true) {
const stream = try server.accept();
errdefer stream.close();
try group.spawn(handleClient, .{stream});
}
}See examples/*.zig for more examples.
The following libraries use zio for networking and concurrency:
- Dusty - HTTP client and server library
- nats.zig - NATS client library
- pg.zig - PostgreSQL client library (fork of karlseguin/pg.zig)
- memcached.zig - Memcached client library
# Build the library and examples
zig build
# Run tests
zig build testThis project is licensed under the MIT license.