Timeouts in stream reader not found

When using zig 0.16 for a tcp application, the std library provides a nice stream interface, with a reader and writer:

pub fn main (init: std.process.Init) !void {
    const io = init.io;
    const addr = try Io.net.IpAddress.parseLiteral("127.0.0.1:8080");

    var server = try addr.listen(io, .{});
    defer server.deinit(io);

    var group = Io.Group.init;
    defer group.cancel(io);

    while (true) {
        const stream = try server.accept(io);
        errdefer stream.close(io);
        try group.concurrent(io, handleClient, .{io, stream});
    }
}
fn handleClient (io: Io, stream: net.Stream) !void {
    defer stream.close(io);

    var r_buf: [256]u8 = undefined;
    var reader = stream.reader(io, &r_buf);

    const msg = reader.interface.takeByte() // this will block until message recieved
        catch return error.Canceled;
    std.debug.print("Got byte: {c}", .{msg});
}

const Io = std.Io;
const net = Io.net;
const std = @import("std");

Problem here is that all reader methods block until the specified data was sent. So what if someone connects and just never sends data? It will block for all eternity, a solution would be a specified timeout, but the reader interface will not provide one. Not having timeouts is lethal for production, so is this stream reader interface just not suited for production level or am i missing something?

I believe it’s not suitable for production as is. I’ve made another post about this some time ago, I suggested adding setTimeout to Stream.Reader and Stream.Writer. I didn’t get much feedback on that. If you care about timeouts, I suggest you have a look at my zio project.

i’m gonna get the names wrong because i’m still learning 0.16, but you can combine your call to read() with another task which does the timeout. i think the keyword is select?

i guess whether having to do a modicum of thinking and combining existing pieces counts as “not production ready” is up for interpretation

Yes, you can use Io.Select for this, but then you need two io.concurrent calls for each read, and one of them just sitting idly in sleep. That’s ridiculously expensive for a socket timeout, which in Io.Threaded should be either socket option or poll timeout.

1 Like

Yeah, right now im working with socket.receiveTimeout(..) to build my own ergonomic reading functions.

It just feels so wrong as they’ve been putting so much work and promotion into the new Reader interface and the whole network thing seems to be built around it, that I just think I’m missing something. Well, at least it’s nice for prototyping.

I believe there is an open issue for this, #31098 std.Io.concurrent: add a Timeout parameter, so the Zig team is definitely aware.

1 Like

That issue solves a different problem, for tasks to have limited time to run. The issue here involves no tasks, just using Io.Reader. It’s timeout on task vs I/O operation. Both are welcome, but timeouts on I/O operations are essential in production code.

i told some dozens of times - don’t implement that stuff inside “libraries“, and do such kind of things yourself, using specific OS capabilities

i mean this

maybe, it’s only just my rambles, but what is wrong with an idea to have a library of event driven state machines instead of having a bunch of (sometimes) strange libraries for every app-level protocol?