http://qs1969.pair.com?node_id=849143


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

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". ;-)