Signals and Interrupts

#!/usr/bin/perl -w
#######################  Using the Built-In SIG Hash  ########################

#  Argumentative comment:  Signal handling in Perl is somewhat brain dead
#  compared to C, especially C under Unix.  The best use you can make of
#  signals in Perl is to protect critical regions of code so that terminal
#  interrupts do not, for example, interrupt the writing of a critical
#  file such as the /etc/passwd file.

$SIG{INT}  = 'IGNORE';   #  INT is a CTRL-C interrupt.
$SIG{QUIT} = 'IGNORE';   #  QUIT is a CTRL-\ interrupt.
$SIG{TSTP} = 'IGNORE';   #  TSTP is a CTRL-Z interrupt.

for (reverse 1..10)    #  Pretend this for loop is the "critical section".
{
    print "$_\n";
    sleep 1;   #  Allow time to type all sorts of terminal interrupts.
}
print "BLASTOFF!!!\n";

#  Restore handling of signals after critical section.  Use 'DEFAULT' as
#  the value to the right of the = to handle the signal in the default
#  manner i.e., terminate on CTRL-C and CTRL-\, suspend job on CTRL-Z.
$SIG{INT}  = \&handler; 
$SIG{QUIT} = \&handler;
$SIG{TSTP} = \&handler;

#  Let program live long enough for me to hit a terminal generated interrupt!
sleep(10);

sub handler
{
     print "Got a terminal interrupt!  Bailing!\n";
     exit(1);
}
##########################  Program Session Below  #########################

$ signals.pl
10        #  I hit a million CTRL-C, CTRL-Z, and CTRL-\ in here but,
9         #  as you can see,  nothing happened at all!
8
7
6
5
4
3
2
1
BLASTOFF!!!
Got a terminal interrupt!  Bailing!    #  A CTRL-Z got me in here!
$ 



#!/usr/bin/perl -w

#  What happens if you allow a return from a signal handling sub?

$SIG{INT}  = \&handler; 
$SIG{QUIT} = \&handler;
$SIG{TSTP} = \&handler;

for (1..5)    #  Pretend this for loop is the "critical section".
{
    $line = <STDIN>; 
}


sub handler
{
     my ($sigtype) = shift;   #  Note: didn't work with @_

     print "Got a  SIG$sigtype interrupt!\n";
}
##########################  Program Session Below  #########################

$ signals2.pl
Name "main::line" used only once: possible typo at ./signals2.pl line 11.
Got a  SIGINT interrupt!
Got a  SIGQUIT interrupt!
Got a  SIGTSTP interrupt!
Got a  SIGINT interrupt!
Got a  SIGINT interrupt!
hello
bye
cat
dog
mouse
$ 
######################  Important Comments Below  #######################

Notice that the file read operator, when interrupted and returned to,
completed successfully when I actually got around to "satisfying" the
read operator (with "hello", "bye", and so on).  However, there is no
absolute assurance that interrupted functions are *re-entrant* across
the board.  On some systems, some functions are NOT interruptible.

For these and other reasons, it is often best to ignore signals (with
some notable exceptions) or to exit from the program in the signal
handler.  Sometimes, it is best neither to ignore signals nor to bail
from the handler.  We will see how to safeguard code when you cannot
take the easy way out via 'IGNORE' or exit() from the handler.



#!/usr/bin/perl -w

#  Using alarm clocks to time-out processes.

#  Tell operating system that you're going to acknowledge an alarm signal.
$SIG{ALRM} = \&timeout;

#  SIGALRM will be generated if user doesn't respond in five seconds.
alarm 5;   #  Set alarm to five seconds.
$line = <STDIN>;

#  Turn the alarm clock off.  This is important!  If we have a lot more
#  processing to do, we don't want the alarm clock to go off by mistake!
alarm 0;

print "Output: $line\n";


sub timeout
{
     print "Too bad!  You're too slow!  I'm bailing!\n";
     exit(1);
}
###########################  Program Session Below  #########################

$ alarm.pl
Too bad!  You're too slow!  I'm bailing!

$ alarm.pl   #  This time I respond before the alarm clock expires.
foobar
Output: foobar
$ 
#####################  Returning From the Alarm Handler  #####################
#!/usr/bin/perl -w

$SIG{ALRM} = \&timeout;

#  SIGALRM will be generated if user doesn't respond in five seconds.
alarm 5;   #  Set alarm to five seconds.
$line = <STDIN>;
alarm 0;
print "Output: $line\n";

#  What if we don't bail?
sub timeout
{
     print "Too bad!  You're too slow!  I'm bailing!\n";
}
############################  Program Session Below  #######################

$ alarm2.pl
Too bad!  You're too slow!  I'm bailing!
foobar
Output: foobar
$ 



##################  Giving the User an "Alarm Warning"  ####################
#!/usr/bin/perl -w

$SIG{ALRM} = \&timeout;

$chances = 1;

#  SIGALRM will be generated if user doesn't respond in five seconds.
alarm 5;   #  Set alarm to five seconds.
$line = <STDIN>;
alarm 0;
print "$line\n";

#  We can reset the alarm clock before returning!
sub timeout
{
#   The only variable that is passed into the handler function is the
#   type of signal and even that is passed by Perl, not by the programmer.
#   That is why we needed a global variable ($chances) to do what we needed
#   to do below.
    
      if ($chances == 1)
      {
          print "Too bad!  You're too slow!  One more chance!\n";
          alarm 5;
          $chances++:
     }
     else
     {
          print "Sorry, no more chances to respond!\n";
          exit(1);
     }
}

######################  Two Program Sessions Below  #######################

$ alarm3.pl
Too bad!  You're too slow!  One more chance!
Sorry, no more chances to respond!

$ alarm3.pl
Too bad!  You're too slow!  One more chance!
Foobar
Output: Foobar

$