# How to handle timeouts in the new Io?

**URL:** <https://ziggit.dev/t/how-to-handle-timeouts-in-the-new-io/15053>\
**Category:** Explain\
**Created:** [April 19, 2026, 12:57pm UTC](https://ziggit.dev/t/how-to-handle-timeouts-in-the-new-io/15053 "2026-04-19T12:57:32Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Bobvan](https://ziggit.dev/user_avatar/ziggit.dev/bobvan/32/5105_2.png) [@Bobvan](https://ziggit.dev/u/Bobvan)\
**Post date:** [April 19, 2026, 12:57pm UTC](https://ziggit.dev/t/how-to-handle-timeouts-in-the-new-io/15053/1 "2026-04-19T12:57:32Z")

</div>

I have been toying around with the new Io.  
Here is my test code:

```zig
const std = @import("std");

fn testingConnection(io: std.Io) !void {
    try io.sleep(.fromSeconds(1), .awake); // only simulates work, is not necessary for correctnes since address already listens

    const address: std.Io.net.IpAddress = .{ .ip4 = .loopback(8008) };
    const stream = try address.connect(io, .{ .mode = .stream, .protocol = .tcp });
    defer stream.close(io);

    // no buffering setup, no need to call flush
    var stream_writer = stream.writer(io, &.{});
    const writer = &stream_writer.interface;

    try writer.print("Apocalypse message from TCP client!\n", .{});

    try io.sleep(.fromSeconds(1), .awake); // again only to simulate work
}

pub fn main() !void {
    var gpa: std.heap.DebugAllocator(.{}) = .init;
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var threaded: std.Io.Threaded = .init(allocator, .{ .concurrent_limit = .unlimited });
    defer threaded.deinit();
    const io = threaded.io();

    // no buffering setup, no need to call flush
    var stdout_writer: std.Io.File.Writer = .initStreaming(.stdout(), io, &.{});
    const stdout = &stdout_writer.interface;

    try stdout.print("Hello world!\n", .{});

    const address: std.Io.net.IpAddress = .{ .ip4 = .loopback(8008) };
    var server = try address.listen(io, .{});
    defer server.deinit(io);

    var connection_fut = try io.concurrent(testingConnection, .{io});
    defer connection_fut.cancel(io) catch {};

    // if testingConnection() fails this will wait forever, prob should setup timeout loop?
    const stream = try server.accept(io);
    defer stream.close(io);

    var stream_buffer: [1024]u8 = undefined;
    var stream_reader = stream.reader(io, &stream_buffer);
    const reader = &stream_reader.interface;

    _ = try reader.streamDelimiter(stdout, '\n'); // unfortunate there is not inclusive version
    _ = try reader.stream(stdout, .limited(1)); // also cleans '\n' from buffer for future use

    try connection_fut.await(io); // waits for the second sleep()
    try stdout.print("Goodbye world!\n", .{});
}

```

It works well, but i have few questions:

- Is there a better way to add timeout to accept() then just Select it as async task together with concurrent task for timeout?
- Is this the reccomended way to do this? I am looking to understand the usage patrerns of the interface.
- Is there a better way to do the Inclusive stream? I see there are Inclusive/Exclusive variants for take and peek, why not stream? (I know i can just implement it, but since take and peek have them, what is the reasoning behind stream)
- I see there is testing io, how far along is it, what can it catch and how to get its behavior when declaring it myself?

Overall i really like the interface so far, I hope signal processing will make it inside aswell. It just makes sense to be inside imo.

Robert 😊

---

<div class="post-metadata">

**Author:** ![vulpesx](https://ziggit.dev/user_avatar/ziggit.dev/vulpesx/32/3989_2.png) [@vulpesx](https://ziggit.dev/u/vulpesx)\
**Post date:** [April 19, 2026, 1:26pm UTC](https://ziggit.dev/t/how-to-handle-timeouts-in-the-new-io/15053/2 "2026-04-19T13:26:49Z")

</div>

> [@Bobvan](#):
>
> - Is there a better way to add timeout to accept() then just Select it as async task together with concurrent task for timeout?
> - Is this the reccomended way to do this? I am looking to understand the usage patrerns of the interface.

When dealing with futures the select, or similar, pattern is all you get.

However, the lower level `Operation` and `Batch` API’s do offer built in support for timeouts.

Those APIs are limited right now. But will be expanded to support every operation, which will intern give every operation timeout support.  
But that is still a lower level API used by abstractions like a `Stream`, which should (when it moves to this API) provide a way to set timeouts.

> [@Bobvan](#):
>
> I see there is testing io, how far along is it, what can it catch and how to get its behavior when declaring it myself?

The testing io is just a `Threaded` currently. But an implementation for testing an leak checking is planned.

> [@Bobvan](#):
>
> I hope signal processing will make it inside aswell. It just makes sense to be inside imo.

I don’t see why signal processing would go into Io? You could get the data through Io but processing it should be separate IMO.

---

<div class="post-metadata">

**Author:** ![dee0xeed](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/3ab097/32.png) [@dee0xeed](https://ziggit.dev/u/dee0xeed)\
**Post date:** [April 19, 2026, 8:03pm UTC](https://ziggit.dev/t/how-to-handle-timeouts-in-the-new-io/15053/3 "2026-04-19T20:03:33Z")

</div>

> How to handle timeouts in the new Io?

my (probably stupid) answer is - do not use “new i/o“, use OS capabilities, that way -

> <https://github.com/dee0xeed/edsm-in-zig-demo-2/blob/main/src/common-sm/rx.zig#L142>

---

<div class="post-metadata">

**Author:** ![Bobvan](https://ziggit.dev/user_avatar/ziggit.dev/bobvan/32/5105_2.png) [@Bobvan](https://ziggit.dev/u/Bobvan)\
**Post date:** [April 19, 2026, 9:19pm UTC](https://ziggit.dev/t/how-to-handle-timeouts-in-the-new-io/15053/4 "2026-04-19T21:19:16Z")

</div>

> [@vulpesx](#):
>
> However, the lower level `Operation` and `Batch` API’s do offer built in support for timeouts.

Yes i know about this, thanks for confirmation that its intended for it to start backing Streams in the future.

> [@vulpesx](#):
>
> The testing io is just a `Threaded` currently. But an implementation for testing an leak checking is planned.

Didnt know that, you saved me a lot of time trying to find the debug version.

> [@vulpesx](#):
>
> I don’t see why signal processing would go into Io? You could get the data through Io but processing it should be separate IMO.

I meant that it IMO makes sense to be able to submit “Signal handler functions” to IO. Its also interaction with kernel and if abstracted away, can be used for testing purposes. I didnt mean that it should handle it behind your back automatically. Is that a sound idea?

---

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [April 20, 2026, 12:31am UTC](https://ziggit.dev/t/how-to-handle-timeouts-in-the-new-io/15053/5 "2026-04-20T00:31:25Z")

</div>

Tracking issue:  
[https://codeberg.org/ziglang/zig/issues/31098](https://codeberg.org/ziglang/zig/issues/31098)

---

<div class="post-metadata">

**Author:** ![alanza](https://ziggit.dev/user_avatar/ziggit.dev/alanza/32/6006_2.png) [@alanza](https://ziggit.dev/u/alanza)\
**Post date:** [April 20, 2026, 12:40am UTC](https://ziggit.dev/t/how-to-handle-timeouts-in-the-new-io/15053/6 "2026-04-20T00:40:22Z")

</div>

> [@Bobvan](#):
>
> I meant that it IMO makes sense to be able to submit “Signal handler functions” to IO.

You happen to have fallen into an English trap: “signal processing” read to me (and I imagine to @vulpesx) as if you were talking about, for example, [digital filters](https://ccrma.stanford.edu/~jos/filters/) for audio signals, not responding to signals in the IPC sense

---

<div class="post-metadata">

**Author:** ![vulpesx](https://ziggit.dev/user_avatar/ziggit.dev/vulpesx/32/3989_2.png) [@vulpesx](https://ziggit.dev/u/vulpesx)\
**Post date:** [April 20, 2026, 4:46am UTC](https://ziggit.dev/t/how-to-handle-timeouts-in-the-new-io/15053/7 "2026-04-20T04:46:54Z")

</div>

> [@Bobvan](#):
>
> I meant that it IMO makes sense to be able to submit “Signal handler functions” to IO. Its also interaction with kernel and if abstracted away, can be used for testing purposes. I didnt mean that it should handle it behind your back automatically. Is that a sound idea?

ah, that makes more sense. “signal processing” refers to analysing and modifying signals in the realm of audio, radio, etc.

I’m not sure if Io is the best place for OS signal handling, but it wouldn’t be out of place.

---

<div class="post-metadata">

**Author:** ![lalinsky](https://ziggit.dev/user_avatar/ziggit.dev/lalinsky/32/3720_2.png) [@lalinsky](https://ziggit.dev/u/lalinsky)\
**Post date:** [April 20, 2026, 7:08am UTC](https://ziggit.dev/t/how-to-handle-timeouts-in-the-new-io/15053/8 "2026-04-20T07:08:08Z")

</div>

I’ve created [https://codeberg.org/ziglang/zig/issues/31970](https://codeberg.org/ziglang/zig/issues/31970) to solve the particular issue with `accept`. There is already a similar solution for `connect`, currently not implemented in std, but allowing other implementations like mine to support it.

---

<div class="post-metadata">

**Author:** ![dee0xeed](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/3ab097/32.png) [@dee0xeed](https://ziggit.dev/u/dee0xeed)\
**Post date:** [April 24, 2026, 8:34pm UTC](https://ziggit.dev/t/how-to-handle-timeouts-in-the-new-io/15053/9 "2026-04-24T20:34:03Z")

</div>

> The `Batch` API provides a low-level, per-`Operation` way to set a time limit for a set of actions:

why batch? do it for every request-reply action, why not? set a maximal awaiting time every time (pun intended :-)), when you really to await for some data to arrive, like this (for ex.)

 ![to](https://ziggit.dev/uploads/default/original/2X/f/f1d08ee3b303ff9daf99b2df1840c7d4974274b4.png)
