Readavailable with timeout

I would like to read the first message out of a websocket as soon as it arrives, but also I want to close the connection if nothing arrives within a second.

How can I do that?

Example:

task = @async HTTP.WebSockets.listen("127.0.0.1", UInt16(9091)) do ws
    while !eof(ws)
        cmd = JSON.parse(String(readavailable(ws)))
        println(cmd)
     end
end

The readavailable function does not have a timeout parameter. If I call it then it may block indefinitely. The eof function does not block at all, so it cannot be used to “wait for the first message”.

So how do I do this?

Background info: I want to create a server that can respond quickly, closes the connection if there is no communication for a while.

I had a similar problem, except that I needed to read at most one message. I solved it with the following function, which may point you towards a solution for your case. The idea is to create a timer that interrupts the asynchronous task:

function async_reader(io::IO, timeout_sec)::Channel
    ch = Channel(1)
    task = @async begin
        reader_task = current_task()
        function timeout_cb(timer)
            put!(ch, :timeout)
            Base.throwto(reader_task, InterruptException())
        end
        timeout = Timer(timeout_cb, timeout_sec)
        data = String(readavailable(io))
        timeout_sec > 0 && close(timeout) # Cancel the timeout
        put!(ch, data)
    end
    bind(ch, task)
    return ch
end

I call it like this:

ch_out = async_reader(io, timeout)
out = take!(ch_out)  # if out===:timeout, a timeout occurred

So, out is equal to either the data from io, or :timeout. The take! function will block for at most timeout seconds.

1 Like

Thank you for your answer. It does work, but it creates a Channel, a Timer and a Task for each read operation. It seems to be an overkill.

Probably I could rewrite async_reader to put data into the channel in a loop. But then I would have to use the same timeout for all operations, and it would still create and cancel a Timer object for each read operation, even if there is plenty of data available.

It cannot be efficient. I cannot write speedy network I/O with this. There must be a better way.

I would have expected the result from the readAvailable function would be binary : Yes or No ( true or false ) without any waiting, as per other languages. Then the readAvailable can be used in a loop - and divert the routine to take action ( such as reading the input read(stdin, Char) when it is known that there is a character to be read. ) Seems very strange to me that readAvailable should hang the process to wait for an input - surely that is not what was intended - at the moment it operating no different than the standard read function

readavailable blocking is actually the intended behavior. You can do what you describe by using bytesavailable.

1 Like

Actually, you can’t do that with bytesavailable, because it is also a synchronous function. What I really wanted (e.g. wait at most 2 seconds for data to be available) cannot be done with synchronous/blocking functions.

I am not sure what you mean by synchronous. What I meant is that bytesavailable is not blocking; that is, if there is nothing to read, it immediately returns 0, which I think is what @GregMcC wanted.

1 Like