Io Implementations

The starting point for async code is the std.Io interface. Init.io provides a default implementation, but we can also choose one explicitly:

const std = @import("std");
const print = std.debug.print;
const builtin = @import("builtin");

// As before, 'sleep' simulates a slow operation.
fn sleep(ms: isize) void {
    var ts: std.posix.timespec =
        .{ .sec = 0, .nsec = ms * 1000000 };
    _ = std.posix.system.nanosleep(&ts, &ts);
}

fn task(count: u64, label: []const u8) void {
    for (0..count) |_| {
        print("{s}\n", .{label});
        sleep(100);
    }
}

// 'run' starts two tasks and waits for them to finish.
fn run(io: std.Io) void {
    var a = io.async(task, .{ 4, "aaa" });
    var b = io.async(task, .{ 4, "    bbb" });
    a.await(io);
    b.await(io);
}

fn runThreaded(allocator: std.mem.Allocator) !void {
    var th = std.Io.Threaded.init(allocator, .{});
    defer th.deinit();

    run(th.io());
}

fn runSingleThreaded() !void {
    var th = std.Io.Threaded.init_single_threaded;
    defer th.deinit();

    run(th.io());
}

fn runEvented(allocator: std.mem.Allocator) !void {
    var ev: std.Io.Evented = undefined;
    try ev.init(allocator, .{});
    defer ev.deinit();

    run(ev.io());
}

pub fn main(init: std.process.Init) !void {
    print("Threaded:\n", .{});
    try runThreaded(init.gpa);

    print("\nSingle-threaded:\n", .{});
    try runSingleThreaded();

    // std.Io.Evented is not (yet) working on Linux and
    // Windows, so we’ll skip it there.
    switch (comptime builtin.os.tag) {
        .linux, .windows => {},
        else => {
            print("\nEvented:\n", .{});
            try runEvented(init.gpa);
        },
    }
}

Running on Linux:

$ zig run io.zig
Threaded:
    bbb
aaa
aaa
    bbb
aaa
    bbb
aaa
    bbb

Single-threaded:
aaa
aaa
aaa
aaa
    bbb
    bbb
    bbb
    bbb

Next example: Queue.