Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Re^2: SIG{INT} not handling CTRL-C ???

by girishatreya2005 (Novice)
on Jul 12, 2010 at 15:03 UTC ( #849026=note: print w/ replies, xml ) Need Help??


in reply to Re: SIG{INT} not handling CTRL-C ???
in thread SIG{INT} not handling CTRL-C ???

@Neighbour

Thank you very much.

Active waiting in the main did the trick.


Comment on Re^2: SIG{INT} not handling CTRL-C ???
Re^3: SIG{INT} not handling CTRL-C ???
by afoken (Parson) on Jul 13, 2010 at 01:59 UTC

    No, just sleeping won't do the trick. It will bite you again as soon as one of your forked processes needs longer than that. And it will waste resources and your time if all of your forked processes are faster than the delay.

    Use the proper way: Remember the PIDs of the forked processes, and wait() for all of your forked processes. End your main process when you have waited for all processes.

    Stupid example:

    #!/usr/bin/perl use 5.010; use strict; use warnings; $|=1; # autoflush my %pids; for (1..10) { my $pid=fork() // die "Can't fork: $!"; if ($pid) { # parent, remember PID $pids{$pid}++; say "Forked child $_, PID=$pid"; } else { # child say "Child process $_"; sleep (5+rand(5)); exit 0; } } while (%pids) { my $pid=wait(); delete $pids{$pid}; say "child with PID $pid exited"; } say "all childs exited";

    If you want to do some work in your parent process, rely on SIGCHLD instead. SIGCHLD will be sent to your parent process as soon as a child process exits.

    Stupid example:

    #!/usr/bin/perl use 5.010; use strict; use warnings; use POSIX ":sys_wait_h"; $|=1; # autoflush my %pids; sub reaper { $SIG{CHLD}=\&reaper; # This does not work, because you may get only one signal when # two or more processes exit at nearly the same time. This is due # to the way signals are implemented. #my $pid=wait(); #delete $pids{$pid}; my $pid; do { $pid=waitpid(-1, WNOHANG); delete $pids{$pid}; } while $pid>0; } $SIG{CHLD}=\&reaper; for (1..10) { my $pid=fork() // die "Can't fork: $!"; if ($pid) { # parent, remember PID $pids{$pid}++; say "Forked child $_, PID=$pid"; } else { # child $SIG{CHLD}='DEFAULT'; # maybe not needed, but doesn't hurt say "Child process $_"; sleep (5+rand(5)); exit 0; } } while (keys %pids) { say "Parent is working, ",scalar keys %pids," children still alive + (PIDS ",join(', ',keys %pids),")..."; sleep 1; } say "all childs exited";

    Note that you should keep the signal handler routine as short as possible, and that you should avoid any I/O there. Just set or clear a flag and return as fast as possible. With modern Perl implementations, you usually have "safe signals" that allow to do more in a signal handler, but you can disable them for speed or other special purposes. And in older Perl implementations, unsafe signals are standard and WILL bite you very fast.

    And now, propagating SIGINT:

    You know the PIDs of all of your child processes, because you need them for wait()/waitpid(). So it is no problem to use it to send SIGINT to your child processes:

    #!/usr/bin/perl use 5.010; use strict; use warnings; use POSIX ":sys_wait_h"; $|=1; # autoflush my %pids; sub reaper { $SIG{CHLD}=\&reaper; # This does not work, because you may get only one signal when # two or more processes exit at nearly the same time. This is due # to the way signals are implemented. #my $pid=wait(); #delete $pids{$pid}; my $pid; do { $pid=waitpid(-1, WNOHANG); delete $pids{$pid}; } while $pid>0; } $SIG{CHLD}=\&reaper; sub sigint { $SIG{INT}=\&sigint; kill INT => keys %pids; } $SIG{INT}=\&sigint; for (1..10) { my $pid=fork() // die "Can't fork: $!"; if ($pid) { # parent, remember PID $pids{$pid}++; say "Forked child $_, PID=$pid"; } else { # child $SIG{CHLD}='DEFAULT'; # maybe not needed, but doesn't hurt $SIG{INT}='DEFAULT'; say "Child process $_"; sleep (5+rand(5)); exit 0; } } while (keys %pids) { say "Parent is working, ",scalar keys %pids," children still alive + (PIDS ",join(', ',keys %pids),")..."; sleep 1; } say "all childs exited";

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

      In my implementation ,the parent as well as it's children will be running in an infinite loop.

      In this scenario, would defining the CTRL-C handler to kill the children when I hit the CTRL-C key be a feasible option?

        If you set up a SIGINT handler like mine, it will propagate the SIGINT to all children you track in %pids. That's all.

        If you want SIGINT to also abort your main program, you must somehow abort the infinite loop in it. My examples stop as soon as all children have exited. That may or may not be sufficient. Perhaps you also need to set a "stop" flag inside the SIGINT handler, and modify the infinite loop to break as soon as the "stop" flag is set.

        What your forked child processes do when they receive a SIGINT depends on what signal handler they have installed. They may simply ignore the signal. They may have the default handler installed that terminates the process. They may have a custom handler installed that does a completely different thing.

        There is a convention for sending signals to abort a process gracefully, in order of decreasing politeness: First, you send SIGINT (i.e. tell the process "the user wants you to stop what you are doing") once or twice, and wait a little while. If that does not terminate the process, try SIGHUP (connection hang up) if the process runs on a terminal. (Daemons often abuse SIGUP to re-read their configuration.) Next, try SIGTERM ("terminate now"). If even that does not work, and you don't care about data loss, nuke the process out of the process list by sending it the SIGKILL signal. That signal can not be caught, it WILL terminate the process without giving it a chance to save data.

        Have a look at perlipc, especially the signals section, and at http://en.wikipedia.org/wiki/Signal_%28computing%29.

        Update: Also note that Windows has no concept of signals, just a few signals are emulated by the Perl ports for Windows, and the emulation is not perfect. I.e. don't use signals on Windows.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://849026]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (7)
As of 2014-09-19 07:24 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (132 votes), past polls