Skip to content

Commit

Permalink
wip non linux support
Browse files Browse the repository at this point in the history
  • Loading branch information
Cloudef committed Jun 20, 2024
1 parent f8ee6d2 commit b12cee4
Show file tree
Hide file tree
Showing 18 changed files with 973 additions and 297 deletions.
10 changes: 9 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

var opts = b.addOptions();
const fallback = b.option(bool, "fallback", "use fallback event loop") orelse false;
opts.addOption(bool, "fallback", fallback);

const aio = b.addModule("aio", .{
.root_source_file = b.path("src/aio.zig"),
.target = target,
.optimize = optimize,
});
aio.addImport("build_options", opts.createModule());

const coro = b.addModule("coro", .{
.root_source_file = b.path("src/coro.zig"),
Expand All @@ -20,7 +25,7 @@ pub fn build(b: *std.Build) void {
const run_all = b.step("run", "Run all examples");
inline for (.{
.aio_dynamic,
.aio_static,
.aio_immediate,
.coro,
}) |example| {
const exe = b.addExecutable(.{
Expand All @@ -39,13 +44,16 @@ pub fn build(b: *std.Build) void {
run_all.dependOn(&cmd.step);
}

const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match any filter") orelse "";
const test_step = b.step("test", "Run unit tests");
inline for (.{ .aio, .coro }) |mod| {
const tst = b.addTest(.{
.root_source_file = b.path("src/" ++ @tagName(mod) ++ ".zig"),
.target = target,
.optimize = optimize,
.filters = &.{test_filter},
});
if (mod == .aio) tst.root_module.addImport("build_options", opts.createModule());
if (mod == .coro) tst.root_module.addImport("aio", aio);
const run = b.addRunArtifact(tst);
test_step.dependOn(&run.step);
Expand Down
9 changes: 6 additions & 3 deletions docs/pages/aio-immediate.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@ var my_buffer: [1024]u8 = undefined;
var my_len: usize = undefined;
try aio.multi(.{
aio.Write{.file = f, .buffer = "contents", .link_next = true},
aio.Write{.file = f, .buffer = "contents", .link = .soft},
aio.Read{.file = f, .buffer = &my_buffer, .out_read = &my_len},
});
```

The `.link_next` field of operation can be used to link the operation to the next operation.
The `.link` field of operation can be used to link the operation to the next operation.
When linking operations, the next operation won't start until this operation is complete.

`soft` link will propagate failure to next operations in the link chain.
`hard` link will not propagate failure, and the next operation starts normally.

#### Using complete

Complete is similar to multi, but it will not return `error.SomeOperationFailed` in case any of the operations fail.
Expand All @@ -51,7 +54,7 @@ var write_error: std.posix.WriteError = undefined;
var read_error: std.posix.ReadError = undefined;
const res = try aio.complete(.{
aio.Write{.file = f, .buffer = "contents", .out_error = &write_error, .link_next = true},
aio.Write{.file = f, .buffer = "contents", .out_error = &write_error, .link = .soft},
aio.Read{.file = f, .buffer = &my_buffer, .out_error = &read_error, .out_read = &my_len},
});
Expand Down
11 changes: 8 additions & 3 deletions docs/pages/aio-operations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@ link_next: bool = false,

If `out_id` is set, the id of the operation will be stored into that address immediately after sucessful queue.
The `id` can then be used in future operations to refer to this operation.


If `out_error` is set, the error of the operation will be stored into that address, in case the operation failed.
If there was no failure a `error.Success` will be stored in that address.
`counter` can be used to set either decreasing or increasing counter.
When operation completes it will either decrease or increase the `u16` stored at the address.
`link_next` can be used to link the next operation into this operation.


`link` can be used to link the next operation into this operation.
When operations are linked, the next operation won't start until this operation has completed first.
`soft` link will propagate failure to next operations in the link chain.
`hard` link will not propagate failure, and the next operation starts normally.

### Fsync

Expand Down Expand Up @@ -144,13 +150,12 @@ ns: i128,
### LinkTimeout

Timeout linked to a operation.
The operation before must have set `link_next` to `true`.
The operation before must have set `link` to either `soft` or `hard`.
If the operation finishes before the timeout, then the timeout will be canceled.
If the timeout finishes before the operation, then the operation will be canceled.

```zig
ns: i128,
out_expired: ?*bool = null,
```

### Cancel
Expand Down
5 changes: 3 additions & 2 deletions docs/pages/coro-blocking-code.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ fn task(pool: *ThreadPool) !void {
try std.testing.expectEqual(69, ret);
}
var scheduler = try Scheduler.init(std.testing.allocator, .{});
defer scheduler.deinit();
var pool: ThreadPool = .{};
defer pool.deinit(); // pool must always be destroyed before scheduler
try pool.start(std.testing.allocator, 0);
var scheduler = try Scheduler.init(std.testing.allocator, .{});
defer scheduler.deinit();
_ = try scheduler.spawn(task, .{&pool}, .{});
try scheduler.run();
```
Expand Down
6 changes: 4 additions & 2 deletions docs/pages/coro-context-switches.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ coro.yield(SomeEnum.value);
```

To continue running the task from where it left, you need to issue the same enum value to the following function.
If the task currently isn't being yielded in the supplied state, the call is no-op.
If you specify the third parameter as `.wait`, the current task will yield (or block if not a task) until the
target task yields to that particural state. If instead third parameter is `.no_wait` then if the task currently
isn't being yielded in the supplied state, the call is a no-op.

```zig
coro.wakeupFromState(task, SomeEnum.value);
coro.wakeupFromState(task, SomeEnum.value, .wait);
```

This is the preferred way to handle the control flow between tasks.
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ title: 'zig-aio: io_uring like asynchronous API and coroutine powered IO tasks f
zig-aio provides io_uring like asynchronous API and coroutine powered IO tasks for zig

```zig
// [!include ~/../examples/aio_static.zig]
// [!include ~/../examples/aio_immediate.zig]
```

## Features
Expand Down
7 changes: 5 additions & 2 deletions examples/aio_dynamic.zig
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ pub fn main() !void {
},
});

const ret = try work.complete(.blocking);
var num_work: u16 = 2;
while (num_work > 0) {
const ret = try work.complete(.blocking);
num_work -= ret.num_completed;
}

log.info("{s}", .{buf[0..len]});
log.info("{s}", .{buf2[0..len2]});
log.info("{}", .{ret});
}
2 changes: 1 addition & 1 deletion examples/aio_static.zig → examples/aio_immediate.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const std = @import("std");
const aio = @import("aio");
const log = std.log.scoped(.aio_static);
const log = std.log.scoped(.aio_immediate);

pub fn main() !void {
var f = try std.fs.cwd().openFile("flake.nix", .{});
Expand Down
13 changes: 6 additions & 7 deletions examples/coro.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,24 @@ fn server(client_task: coro.Task) !void {
try std.posix.bind(socket, &address.any, address.getOsSockLen());
try std.posix.listen(socket, 128);

coro.wakeupFromState(client_task, Yield.server_ready);
coro.wakeupFromState(client_task, Yield.server_ready, .wait);

var client_sock: std.posix.socket_t = undefined;
try coro.io.single(aio.Accept{ .socket = socket, .out_socket = &client_sock });

var buf: [1024]u8 = undefined;
var len: usize = 0;
try coro.io.multi(.{
aio.Send{ .socket = client_sock, .buffer = "hey ", .link_next = true },
aio.Send{ .socket = client_sock, .buffer = "I'm doing multiple IO ops at once ", .link_next = true },
aio.Send{ .socket = client_sock, .buffer = "how cool is that? ", .link_next = true },
aio.Send{ .socket = client_sock, .buffer = "hey ", .link = .soft },
aio.Send{ .socket = client_sock, .buffer = "I'm doing multiple IO ops at once ", .link = .soft },
aio.Send{ .socket = client_sock, .buffer = "how cool is that? ", .link = .soft },
aio.Recv{ .socket = client_sock, .buffer = &buf, .out_read = &len },
});

log.warn("got reply from client: {s}", .{buf[0..len]});
try coro.io.multi(.{
aio.Send{ .socket = client_sock, .buffer = "ok bye", .link_next = true },
aio.CloseSocket{ .socket = client_sock, .link_next = true },
aio.Send{ .socket = client_sock, .buffer = "ok bye", .link = .soft },
aio.CloseSocket{ .socket = client_sock, .link = .soft },
aio.CloseSocket{ .socket = socket },
});
}
Expand All @@ -64,7 +64,6 @@ fn client() !void {
.socket = socket,
.addr = &address.any,
.addrlen = address.getOsSockLen(),
.link_next = true,
});

while (true) {
Expand Down
4 changes: 3 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
# Zig flake helper
# Check the flake.nix in zig2nix project for more options:
# <https://github.com/Cloudef/zig2nix/blob/master/flake.nix>
env = zig2nix.outputs.zig-env.${system} { zig = zig2nix.outputs.packages.${system}.zig.master.bin; };
env = zig2nix.outputs.zig-env.${system} {
zig = zig2nix.outputs.packages.${system}.zig.master.bin;
};
system-triple = env.lib.zigTripleFromString system;
in with builtins; with env.lib; with env.pkgs.lib; rec {
# nix run .
Expand Down
Loading

0 comments on commit b12cee4

Please sign in to comment.