Question

Suppose I have a select loop with some open connections that I'm going to read from and I have state machines attached to each connection that are meant to parse some stuff coming over the wire and do the right thing. So let's say one of the connections, c, is ready and the select loops hands it to a state machine m for processing. We would like to read 10 bytes from the connection and we are going to use readpartial. Unfortunately only 6 bytes have gotten to us so m reads 6 bytes and unblocks. Next time the loop hands us c it has 11 more bytes. m is still in whatever state it was last time the select loop handed us c and knows that it needs to read 4 more bytes so it reads those 4 bytes with readpartial(4), does some kind of side effecting operation using those 10 bytes that are now in its buffer and transfers to a new state. m still holding the connection so at this point it can unblock the loop or start doing the operations that the new state dictates and one of those operations is likely to be something that involves readpartial. In this case the correct thing to do is to continue processing the connection because readpartial will not block. But suppose the sequence of actions had been: receive 6 bytes, read 6 bytes & unblock, receive 4 bytes, read 4 bytes & do some stuff. In that case there would be nothing left to read and so if the state machine had decided to call readpartial on the connection then it would have blocked the select loop which is an undesirable situation and the correct thing in this case would have been for the state machine to unblock and wait for the select loop to come around again.

What's the correct and efficient way to solve this problem if I never want to block the select loop and also don't want to leave bytes in the connection buffer? I have gone over Ruby's Socket and IO API and I can't find a method that will tell me if there is stuff to read or not. read_nonblock is a potential avenue of exploration for testing whether the stream is readable or not by reading a single byte and then putting it back but in my tests it doesn't behave as expected, i.e. char = c.read_nonblock(1); c.ungetc(char); wire_data = c.read_partial(10) results in wire_data == char even if the connection buffer had more than char in the buffer.

Was it helpful?

Solution

As usual writing out the problem I was having helped me see the solution. Instead of calling readpartial in the state machine I can just call read_nonblock. read_nonblock will throw an exception if there is nothing to read and I can just catch this exception and unblock the select loop.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top