Search code examples
perlselectfilehandle

IO::Select and adding a tag to the handle


I'm wondering what's the best way to add additional data to a handle when I'm using IO::Select?

Basically I'd like to add a handle to IO::Select but also have additional info attached to that handle that I could retrieve later.

Note: I know I could keep a separate data structure which holds a handle and additional data but that would require coordinating two data structures and that's probably going to cause more problems than its worth.


Solution

  • A direct way follows from IO::Select docs for add method

    Each handle can be an IO::Handle object, an integer or an array reference where the first element is an IO::Handle or an integer.

    So there, there's "an array reference" that one may use.

    An example:

    use warnings;
    use strict;
    use feature 'say';
    
    use Time::HiRes qw(sleep);
    use POSIX qw(:sys_wait);
    use IO::Select;
    
    my $sel = IO::Select->new;
    
    my @procs;
    for my $job (1..3) {
        pipe my ($reader, $writer);
        $sel->add( [$reader, "job-$job"] );  # add a tag to the handle
    
        my $pid = fork // die "Can't fork: $!";
    
        if ($pid == 0) {
            close $reader;
            sleep rand 4;
            say $writer "\tkid $$ (job $job)";
            close $writer;
            exit; 
        }   
        close $writer;
        push @procs, $pid;
    }       
    say "Started processes @procs\n";
    
    # Read from pipes when ready, print piped messages
    while ( my @ready = $sel->can_read ) {
        foreach my $p (@ready) {
            my ($handle, $tag) = @$p;
            say "Reading from fileno ", $handle->fileno, ", tag: ", $tag;
            print while <$handle>;
            $sel->remove($p);      # *this* order: remove then close
            close $handle;
        }   
    }   
    
    # Reap
    my $msg = "\nExited (with status): ";
    my $kid = 0; # poll to reap
    while (($kid = waitpid -1, WNOHANG) > -1) {
        $msg .= "$kid ($?) " if $kid > 0; 
        sleep 0.1;
    }   
    say $msg;
    

    Prints

    Started processes 15679 15680 15681
    
    Reading from fileno 5, tag: job-2
            kid 15680 (job 2)
    Reading from fileno 4, tag: job-1
            kid 15679 (job 1)
    Reading from fileno 6, tag: job-3
            kid 15681 (job 3)
    
    Exited (with status): 15680 (0) 15679 (0) 15681 (0)