blocking socket and alarm
InfiniteLoop
created: 2006-09-05 10:10:25
Greetings Monks,
   I have a script that opens a blocking socket to a messaging server, in a while(1) loop (it is a backgrounded processs). While the script is running, I want to interrupt the socket read process, say every 1 minute, and write a status message to, say, STDERR (basically I want to monitor the process). Here is how my code looks like this:
use Sys::AlarmCall;

#open socket 
#....
&socket_read();

sub alarm_signal {
    print STDERR "Received alarm\n";
    &socket_read();
};

sub socket_read {

    while(1){
        alarm_call(60, 'alarm_signal');
        my $len = $socket->sysread($line,1024);
        #.....
    }
}



Although the alarm_signal is executed (not after 60 seconds), I get the:
 Deep recursion on subroutine "Sys::AlarmCall::alarm_call" at ... error.

I feel Im not using the alarm properly here. How would you implement a way to interrupt a background process to print a debug message, periodically ?

 I forgot to mention, this elaborate alarm call, is to monitor a messaging server. The socket read, is for the advisory message from the messaging server. Basically I wanted a "watch the watcher" kind of script, hence the interrupt to write a debug message.
Re: blocking socket and alarm
created: 2006-09-05 10:59:28

The function passed to alarm_call is not called on timeout as you expect. The function passed to alarm_call is the one alarm_call will interupt after the specified timeout. Reread the [mod://Sys::AlarmCall|docs].

sub socket_read {
    my $len;

    {
        ($len, my $errmsg) = alarm_call(60, '->sysread', $socket, $line, 1024);

        if (defined($len)) {
            if ($len eq 'TIMEOUT') {
               print STDERR "Received alarm\n";
               redo;
            }

            if ($len eq 'ERROR') {
               die($errmsg);
            }
        }
    }

    ...
}

Of course, you could also use [doc://alarm|alarm] directly.

sub socket_read {
    my $len;

    {
        eval {
            local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
            alarm 60;
            $len = $socket->sysread($line, 1024);
        };
        alarm 0;

        if (my $e = $@) {
            if ($e eq "alarm\n") {
               print STDERR "Received alarm\n";
               redo;
            }

            die($e);
        }
    }

    ...
}

Untested.

Update: Added second snippet. Handle exceptions.

Re: blocking socket and alarm
created: 2006-09-05 12:59:37
A more robust (and portable) approach is to perform a 'select' call on the socket handle with a 60 second timeout. That way you don't have to worry about any signal nastiness.

Either use IO::Select or the second form from perldoc -f select

Re^2: blocking socket and alarm
created: 2006-09-05 13:07:44

It's also much simpler. Why didn't I think of that?

use IO::Select ();

sub socket_read {
   my $sel = IO::Select->new($socket);

   while (!$sel->can_read(60)) {
      print STDERR "Received alarm\n";
   }

   my $len = $socket->sysread($line, 1024);

   ...
}

Reference: [mod://IO::Select]

Re: blocking socket and alarm
created: 2006-09-05 13:02:00
I don't think I would use alarm() for this. Instead I'd use select() (easier with IO::Select) to do non-blocking reads on the socket. Then it's easy to periodically write to the log when the right amount of time has passed.

-sam

perlmonks.org content © perlmonks.org and ikegami, InfiniteLoop, ozone, samtregar

prlmnks.org © 2006 edmund von der burg (eccles & toad)

v 0.03