http://www.perlmonks.org?node_id=300016

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

I've written a perl script to poll the task list on a Win32 server to see if the spooler service is still up. We have had problems with the spooler service crashing, we could rebuild the box, but we're planning on moving to another server anyways, so I whipped up this script to page me when the spooler service goes down. Anyways....

The script sleeps for 10 minutes after each query to the server. I wanted to respond more gracefully to Ctrl-C, so I decided to write up a sigint trap. To my frustration I noticed that it doesn't interrupt the sleep command. To test this concept I whipped up this;

#!/perl/bin/perl -w use strict; $SIG{'INT'} = 'cleanup'; while (1) { print "."; sleep 10; } sub cleanup {die "\nending\n";}
It doesn't interrupt the sleep 10 command. I looked up the docs on the sleep command, it is supposed to respond to signal interrupts like SIGALARM, but doesn't it respond to SIGINT? What would you recommend instead of the sleep command that would respond to a SIGINT and allow for a more graceful shutdown? Thanks.

update (broquaint): added formatting

Replies are listed 'Best First'.
Re: sleep doesn't respond to sigint(2)?
by Abigail-II (Bishop) on Oct 17, 2003 at 14:56 UTC
    Are you running 5.8.x? Then you might be bitten by 'safe signals'. Delivery of signals is delayed until the next time Perl dispatches an op - and that might not happen until the sleep is finished. This is a problem on some OSses (I haven't been able to reproduce it under Linux), and therefore there's a fix in 5.8.1. By setting an environment variable (look at perldelta for the details), you can get the old behaviour back.

    Abigail

      I'm running 5.8.0
Re: sleep doesn't respond to sigint(2)?
by snax (Hermit) on Oct 17, 2003 at 14:34 UTC
    Use a ref to that sub, not a string:
    $SIG{INT} = \&cleanup;
    Update:
    Oh, Win32. Check this out, at the bottom:
    Perl Quirks on Win32

    Looks like signal handling and Win32 is a bad mix.

    Update, again:
    Well, I am an idiot. My first suggestion (use a ref) is somewhat pointless, as the string construction should work fine, subject to the Win32 issues noted above (although the perlipc page suggests the best way is to use a ref).

    Apologies...

      that explains it...thanks
Re: sleep doesn't respond to sigint(2)?
by JamesNC (Chaplain) on Oct 17, 2003 at 20:39 UTC
    Just use a smaller sleep an count or you you can use select to get a really small wait between looks like so
    #!/perl/bin/perl -w use strict; $SIG{'INT'} = 'cleanup'; my $c= 0; while (1) { select(undef, undef, undef, .2); $c++; if($c == 50 ){ #10 seconds print "checking something...\n"; $c= 0; } } sub cleanup {die "\nending\n";}

    JamesNC
Re: sleep doesn't respond to sigint(2)?
by bart (Canon) on Oct 18, 2003 at 12:20 UTC
    Well, why it does work on Indigoperl 5.6.1 (Win32, ActivePerl-compatible), I do get a "illegal instruction" fatal error on Win98 — known on other platforms as a segfault. So the old behaviour isn't any better than the new one.

    Well, the page that snax pointed to says "... there are serious caveats, such as inability to die() or exit() from a signal handler.".

    p.s. Did alarm() get fixed on 5.8.x? Because on perl5.6.x/Win32, it doesn't interrupt a sleep(), like it does (and should) on other platforms.

      Alarm works on Win32, but not ALL the time such as when you do a blocking system calls like  $response = <stdin> Abigail explains why this is so. Here is a sub that I wrote that will get you around the <stdin> problem that uses Term::ReadKey. If you don't supply a sub handler... it will just use a default... this is pretty useful for getting input from the keyboard when you want to timeout... play with it... you will get the idea..
      #!/perl/bin/perl -w use strict; my $input = &my_stdin( prompt =>"Enter some text: ", timeout => 3, handler => \&my_alarm ); print "You entered: $input \n"; sub my_stdin { use Term::ReadKey; $| = 1; #turn buffering off so we can see our print below my %args = @_; my ($output,$key ); my $prompt = $args{'prompt'}; my $timeout = $args{'timeout'}; my $toh = $args{'handler'}; # ref to a handler $SIG{ALRM} = $toh || sub { print "Timeout\n"; exit;}; alarm($timeout); print $prompt; ReadMode 4; while(1){ if(defined($key = ReadKey(-1))){ my $val = ord $key; # 8 is backspace 10 and 13 are $output .= $key unless $val == 8 || $val == 10 || $val == 13; #echo to the screen print $key if length $output > 0; alarm($timeout); #reset the alarm #last if the user hits return last if ( $val == 10 || $val == 13); # chop one if we get a backspace if( $val == 8 ){ chop $output; print " "; print chr(8); } } } ReadMode 0; print "\n"; $output; } sub my_alarm{ print "I just died because you waited too long!"; exit; }


      Update: missing code added :-)
      Update2: added code to handle backspaces

      JamesNC