Uh oh!
There was an error while loading. Please reload this page.
std: Introduce Io Interface - #25592
Conversation
Uh oh!
There was an error while loading. Please reload this page.
| /// Copied and then passed to `start`. | ||
| context: []const u8, | ||
| context_alignment: std.mem.Alignment, | ||
| start: *const fn (*Group, context: *const anyopaque) void, |
There was a problem hiding this comment.
Is there a particular reason for passing the *Group as a separate parameter to start (which is not needed by the majority of call sites) instead of just having Select(...).async add it to the args in context?
There was a problem hiding this comment.
maybe I'm not understanding the suggestion, but that kind of sounds like a pain in the ass and unclear that it will generate better code
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| /// `s` is a struct with every field a `*Future(T)`, where `T` can be any type, | ||
| /// and can be different for each field. | ||
| pub fn select(io: Io, s: anytype) SelectUnion(@TypeOf(s)) { |
There was a problem hiding this comment.
| pubfnselect(io: Io, s: anytype) SelectUnion(@TypeOf(s)) { | |
| pubfnselect(io: Io, s: anytype) Cancelable!SelectUnion(@TypeOf(s)) { |
The doc comment for select should also probably be updated to describe when error.Canceled is returned. Based on my own understanding of how select should behave, maybe something like /// Returns `error.Canceled` if all futures in `s` are canceled. would work?
9c20f4f to
477c939CompareValorZard
commented
Oct 19, 2025
What happened to std.Io.AsyncDetached/goroutines? I remember that being a core feature back when this design was first proposed. |
Uh oh!
There was an error while loading. Please reload this page.
This comment was marked as resolved.
This comment was marked as resolved.
andrewrk
commented
Oct 19, 2025
|
So what would be the equivalent Zig code for package main
import (
"fmt"
)
funcfibonacci(nint, cchanint) {
x, y:=0, 1fori:=0; i<n; i++ {
c<-xx, y=y, x+y
}
close(c)
}
funcmain() {
c:=make(chanint, 10)
gofibonacci(cap(c), c)
fori:=rangec {
fmt.Println(i)
}
}(Taken from: https://go.dev/tour/concurrency/4 ) |
mlugg
commented
Oct 19, 2025
@ValorZard that's actually a great example of a flaw with So, a "direct" port of your code would look like this: //! This example omits the creation of the `Io` instance for brevity.fnfibonacci(n: usize, q: *Io.Queue(u64), io: Io) !void {
varx: u64=0;
vary: u64=1;
for (0..n) |_| {
tryq.putOne(x);
constnew=x+y;
x=y;
y=new;
}
}
pubfnmain(io: Io) !void {
// This is the "detach" group; you run tasks in this group if you just want them to clean// themselves up upon completion. There is just one of these across the entire application.vargroup: Io.Group=.init;
errdefer_=group.cancel();
trymainInner(io, &group));
trygroup.wait();
}
fnmainInner(io: Io, group: *Io.Group) !void {
constn=10;
varq_buf: [n]u64=undefinedlvarq: Io.Queue(u64) = .init(&q_buf);
trygroup.concurrent(io, fibonacci, .{ n, &q, io });
for (0..n) |_| {
std.debug.print("{d}\n", .{tryq.getOne(io)});
}
}But looking at that, the redundant work I was talking about becomes blindingly obvious: I mean, we're making a group with only one task, and we obviously know when it finishes! So, we could rewrite it as follows: fnfibonacci(n: usize, q: *Io.Queue(u64), io: Io) !void {
varx: u64=0;
vary: u64=1;
for (0..n) |_| {
tryq.putOne(x);
constnew=x+y;
x=y;
y=new;
}
}
pubfnmain(io: Io) !void {
constn=10;
varq_buf: [n]u64=undefinedlvarq: Io.Queue(u64) = .init(&q_buf);
constfib=tryio.concurrent(fibonacci, .{ n, &q, io });
deferfib.cancel() catch {};
for (0..n) |_| {
std.debug.print("{d}\n", .{tryq.getOne(io)});
}
}This code is strictly better---it's logically simpler, with obvious lifetimes which both helps you read the code and helps the |
Uh oh!
There was an error while loading. Please reload this page.
andrewrk
commented
Oct 24, 2025
Disabling wasm32 backend coverage for test-cases due to this failure: Coverage can be reinstated when wasm backend is further along. cc @pavelverigo |
Uh oh!
There was an error while loading. Please reload this page.
Introduced in ziglang/zig#25592 Signed-off-by: Pablo Alessandro Santos Hugen <phugen@redhat.com>
`test/tests.zig` fails after ziglang/zig#25592 was merged in. This just ensures that Io is passed, it might not be the ideal solution. `build.zig` was also failing due to new color parameter.
`test/tests.zig` fails after ziglang/zig#25592 was merged in. This just ensures that Io is passed, it might not be the ideal solution. `build.zig` was also failing due to new color parameter.
`test/tests.zig` fails after ziglang/zig#25592 was merged in. This just ensures that Io is passed, it might not be the ideal solution. `build.zig` was also failing due to new color parameter.
`test/tests.zig` fails after ziglang/zig#25592 was merged in. This just ensures that Io is passed, it might not be the ideal solution. `build.zig` was also failing due to new color parameter.
`test/tests.zig` fails after ziglang/zig#25592 was merged in. This just ensures that Io is passed, it might not be the ideal solution. `build.zig` was also failing due to new color parameter.
This patchset adds
std.Ioand provides two implementations for it:-fno-single-threaded- supports concurrency and cancellation.-fsingle-threaded- does not support concurrency or cancellation.std.IoAPI.std.Io.Threadedhas networking and file-system operations implemented.Cancellation works beautifully, except for a known race condition that has a
couple of competing solutions already in mind.
All of
std.nethas been deleted in favor ofstd.Io.net.std.fshas been partially updated to usestd.Io- only as required so thatstd.Io.Writer.sendFilecould use*std.Io.File.Readerrather than*std.fs.File.Reader.closes#8224
Laundry List of Io Features
async/await- these primitives express that operations can be doneindependently, making them infallible and support execution on limited Io
implementations that lack a concurrency mechanism.
concurrent- same asasyncexcept communicates that the operationmust be done concurrently for correctness. Requires memory allocation.
cancel- equivalent toawaitexcept also requests the Io implementationto interrupt the operation and return
error.Canceled.std.Io.Threadedsupports cancellation by sending a signal to a thread, causing blocking
syscalls to return
EINTR, giving a chance to notice the cancellation request.select- API for blocking on multiple futures usingswitchsyntaxGroup- efficiently manages many async tasks. Supports waiting for andcancelling all tasks in the group together.
Queue(T)- Many producer, many consumer, thread-safe, runtime configurablebuffer size. When buffer is empty, consumers suspend and are resumed by
producers. When buffer is full, producers suspend and are resumed by consumers.
TypeErasedQueue.Select- for blocking on runtime-known number of tasks and handling asubset of them.
Clock,Duration,Timestamp,Timeout- type safety for units of measurementMutex,Condition- synchronization primitivesDemo
Here is an example that makes an HTTP request to a domain:
Thanks to the fact that networking is now taking advantage of the new
std.Iointerface,this code has the following properties:
returned IP address.
attempts are canceled, including DNS queries.
-fsingle-threadedeven though theoperations happen sequentially.
You can see how this is implemented in
std.Io.net.HostName.connect:Upgrade Guide
Missing
ioParameterIf you need an
ioparameter, and you don't have one, you can get one like this:This is legal as long as these functions are not called:
Io.VTable.concurrentThis is a non-ideal workaround - like reaching for
std.heap.page_allocatorwhenyou need an
Allocatorand do not have one. Instead, it is better to accept anIoparameter if you need one (or store one on a context struct for convenience).Point is that the application's
mainfunction should generally be responsible forconstructing the
Ioinstance used throughout.When you're testing you can use
std.testing.io(much likestd.testing.allocator).How to use async/await
Use this pattern to avoid resource leaks and handle cancellation gracefully:
If the
fooorbarfunction does not return a resource that must be freed, then theifcan be simplified to_ = foo() catch {}, and if the function returnsvoid, then the discard can also be removed. Thecancelis necessary however because it releases the async task resource when errors (includingerror.Canceled) are returned.Related
Followup Issues
netLookup#25740netLookup#25741netLookup#25742netLookup#25743HostName.lookupandHostName.connect#25744max_iovecs_lentoIo#25750checkCanceland syscall) #25751std.process.ChildAPI intostd.Io#25752writeResolutionQuery#25754