Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Handling SIG INT multiple times

by delight (Initiate)
on Oct 01, 2015 at 14:15 UTC ( [id://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
      A reply falls below the community's threshold of quality. You may see it by logging in.
      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!
      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
Domain Nodelet?
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?Last hourOther CB clients
Other Users?
Others studying the Monastery: (5)
As of 2024-04-24 02:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found