Waiting on a stream with select()

The previous example was actually not completely correct -- it works, but depending on the wvcon implementation, it could accidentally use 100% CPU time. (The default wvcon implementation doesn't actually have this "problem," but that's a coincidence and might change in the future.)

Remember that read() is not guaranteed to fill the input buffer. In fact, it's not even guaranteed to read any bytes at all. So in theory, the previous example might find itself endlessly looping, calling isok() and read() over and over until some input is received. That will work, of course, but it wastes a lot of CPU time. What we should really do is tell WvStream to wait until some data is available, and only then call read(). Here's how.

	  
/*
 * A WvStream example.
 *
 * Some text about this example...
 */

#include <wvstream.h>

int main()
{
    char buffer[10];
    size_t numread;
    
    while (wvcon->isok())
    {
	if (wvcon->select(-1))
	{
	    numread = wvcon->read(buffer, sizeof(buffer));
	    if (numread) 
	    {
		wvcon->print("You said: ");
		wvcon->write(buffer, numread);
		wvcon->print(" (%s bytes)\n", numread);
	    }
	}
    }
}

 	 

Note again that the (-1) parameter to select() means "wait forever." In this case, it doesn't really matter if you set it to 5000 (5 seconds), for example, because we'll simply wait five seconds and restart the loop.

If we do select(0), select() doesn't wait at all. This isn't as pointless as it seems: select() returns true or false depending whether the stream was ready or not, so select(0) is a good way to test if a stream is ready without actually stopping your program.

You can also select() to see if a stream is writable, but this is used much less often since WvStream now has write buffers anyway (so you can always write to a stream without delaying). Sometimes it can be useful, though: for example, a TCP connection is not "writable" until it's connected to the remote host. That makes sense, if you think about it. It's not readable either, of course, but it doesn't necessarily become readable as soon as it connects. A connected TCP stream is always writable.

You can check for (or wait for) readability and writability at the same time. There is also a test for a stream "exception", but no one has ever used that, so I don't know what it does. The prototype for select() actually looks like this:

bool select(time_t msec_timeout,
     	bool readable=true, bool writable=false, bool isexception=false);

That means normally, we want to check whether a stream is readable, and we don't care if it's writable or has an exception. To check whether a stream is writable, for example, use a line like this:

wvcon->select(0, false, true, false);

To wait up to 5 seconds until the stream is either readable or writable, try this:

wvcon->select(5000, true, true, false);

Now, you're probably wondering why read() can't just wait for data and write() can't just send it out, saving us all a lot of trouble. Well, that's all fine and good for single-tasking one-stream programs, but WvStreams was designed to handle lots of simultaneous connections -- that's when select() really gets useful. We'll talk about that later, in the section about WvStreamList.