Perl Cookbook

Perl CookbookSearch this book
Previous: 7.12. Flushing OutputChapter 7
File Access
Next: 7.14. Doing Non-Blocking I/O
 

7.13. Reading from Many Filehandles Without Blocking

Problem

You want to learn whether input is available to be read, rather than blocking for input as < > does. This is useful when reading from pipes, sockets, devices, and other programs.

Solution

Use select with a timeout value of 0 seconds, if you're comfortable with manipulating bit-vectors representing file descriptor sets:

$rin = '';
# repeat next line for all filehandles to poll
vec($rin, fileno(FH1), 1) = 1;
vec($rin, fileno(FH2), 1) = 1;
vec($rin, fileno(FH3), 1) = 1;

$nfound = select($rout=$rin, undef, undef, 0);
if ($nfound) {
  # input waiting on one or more of those 3 filehandles
  if (vec($rout,fileno(FH1),1)) { 
      # do something with FH1
  }
  if (vec($rout,fileno(FH2),1)) {
      # do something with FH2
  }
  if (vec($rout,fileno(FH3),1)) {
      # do something with FH3
  }
}

The IO::Select module provides an abstraction to hide the bit-vector operations:

use IO::Select;

$select = IO::Select->new();
# repeat next line for all filehandles to poll
$select->add(*FILEHANDLE);
if (@ready = $select->can_read(0)) {
    # input waiting on the filehandles in @ready
}

Discussion

The select function is really two functions in one. If you call it with one argument, you change the current default output filehandle (see Recipe 7.12). If you call it with four arguments, it tells you which filehandles have input waiting or are ready to receive output. This recipe only deals with four-argument select.

The first three arguments to select are strings containing bit-vectors. Each bit-vector represents a set of file descriptors to inspect for pending input, pending output, and pending expedited data (like out-of-band or urgent data on a socket), respectively. The final argument is the timeout  - how long select should spend waiting for status to change. A timeout value of 0 indicates a poll. Timeout can also be a floating-point number of seconds, or undef to wait (block) until status changes:

$rin = '';
vec($rin, fileno(FILEHANDLE), 1) = 1;
$nfound = select($rin, undef, undef, 0);    # just check
if ($nfound) {
    $line = <FILEHANDLE>;
    print "I read $line";
}

This code isn't perfect, though. If someone connects and sends a character but never sends a newline, your program will block in the <FILE>. We get around this by reading characters one at a time and processing completed lines when we read a "\n". This removes the need for the blocking <FILE> call. Another solution (if you're not testing files) is detailed in Recipe 7.15.

The IO::Select module hides the bit-vectors from you. IO::Select->new() returns a new object on which you call the add method to add one or more filehandles to the set. When you've added all the filehandles you are interested in, call can_read, can_write, or has_exception. These functions return a list of filehandles you can safely read from, write to, or that have unread exceptional data (TCP out-of-band data, for example).

Don't mix calls to four-argument select with calls to any of the buffered I/O functions listed in this chapter's Introduction (read, <>, seek, tell, etc.). Instead, you must use sysread  - and sysseek if you want to reposition the filehandle within the file.

If you want to read whatever is available on a socket or pipe and return immediately, see Recipe 7.14. If you're trying to do non-blocking reads on a terminal, see Recipes Recipe 15.6 and Recipe 15.8.

See Also

The select function in perlfunc (1) and in Chapter 3 of Programming Perl; the documentation for the standard module IO::Select; Recipe 7.14


Previous: 7.12. Flushing OutputPerl CookbookNext: 7.14. Doing Non-Blocking I/O
7.12. Flushing OutputBook Index7.14. Doing Non-Blocking I/O