views:

79

answers:

2

I have this simple perl daemon:

#!/usr/bin/perl

use strict;
use warnings;
use Proc::Daemon;

Proc::Daemon::Init;

my $continue = 1;

$SIG{TERM} = sub { $continue = 0 };
$SIG{USR1} = sub { do_process(1) };

# basic daemon                                                                                    

boxesd_log("started boxesd");

while ($continue) {
    do_process(0);
    sleep(30);
}

boxesd_log("finished boxesd");

exit(0);

# required subroutines                                                                            

sub do_process {
    my ($notified) = @_;
    boxesd_log("doing it $notified");
}

But there is something that is not working right.

When the daemon starts, it logs every 30 seconds without the notification as expected:

Sat Oct 30 21:05:47 2010 doing it 0
Sat Oct 30 21:06:17 2010 doing it 0
Sat Oct 30 21:06:47 2010 doing it 0

The problem comes when I send the USR1 signal to the process using kill -USR1 xxxxx. The output is not what I expect:

Sat Oct 30 21:08:25 2010 doing it 1
Sat Oct 30 21:08:25 2010 doing it 0

I get two continuous entries, one from the signal handling subroutine and another form the ever running loop. It seems as if the sleep gets interrupted whenever the USR1 signal is received.

What is going on?

+7  A: 

The sleep is getting interrupted in the sense that your program will handle the incoming signal, but the loop will continue (going back to sleep) until a TERM signal is received. This is documented behaviour for the sleep() function:

May be interrupted if the process receives a signal such as "SIGALRM".

Note that if you need to sleep for 30 seconds, even if interrupted by a signal, you can determine the number of seconds slept and then sleep again for the remainder:

while (1)
{
    my $actual = sleep(30);
    sleep(30 - $actual) if $actual < 30;
}

PS. You can read more about signal handling in perldoc perlipc, and in virtually any unix programming book by W. Richard Stevens. :)

Ether
You're not checking to see if your second `sleep` got interrupted too. To handle it correctly, you need a loop of some sort, not just an `if`.
cjm
Er, the edited answer is an infinite loop. :(
mark4o
@mark4o: infinite until a signal interrupts it, yes, which is generally what daemons do. (I removed the `$continue` variable for clarity, as it wasn't relevant to the core question of how `sleep` behaves in various conditions. Obviously one might want to make the actual code more complicated, e.g. sleeping again on a second interruption as cjm mentioned.)
Ether
Oh I thought that loop was supposed to replace the `sleep(30)`, not the whole main loop. In that case I agree with cjm, and also the call to `do_process` is missing.
mark4o
A: 

The standard behaviour of sleep() function is actually to sleep the specified time or until a signal is received, see its man page. I presume that the Perl implementation just calls the libc sleep() function.

Sven Marnach
This is somewhat dependent on the level of intimacy that Perl enjoys with your operating system. Not all systems can be configured to restart slow system calls, although Perl does try to do this where possible. But you *can* get back EINTR, which is really quite annoying.
tchrist