Non-blocking use of std.Io.net.Reader? takeStruct blocks when no data is available

Is there a way to use Io.net.Reader in a non-blocking way?

I wanted to try out the new Io interface by implementing a wayland client myself but ran into the following obstacle:

I have the following function:

pub fn receive(io: Io) void {
    var buff: [256]u8 = undefined;
    var r = stream.reader(io, &buff);
    var reader = &r.interface;

    while (true) {
        const header = reader.takeStruct(wl.MessageHeader, native_endianess) catch |err| switch (err) {
            Io.Reader.Error.EndOfStream => break,
            else => @panic(@errorName(err)),
        };

        processMessage(io, header, reader);

        // if (reader.seek == reader.end) break;
    }
}

The stream is created using

std.Io.UnixAddress.connect()

This code blocks at reader.takeStruct if there is nothing else to receive. Is there a non blocking way of doing this using the reader?

If I uncomment the line:

// if (reader.seek == reader.end) break;

It will work but but than not all messages are processed in one call, that would be fine but in order to process all messages I would need to call it in a loop as new messages can come in during process message so again I get stuck at takeStruct.

This is some new territory for me so if it’s a logical error please let me know!

You aren’t expressing asynchrony in this code because you are not using the std.Io.async call. Andrew wrote an excellent article explaining the exact meaning of that and how to use std.Io.Threaded at a basic level: here

2 Likes

Not sure how async is going to help me here? if takeStruct keeps blocking until new data arrives the await call will never return as the last takeStruct call will always block until new data arrives. All I am looking for is for a way to use the reader so that I’m able to read if data is available if not return.
It might be that Reader is not meant for this use case though and I would need to call stream.socket.receive() instead.

The point is that you cannot assume any asynchronous properties of the Reader. The design of std.Io is such that asynchrony is expressed explicitly; therefore, there is no “non-blocking” read.

It sounds like you are running into some issue with your assumptions about your input stream, but it is hard to know without knowing more about the usage. If your issue is that you want to continue processing while the reader blocks, use std.Io.async. If your issue is that your reader is essentially hanging, check your assumptions about your input.

5 Likes

Io.Threaded doesn’t do non-blocking reads, even if it did, you still wouldn’t get the behaviour you are asking for, as @minkatter said, your understanding/expectations of Io is incorrect.

The Reader/Writer interfaces have no concept of nonblocking I/O, even an Io that implements non-blocking I/O it will still block the readers ‘thread’ (for lack of a better term) as the reader has no way to return in a nonblocking context.
So to take advantage of nonblocking with a reader, you need to put it on a separate ‘thread’ so that when it blocks, it doesn’t block your ‘thread’. This is what io.async/concurrent are for.

The correct term for whatever ‘thread’ is depends on the Io implementation, for the case of Threaded it is an actual OS thread. For the even less completed Evented it will be ‘fibre’/‘green thread’

This is how it works in other languages, they just do it implicitly and forcefully. Zig gives you the choice.

1 Like