tardy (def: delaying or delayed beyond the right or expected time; late.) is an asynchronous runtime for writing applications and services in Zig. Most of the code for this project originated in zzz, a performance oriented networking framework.
- tardy utilizes the latest Asynchronous APIs while minimizing allocations.
- tardy natively supports Linux, Mac, BSD, and Windows.
- tardy is configurable, allowing you to optimize the runtime for your specific use-case.
tardy is a thread-local, I/O driven runtime for Zig, providing the core implementation for asynchronous libraries and services.
- Per-thread Runtime isolation for minimal contention
- Native async I/O (io_uring, epoll, kqueue, poll, etc.)
- Asynchronous
Socket
s andFile
s. - Coroutines (internally called Frames).
Compatible Zig Version: 0.13.0
Latest Release: 0.2.1
zig fetch --save git+https://github.com/tardy-org/tardy#v0.2.1
You can then add the dependency in your build.zig
file:
const tardy = b.dependency("tardy", .{
.target = target,
.optimize = optimize,
}).module("tardy");
exe.root_module.addImport(tardy);
A basic multi-threaded TCP echo server.
const std = @import("std");
const log = std.log.scoped(.@"tardy/example/echo");
const Pool = @import("tardy").Pool;
const Runtime = @import("tardy").Runtime;
const Task = @import("tardy").Task;
const Tardy = @import("tardy").Tardy(.auto);
const Cross = @import("tardy").Cross;
const Socket = @import("tardy").Socket;
const Timer = @import("tardy").Timer;
const AcceptResult = @import("tardy").AcceptResult;
const RecvResult = @import("tardy").RecvResult;
const SendResult = @import("tardy").SendResult;
fn echo_frame(rt: *Runtime, server: *const Socket) !void {
const socket = try server.accept(rt);
defer socket.close_blocking();
// you can use the standard Zig Reader/Writer if you want!
const reader = socket.reader(rt);
const writer = socket.writer(rt);
log.debug(
"{d} - accepted socket [{}]",
.{ std.time.milliTimestamp(), socket.addr },
);
try rt.spawn(.{ rt, server }, echo_frame, 1024 * 16);
var buffer: [1024]u8 = undefined;
while (true) {
const recv_length = reader.read(&buffer) catch |e| {
log.err("Failed to recv on socket | {}", .{e});
return;
};
writer.writeAll(buffer[0..recv_length]) catch |e| {
log.err("Failed to send on socket | {}", .{e});
return;
};
log.debug("Echoed: {s}", .{buffer[0..recv_length]});
}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
defer _ = gpa.deinit();
// tardy by default is
// - multithreaded
// - unbounded in terms of spawnable tasks
var tardy = try Tardy.init(allocator, .{});
defer tardy.deinit();
const server = try Socket.init(.{ .tcp = .{ .host = "127.0.0.1", .port = 9862 } });
try server.bind();
try server.listen(256);
try tardy.entry(
&server,
struct {
fn start(rt: *Runtime, tcp_server: *const Socket) !void {
try rt.spawn(.{ rt, tcp_server }, echo_frame, 1024 * 1024 * 4);
}
}.start,
);
}
There exist a lot more examples, highlighting a variety of use cases and features here. For an example of tardy in use, you can check out any of the projects in the ecosystem.
- zzz: a framework for writing performant and reliable networked services.
We use Nix Flakes for managing the development environment. Nix Flakes provide a reproducible, declarative approach to managing dependencies and development tools.
- Install Nix
sh <(curl -L https://nixos.org/nix/install) --daemon
- Enable Flake support in your Nix config (
~/.config/nix/nix.conf
):experimental-features = nix-command flakes
- Clone this repository:
git clone https://github.com/tardy-org/tardy.git
cd tardy
- Enter the development environment:
nix develop
This will provide you with a shell that contains all of the necessary tools and dependencies for development.
Once you are inside of the development shell, you can update the development dependencies by:
- Modifying the
flake.nix
- Running
nix flake update
- Committing both the
flake.nix
and theflake.lock
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in tardy by you, shall be licensed as MPL2.0, without any additional terms or conditions.