Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Handling SIG INT multiple times

by delight (Initiate)
on Oct 01, 2015 at 14:15 UTC ( #1143554=perlquestion: print w/replies, xml ) Need Help??

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

Hello guys I'm using sig int to communicate between my class and the script who use it. In detail i'm using signals to change the flow of the script in certain situation. This works okay but the problem is that my sig int handler looks something like:
sub handler { # call some log stuff here a_function() if($myobject->{something} eq 'lorem'); b_function() if($myobject->{something} eq 'ipsum); #etc }
my class send a signal to change the flow of the script in some scenarios, so its just a: kill 'INT', $$; Thing is that after the first okay the handler runs a_function() ( which is a long running function who dont return) if something happens and the class sends another kill, handler() will not be called because, I guess, for the OS or Perl we are still handling the first signal. So my question is there's a solution?

Replies are listed 'Best First'.
Re: Handling SIG INT multiple times
by sundialsvc4 (Abbot) on Oct 01, 2015 at 15:25 UTC

    Basically (and this is true in any programming language ...) you must treat a signal like “the ringing of the telephone.”   It is a signal that something needs to be done.   Its main purpose in life is not “to wake you up,” but “to ensure that you are not asleep.”   (That is to say, to avoid “busy waiting.”)   It is usually not a good design to do substantial work within a signal handler, since that work will be done completely asynchronously to any other part of the application.   (Signal handlers do not execute concurrently with themselves, as you saw, and if a signal is presented while the handler is still running, the additional signal(s) is usually dropped.)   If the activity that is started by the signal is long-running, interrupts could happen again at any time.   Therefore, the signal handler should not drive that activity.   The actual handler should set a flag and get the hell out of there.

    Packages such as POE go a long way toward providing a comprehensive architecture out-of-the-box, but you can also do simpler things.   Most likely, you will want to use a thread to execute the long process (what is now in your signal handler).   When a signal arrives, the signal handler sets a boolean flag to True and strobes a condition-variable.   The main loop of the thread then looks like this:

    while (true) { wait for condition variable and reset while (flag is true) { flag = false; // your long-running process .. I-F there is work to do } }

    The thread will first await to be awakened.   Then, it will loop and remain awake until the flag remains false, which means that another signal did not arrive in the meantime.   It will then wait on the condition-variable again ... which quite probably has already been tripped again.   Thus, as indicated, the meat-and-potatoes work of the thread might at any point find that there is nothing to do.   The thread will not “stall,” but it might make a few extra do-nothing cycles, and who-cares.

    It is easy to see how this can be extended to handle orderly thread termination and wind-up of its affairs:   a “please die” flag is set and the condition is strobed to be sure the thread is awake.

    In this explanation, I am not “getting specific to Perl,” and I will leave it to other Monks to write the code for you.   ;-)   These principles are generally true for any programming environment that supports true, pre-emptive threading.

      In theory and somewhat in practice Perl 6 neatly handles this for you with Supplies and taps handling signals in a thread safe sort of way. See "System events exposed as Supplies" in the Perl 6 design documents. The Perl 6 solution inspired me to write some Perl 5 code with similar capability that I have included below. Note that like the Perl 6 solution my code will "as is" do the actual handling of SIGINTs sequentially but it should be quite easy to add some parallel threads if wanted.

      use strict; use warnings; use threads; use Thread::Queue; my $q = Thread::Queue->new(); # A new empty queue my $thr = threads->create( sub { # Thread will loop until no more work while (defined(my $item = $q->dequeue())) { print "starting work on $item\n"; sleep 5; print "finished work on $item\n"; } } ); my $item = 1; { local $SIG{INT} = sub { $q->enqueue($item++) }; kill 'INT', $$; sleep 2; kill 'INT', $$; sleep 2; } $q->enqueue(undef); # I think in more modern Thread::Queue has $q->end $thr->join();
      Ron

        An interesting idea, and thanks for sharing.   Note, though, that a SIGINT may or may not correspond to the delivery of a particular unit of work, and such programs may or may not call for the presence of a queue.   Sometimes the signal is a request to make some deviation from the usual flow-of-control within the program, and sometimes the purpose of the signal is simply, “you’d better not be asleep right now.”   The state of the program at the time that the signal goes off is unpredictable, although the Perl interpreter does guarantee its own internal state as it invokes your (Perl-language) signal handler sub.   A signal handler would not be a good place to try to touch anything that is shared or locked.   I also do not think that it is a good place to attempt gathering a new unit of work.

      First of all: thank you. I knew/felt that my signal handler was wrong/bad design but I was too lazy for threads. I totally agree with your answer, I guess now I've to code :P. Thank you again!

        Well now, strictly speaking, you could do this in the main thread, if the program never has anything else to do.   The main thread could be the one that contains the loops aforementioned.   For a daemon-program that does not interact with anyone or anything else, that might be just fine.

        The main “take aways” are:   (1) the notion of a signal as a wakeup-call, and of not attempting to do anything substantial in them;   (2) the notion that signals are not one-to-one paired with work requests and might in some cases get dropped;   (3) the notion that the main loop might run a few extra times with nothing to do, provided that, thereby, it is assured never to “sleep through” the signal that is its appointed purpose in life.

        If you use a thread, it will run truly-concurrently with the main, and all the usual shared-variable caveats apply.   Unlike, say, Ruby, these are “real” operating-system threads.   Fortunately, Perl’s implementation is (mostly ... *wink!*) friendly.

          A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1143554]
Approved by stevieb
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (4)
As of 2019-12-15 08:34 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?