Maddingue has asked for the wisdom of the Perl Monks concerning the following question:

Hello fellow monks, I am writing a program built around a loop on a IO::Select object. So far so good. Now I need to throw in a task that must be regularly executed. So I thought setting it as a SIGALRM handler. Sounds simple:
use strict; use warnings; use IO::Select; use IO::Socket; use POSIX qw< :signal_h >; my $port = 6681; my $listen = IO::Socket::INET->new( Proto => "tcp", LocalPort => $port, Listen => 10, Timeout => 10, ) or die "Can't bind socket to port $port: $!"; my $select = IO::Select->new($listen); print STDERR "listing on port $port\n"; my $alarm_handler = sub { print STDERR "! alarm handler\n"; alarm(2); }; my $sigset = POSIX::SigSet->new(SIGALRM); my $sa_alarm = POSIX::SigAction->new($alarm_handler, $sigset, SA_RESTA +RT); sigaction(SIGALRM, $sa_alarm); #$SIG{ALRM} = $alarm_handler; alarm(2); while (my @ready = $select->can_read) { for my $fh (@ready) { if ($fh == $listen) { my $sock = $listen->accept; $select->add($sock); } else { # ... } } }
Execpt that when running this program, it outputs:
$ perl listing on port 6681 ! alarm handler
and exits after the first SIGALRM. What is the obvious thing I'm missing to make the while restart (apart from nesting it in a while(1) { .. } loop)?

Replies are listed 'Best First'.
Re: IO::Select and alarm()
by ikegami (Pope) on Mar 19, 2009 at 14:48 UTC
    use Errno qw( EINTR ); for (;;) { my @ready = $select->can_read() or do { next if $! == EINTR; last; }; for my $fh (@ready) { ... } }


    Note that die("select: $!\n"); would be more appropriate than last, but I preserved the behaviour of the OP.

      Thank you, it works.

      Now I feel stupid because I should have remembered this :)

        There's a difference between can_read and select. can_read returns immediately when the object has no file handles, but I assumed it sleeps indefinitely like select in that situation.

        That means there's a bug in my earlier version. The OP's code is equivalent to

        use Errno qw( EINTR ); while ($select->count()) { my @ready = $select->can_read() or do { next if $! == EINTR; last; # Should be die("select: $!\n"); }; for my $fh (@ready) { ... } }