Article 8623 of comp.lang.perl:
Xref: feenix.metronet.com comp.lang.perl:8623
Path: feenix.metronet.com!news.ecn.bgu.edu!usenet.ins.cwru.edu!howland.reston.ans.net!spool.mu.edu!olivea!hal.com!parlo.hal.COM!not-for-mail
From: [email protected] (Paul Sander)
Newsgroups: comp.lang.perl
Subject: Re: Critical regions or sighold & sigrelse
Date: 3 Dec 1993 17:36:04 -0800
Organization: HaL Computer Systems, Inc.
Lines: 285
Message-ID: <[email protected]>
References: <fop.754856337@teal>
NNTP-Posting-Host: parlo.hal.com
Keywords: signals sighold sigrelese critical

In article <fop.754856337@teal> [email protected] (J. Gabriel Foster) writes:
>Hello,
>       I am implementing a simple spooling daemon with perl, and I need
>to be able to create some sections of code that cannot be interrupted by
>signals,  but I don't want to lose those signals.  sighold and sigrelse
>seem to fit the bill from a C standpoint, but they are not implemented in
>perl that I can see.   Any clues on how to do this?

Here is what I use.

---------

# This file contains simple signal handler code.  The client can register
# commands to be executed when HUP, INT, QUIT, or TERM signals are received,
# and also enter and exit critical sections (which can be nested).  Registered
# signal handlers are kept in a stack and are removed in the reverse order from
# which they were registered.  After a signal is received, the handlers are
# invoked in the reverse order in which they were registered, and then the
# program exits with a status code determined by the client (or with the
# die operator if the code is left unset).  Handlers are invoked in the same
# package that was set when they were registered.  Signals received while a
# critical section is active are postponed until the critical section ends.

# Public interfaces (all in the "main" package):
# &beginCrSect       -- Begins critical section, returning undef.
# &endCrSect         -- Ends critical section, returning undef.
# &pushHandler($cmd) -- Registers $cmd to be invoked when a signal is
#                       received; pushes the handler onto the handler stack.
#                       $cmd is invoked in the package that was active when
#                       it was registered.  Returns undef.
# &popHandler        -- Pops a handler off of the handler stack.  Returns
#                       ($package, $command), where $package is the package
#                       that was active when the handler was pushed, and
#                       $command is the command passed to &pushHandler.
# &setSigCode($code) -- Sets the exit code used when the process terminates
#                       after executing registered signal handlers.  $code
#                       must be between 0 and 255, inclusive.  Returns 1 if
#                       successful, 0 otherwise.

package SigHandle;

if ( ! defined $sighandle_loaded )
{
       $sighandle_loaded = 1;

       if ( ! defined $debugging )
       {
               $debugging = 0;
       }
       if ( $debugging )
       {
               print STDERR "Initializing sighandle.pl\n";
       }
       $crNesting = 0;
       $gotSignal = 0;
       $acceptSignal = 1;
       @handlers = ( );
       $exitCode = -1;
       $SIG{"HUP"} = "SigHandle'terminate";
       $SIG{"INT"} = "SigHandle'terminate";
       $SIG{"QUIT"} = "SigHandle'terminate";
       $SIG{"TERM"} = "SigHandle'terminate";

#-----------------------------------------------

# Begin critical section

sub main'beginCrSect
{
       if ($crNesting == 0)
       {
               if ( $acceptSignal )
               {
                       if ( $debugging )
                       {
                               print STDERR "Entering critical section\n";
                       }
                       $SIG{"HUP"} = "SigHandle'queueSignal";
                       $SIG{"INT"} = "SigHandle'queueSignal";
                       $SIG{"QUIT"} = "SigHandle'queueSignal";
                       $SIG{"TERM"} = "SigHandle'queueSignal";
               }
               elsif ( $debugging )
               {
                       print STDERR "Entering critical section inside handler\n";
               }
       }
       elsif ( $debugging )
       {
               print STDERR "Entering nested critical section\n";
       }
       $crNesting++;
       return undef;
}

#-----------------------------------------------

# End critical section

sub main'endCrSect
{
       if ( $crNesting > 0 )
       {
               $crNesting--;
       }
       if ($crNesting == 0)
       {

               if ( $gotSignal )
               {
                       $acceptSignal = 0;
                       $gotSignal = 0;
                       print STDERR "Interrupted during critical section, ";
                       print STDERR "terminating...\n";
                       &runHandlers;
                       &suicide;
               }
               if ( $acceptSignal )
               {
                       if ( $debugging )
                       {
                               print STDERR "Exiting critical section\n";
                       }
                       $SIG{"HUP"} = "SigHandle'terminate";
                       $SIG{"INT"} = "SigHandle'terminate";
                       $SIG{"QUIT"} = "SigHandle'terminate";
                       $SIG{"TERM"} = "SigHandle'terminate";
               }
               elsif ( $debugging )
               {
                       print STDERR "Exiting critical section in handler\n";
               }

       }
       elsif ( $debugging )
       {
               print STDERR "Exiting nested critical section\n";
       }
       return undef;
}

#-----------------------------------------------

sub runHandlers
{
       local($this,$pkg,$cmd,$str);

       $this = pop(@handlers);
       while ( $this ne "" )
       {
               ( $pkg, $cmd ) = split(/ /,$this,2);
               if ( $debugging )
               {
                       print STDERR "Invoking pkg $pkg cmd $cmd\n";
               }
               $str = join(" ","package $pkg;", $cmd);
               eval($str);
               if ( $@ ne "" )
               {
                       print STDERR "Error invoking $str\n";
                       print STDERR "$@\n";
               }
               $this = pop(@handlers);
       }
}

#-----------------------------------------------

# Exits the process

sub suicide
{
       if ( $exitCode >= 0 )
       {
               exit $exitCode;
       }
       else
       {
               exit 255;
       }
}

#-----------------------------------------------

# Run handlers and terminate after receiving signal

sub terminate
{
       print STDERR "Signal received, cleaning up...\n";
       $SIG{"HUP"} = "SigHandle'queueSignal";
       $SIG{"INT"} = "SigHandle'queueSignal";
       $SIG{"QUIT"} = "SigHandle'queueSignal";
       $SIG{"TERM"} = "SigHandle'queueSignal";
       $acceptSignal = 0;
       $gotSignal = 0;
       &runHandlers;
       &suicide;
}

#-----------------------------------------------

# Signal handler; records a signal if it comes in during a critical
# section.

sub queueSignal
{
       if ( $acceptSignal )
       {
               if ( $debugging )
               {
                       print STDERR "Received signal in critical section, deferred\n";
               }
               $gotSignal = 1;
       }
       else
       {
               print STDERR "Received signal during handler, ignored\n";
       }
}

#-----------------------------------------------

sub main'pushHandler
{
       local($cmd) = @_;
       local($pkg,$handler);

       ( $pkg ) = caller(0);
       if ( $debugging )
       {
               print STDERR "Pushing handler pkg $pkg cmd $cmd\n";
       }
       &main'beginCrSect;
       $handler = join(" ",$pkg,$cmd);
       push(@handlers,$handler);
       &main'endCrSect;
       return undef;
}

#-----------------------------------------------

sub main'popHandler
{
       local($pkg,$cmd);

       &main'beginCrSect;
       ( $pkg, $cmd) = split(/ /,pop(@handlers),2);
       &main'endCrSect;
       if ( $debugging )
       {
               print STDERR "Popping handler pkg $pkg cmd $cmd\n";
       }
       return "$pkg $cmd";
}

#-----------------------------------------------

sub main'setSigCode
{
       local($code) = @_;

       if ( ( $code >= 0 ) && ( $code <= 255 ) )
       {
               $exitCode = $code;
               return 1;
       }
       else
       {
               return 0;
       }
}

#-----------------------------------------------

}
elsif ( $debugging )
{
       print STDERR "sighandle.pl was required multiple times.\n";
}
1;
--
Paul M. Sander  (408) 379-7000  |  "If everything were easy, where would be
HaL Computer Systems, Inc.      |   the challenge?"
1315 Dell Avenue                |
Campbell, CA  95008  USA        |