Subject: proper use of non-blocking read

proper use of non-blocking read

From: Eric Frias <>
Date: Wed, 10 Apr 2013 16:46:36 -0400

I've been trying to figure out how to use libssh2's non-blocking API
correctly, and I'm stuck. I can get it to work 99% of the time, but
that last 1% is eludes me.

The main problem I'm battling is what happens when I need to do a read
on a channel, and I don't know if there's going to be any data there to
read. If there is, I want to read it. If there's no data, I want to be
able to do other stuff like write to the channel or read or write to
another channel. Seems pretty normal.

So let's say I call libssh2_channel_read_ex() and it returns
LIBSSH2_ERROR_EAGAIN. My problem is that I can't tell what this means.
It could mean that:
* there was no data on that channel, and it's safe to go write some data
or service another channel. or,
* in the process of doing the read, libssh2 wandered into
_libssh2_channel_receive_window_adjust, sending a message to the ssh
server. While sending the adjust message, the TCP send buffer filled
up, and the OS level send(2) call returned EAGAIN

Of course, this second case is the one that is giving me problems.
Although I haven't seen it stated explicitly anywhere, it looks like if
I get LIBSSH2_ERROR_EAGAIN due to a failed write, the only thing I can
safely do with libssh2 is wait and call the same
libssh2_channel_read_ex() function again with the same arguments until
it manages to finish the write. If I were to make some other call to
libssh2 which could result in network traffic, libssh2 will give me a
slap on the wrist (in the form of a LIBSSH2_ERROR_BAD_USE) and then I'm
in trouble.

If I always assume that an LIBSSH2_ERROR_EAGAIN means that I can call
other functions, my tests will usually run pretty well for a few
minutes, maybe even hours, but eventually this error (or some variant of
it) will bite me. If I assume that LIBSSH2_ERROR_EAGAIN means the only
thing I can do is call the exact same function until it succeeds, I'm
trapped because I can't safely read from a channel unless I know there
will be data on it.

Am I missing something that would let me use the public API safely?
Right now I'm playing around with workarounds like only allowing my code
to switch and call another function if session->packet.olen == 0 after
an LIBSSH2_ERROR_EAGAIN, but that's ugly and I don't have much
confidence that I'm on the right track.

Received on 2013-04-10