my $chld_flag = 0; $SIG{'CHLD'} = sub { $chld_flag = 1; }; $SIG{'ALRM'} = 'IGNORE'; while (1) { # Check child processes # Includes using waidpid() to clean up after child processes # Display status # Get user commands my $cmd = ''; eval { # Allow interrupts by alarms and 'child' events local $SIG{'CHLD'} = sub { die("CHLD EVENT\n"); }; local $SIG{'ALRM'} = sub { die("ALRM EVENT\n"); }; alarm(10); if (! $chld_flag) { $cmd = ; } }; alarm(0); $chld_flag = 0; # Process user command, if any # Such as launch child processes } #### $SIG{'USR1'} = sub { $flag = 1; }; eval { local $SIG{'USR1'} = sub { die "Timeout\n"; }; DoWork(); }; #### eval { eval { # Allow interrupts by alarms and 'child' events local $SIG{'CHLD'} = sub { die("CHLD EVENT\n"); }; local $SIG{'ALRM'} = sub { die("ALRM EVENT\n"); }; alarm(10); if (! $chld_flag) { $cmd = ; } }; }; #### #!/usr/bin/perl ##### # # Test program to reproduce the following Perl bug: # It is possible for a signal handler defined locally inside an # eval blocks to be executed outside the scope of the eval block. # # Just execute this Perl script. # It will eventually (after a few minutes) exit when the bug occurs. # ##### use strict; use warnings; use Time::HiRes qw( usleep ); my $CHILD_MAX = 25; # Max number of children to run my $child_count = 0; # Count of children currently running my $child_done = 0; # Flag that a child has terminated my %child_pids; # Holds child processes' PIDs # Set the flag that a child has terminated $SIG{'CHLD'} = sub { $child_done = 1; }; # Loop until the bug occurs do { # Cleanup any terminated children if ($child_done) { $child_done = 0; # Check all child processes using non-blocking waitpid() call foreach my $pid (keys(%child_pids)) { if (waitpid($pid, 1) == $pid) { # 1 = POSIX::WNOHANG delete($child_pids{$pid}); $child_count--; } } } # Start more children while ($child_count < $CHILD_MAX) { my $pid; if (($pid = fork()) == 0) { # Child sleeps for a random amount of time and then exits my $usec = 950000 + int(rand(100000)); usleep($usec); exit(0); } # Parent remembers the child's PID for later cleanup $child_pids{$pid} = undef; $child_count++; } # Try to recreate the bug eval { eval { # Local signal handler to 'kill' the sleep() call below local $SIG{'CHLD'} = sub { die("SIGCHLD\n"); }; sleep(1); # Hang around a bit }; # Set the flag for cleaning up terminated child processes if ($@ && ($@ =~ /CHLD/)) { $child_done = 1; } }; # Keep looping until the bug occurs } while (! $@); # When we get here, it shows that the signal handler # defined inside the inner eval block above was # executed OUTSIDE the scope of the inner eval block! print("Bug detected: $@"); exit(1); # EOF