eval {
$var = #setup a network connection...;
$SIG{ALRM} = sub { flock $fh, LOCK_EX;
print $fh "\n";
flock $fh, LOCK_UN;};
alarm(60);
};
####
use strict;
use warnings;
use Fcntl qw(:flock);
use File::Copy 'move';
use POSIX "sys_wait_h"; #for waitpid FLAGS
use Time::Local;
$|=1;
my @child_sleeps = qw(10 15 12 5 12);
my $start_epoch = time();
# Fire off number of child processes equal to the
# number of elements in @child_sleeps;
# Then the parent who started these little guys,
# goes into a blocking wait until they all finish
# Each child can return a status code via exit($code_number).
# However, this code doesn't use that and instead assumes
# that children and the parent are all writing to a common
# file shared via a cooperative flock
$SIG{CHLD} = 'IGNORE';
open(my $fh_log, '>>', "Alogfile.txt") or die "unable to open Alogfile.txt $!";
#$fh_log->autoflush; #not needed this is automatic before locking or unlocking a file!
foreach my $sleep_interval (@child_sleeps)
{
if(my $pid = fork)
{ # parent
safe_print ($fh_log, "Spawned child $pid lasting $sleep_interval seconds\n");
}
elsif(defined $pid ) # pid==0
{ # child
safe_print ($fh_log, "This is child pid $$. I will sleep for $sleep_interval seconds!\n");
sleep($sleep_interval);
safe_print ($fh_log, "Child $$ time is up!! I am gonna croak!\n");
exit(0);
}
else
{ # fork failed pid undefined
die "MASSIVE ERROR - FORK FAILED with $!";
}
}
### now wait for all children to finish, no matter who they are
1 while wait != -1 ; # avoid zombies this is a blocking operation
safe_print ($fh_log, "Parenting talking...all my children are dead! Hooray!\n");
close $fh_log; #must close file before renaming it!
unlink "Alogfile.back" if ( -e "Alogfile.back");
move "AlogFile.txt", "AlogFile.back" or die "unable to rename log file! $!";
print "A happy ending!\n";
sub safe_print
{
my ($fh, @text) = @_;
my $now_epoch = time();
my $delta_secs = $now_epoch - $start_epoch;
flock $fh, LOCK_EX or die "flock can't get lock $!";
print $fh "$delta_secs secs: $_" foreach @text;
print "$delta_secs secs: $_" foreach @text;
flock $fh, LOCK_UN or die "flock can't release lock $!";
}
__END__
Contents of AlogFile.back after a run:
0 secs: Spawned child -10680 lasting 10 seconds
0 secs: This is child pid -10680. I will sleep for 10 seconds!
0 secs: Spawned child -23840 lasting 15 seconds
0 secs: This is child pid -23840. I will sleep for 15 seconds!
0 secs: Spawned child -14556 lasting 12 seconds
0 secs: This is child pid -14556. I will sleep for 12 seconds!
0 secs: Spawned child -13972 lasting 5 seconds
0 secs: This is child pid -13972. I will sleep for 5 seconds!
0 secs: Spawned child -18600 lasting 12 seconds
0 secs: This is child pid -18600. I will sleep for 12 seconds!
5 secs: Child -13972 time is up!! I am gonna croak!
10 secs: Child -10680 time is up!! I am gonna croak!
12 secs: Child -14556 time is up!! I am gonna croak!
12 secs: Child -18600 time is up!! I am gonna croak!
15 secs: Child -23840 time is up!! I am gonna croak!
15 secs: Parenting talking...all my children are dead! Hooray!
A happy ending!
####
use strict;
use warnings;
use Fcntl qw(:flock);
use File::Copy 'move';
use POSIX "sys_wait_h"; #for waitpid FLAGS
my @child_sleeps = qw(10 15 12 5 12);
# Fire off number of child processes equal to the
# number of elements in @child_sleeps;
# Then the parent who started these little guys,
# goes into a blocking wait until they all finish
# Each child can return a status code via exit($code_number).
# However, this code doesn't use that and instead assumes
# that children and the parent are all writing to a common
# file shared via a cooperative flock
$SIG{CHLD} = 'IGNORE';
open(my $fh_log, '>>', "Alogfile.txt") or die "unable to open Alogfile.txt $!";
#$fh_log->autoflush; #not needed this is automatic before locking or unlocking a file!
foreach my $sleep_interval (@child_sleeps)
{
if(my $pid = fork)
{ # parent
safe_print ($fh_log, "Spawned child $pid lasting $sleep_interval seconds\n");
}
elsif(defined $pid ) # pid==0
{ # child
safe_print ($fh_log, "This is child pid $$. I will sleep for $sleep_interval seconds!\n");
sleep($sleep_interval);
safe_print ($fh_log, "Child $$ time is up!! I am gonna croak!\n");
exit(0);
}
else
{ # fork failed pid undefined
die "MASSIVE ERROR - FORK FAILED with $!";
}
}
### now wait for all children to finish, no matter who they are
1 while wait != -1 ; # avoid zombies this is a blocking operation
close $fh_log; #must close file before renaming it!
unlink "Alogfile.back" if ( -e "Alogfile.back");
move "AlogFile.txt", "AlogFile.back" or die "unable to rename log file! $!";
print "A happy ending!\n";
sub safe_print
{
my ($fh, @text) = @_;
flock $fh, LOCK_EX or die "flock can't get lock $!";
print $fh @text;
flock $fh, LOCK_UN or die "flock can't release lock $!";
}
__END__
Results from testing on Windows (they use a negative PID for their fork emulation)
Spawned child -20688 lasting 10 seconds
This is child pid -20688. I will sleep for 10 seconds!
Spawned child -19896 lasting 15 seconds
This is child pid -19896. I will sleep for 15 seconds!
Spawned child -24392 lasting 12 seconds
This is child pid -24392. I will sleep for 12 seconds!
Spawned child -24692 lasting 5 seconds
This is child pid -24692. I will sleep for 5 seconds!
Spawned child -20296 lasting 12 seconds
This is child pid -20296. I will sleep for 12 seconds!
Child -24692 time is up!! I am gonna croak!
Child -20688 time is up!! I am gonna croak!
Child -24392 time is up!! I am gonna croak!
Child -20296 time is up!! I am gonna croak!
Child -19896 time is up!! I am gonna croak!