tardy

repository·main·Indexed 43 days ago

https://github.com/tardy-org/tardy

An asynchronous runtime for Zig providing thread-local, I/O-driven primitives such as asynchronous Sockets and Files. Designed for high performance with minimal allocations, it supports multiple native async I/O backends including io_uring, epoll, kqueue, and poll.

Tokens
1.4K
Snippets
3
Records
4
Agent score
5%

What's inside tardy

  1. What is tardy?

    main

    tardy is an asynchronous runtime for writing applications and services in Zig. It is a thread-local, I/O driven runtime designed to provide core implementations for asynchronous libraries and services while minimizing allocations.

    Key features include:

    • Per-thread Runtime isolation: Minimizes contention.
    • Native async I/O: Supports io_uring, epoll, kqueue, poll, etc.
    • Asynchronous primitives: Provides Sockets and Files.
    • Coroutines: Implemented internally as Frames.
  2. Build and run tardy examples

    main

    The repository includes several examples. You can build and run them using the following commands:

    Run a specific example

    You must specify the async backend via the -Dasync flag and the example name via the -Dexample flag:

    zig build -Dasync=[async_backend] -Dexample=[example_name]

    Build all examples

    zig build -Dexample=all
  3. Install tardy

    main

    You can install tardy using zig fetch. Choose the version based on your Zig compiler version:

    For Main Line (Development) Zig

    Compatible with 0.17.0-dev.1454+5faa79730:

    zig fetch --save 'git+https://github.com/tardy-org/tardy?ref=main#commit_hash'

    For Stable Zig

    Latest release 0.3.2 supports stable Zig 0.16.0:

    zig fetch --save git+https://github.com/tardy-org/tardy#v0.3.2

    Integrating into build.zig

    After fetching, add the dependency to your build.zig file to make the module available to your executable:

    const tardy = b.dependency("tardy", .{
        .target = target,
        .optimize = optimize,
    }).module("tardy");
    
    exe_mod.addImport("tardy", tardy);
  4. Implement a TCP echo server with tardy

    main

    This example demonstrates how to initialize a multi-threaded Tardy instance, bind a Socket to a host/port, and use rt.spawn to handle incoming connections using coroutines (Frames).

    const std = @import("std");
    const options = @import("options");
    const tardy = @import("tardy");
    const Runtime = tardy.Runtime;
    const Socket = tardy.net.Socket;
    const AsyncIO = tardy.AsyncIO;
    
    const backend: AsyncIO.Kind = .init(options.async_backend);
    const Tardy = tardy.Tardy(backend);
    
    fn echo_frame(rt: *Runtime, server: *const Socket) !void {
        const socket = try server.accept(rt);
        defer socket.close_blocking();
    
        var sock_reader = socket.reader(rt, &.{});
        const sock_r = &sock_reader.interface;
    
        var sock_writer = socket.writer(rt, &.{});
        const sock_w = &sock_writer.interface;
        defer sock_w.flush() catch unreachable;
    
        // spawn off a new frame.
        try rt.spawn(echo_frame, .{ rt, server }, .@"16KiB");
    
        var buffer: [501]u8 = undefined;
        while (true) {
            const recv_length = sock_r.readSliceShort(&buffer) catch |e| {
                break;
            };
    
            if (recv_length == 0) return;
    
            sock_w.writeAll(buffer[0..recv_length]) catch |e| {
                break;
            };
        }
    }
    
    pub fn main(init: std.process.Init) !void {
        const host = "0.0.0.0";
        const port = 9862;
    
        const server: Socket = try .init(
            init.io,
            .{ .tcp = .{ .host = host, .port = port } },
        );
        try server.bind();
        try server.listen(501);
    
        var td: Tardy = try .init(init.gpa, init.io, .{
            .pooling = .static,
            .size_tasks_initial = 256,
            .size_aio_reap_max = 256,
        });
        defer td.deinit();
    
        try td.entry(
            &server,
            struct {
                fn start(rt: *Runtime, tcp_server: *const Socket) !void {
                    try rt.spawn(echo_frame, .{ rt, tcp_server }, .@"32KiB");
                }
            }.start,
        );
    }