Proc-Reliable
view release on metacpan or search on metacpan
Reliable.pm view on Meta::CPAN
else {
$do_single_list = !wantarray();
}
my($pid, $t, $i);
my $ntry= 0;
my $starttime= time();
my $endtime= time() + $self->maxtime();
my $time_per_try= $self->time_per_try();
my $patout= $self->pattern_stdout();
my $paterr= $self->pattern_stderr();
my $redo = 0;
#foreach $t (keys(%$self)) {
# print("$t $self->{$t}\n");
#}
$t = 0;
# initialize object output variables
$self->{msg} = undef;
my($fileno_getstdout,
$fileno_getstderr,
$fileno_getstdin,
$fileno_putstdout,
$fileno_putstderr,
$fileno_putstdin);
while(1) {
$Debug && $self->_dprt("ATTEMPT $ntry: '$cmdstr' ");
# initialize object output variables
$self->{stdout} = undef;
$self->{stderr} = undef;
$self->{status} = undef;
# set up pipes to collect STDOUT and STDERR from child process
pipe(GETSTDOUT,PUTSTDOUT) || die("couldn't create pipe 1");
pipe(GETSTDERR,PUTSTDERR) || die("couldn't create pipe 2");
$fileno_getstdout = fileno(GETSTDOUT) || die("couldn't get fileno 1");
$fileno_getstderr = fileno(GETSTDERR) || die("couldn't get fileno 2");
$fileno_putstdout = fileno(PUTSTDOUT) || die("couldn't get fileno 3");
$fileno_putstderr = fileno(PUTSTDERR) || die("couldn't get fileno 4");
PUTSTDOUT->autoflush(1);
PUTSTDERR->autoflush(1);
if(defined($inputref)) {
pipe(GETSTDIN,PUTSTDIN) || die("couldn't create pipe 3");
$fileno_getstdin = fileno(GETSTDIN) || die("couldn't get fileno 5");
$fileno_putstdin = fileno(PUTSTDIN) || die("couldn't get fileno 6");
PUTSTDIN->autoflush(1);
}
# fork starts a child process, returns pid for parent, 0 for child
STDOUT->flush(); # don't dup a non-empty buffer
$redo = 0;
#jvr added
my($oldsigchld) = $SIG{CHLD};
$SIG{CHLD} = sub { $self->_collect_child(); };
##### PARENT PROCESS #####
if($pid = fork()) {
# close the ends of the pipes the child will be using
close(PUTSTDOUT);
close(PUTSTDERR);
if(defined($inputref)) {
close(GETSTDIN);
}
#print("sigs 1: ",$SIG{ALRM}," , ",$SIG{PIPE}," , ",$SIG{CHLD},"\n");
# set up handler to collect child return status no matter when it dies
eval {
# exit the eval if child takes too long or dies abnormally
local $SIG{ALRM} = sub { die("SIGALRM") };
local $SIG{PIPE} = sub { die("SIGPIPE") };
#print("sigs 2: ",$SIG{ALRM}," , ",$SIG{PIPE}," , ",$SIG{CHLD},"\n");
$t = min($endtime - time(), $time_per_try);
if($t < 1) {
return 1;
}
alarm($t);
# set up and do a select() to read/write the child to avoid deadlocks
my($stdinlen);
my($stdoutdone, $stderrdone, $stdindone) = (0, 0, 0);
my($nfound, $fdopen, $bytestodo, $blocksize, $s);
my($rin, $win, $ein) = ('', '', '');
my($rout, $wout, $eout) = ('', '', '');
my($gotread) = 0;
# bug: occational death with: 'Modification of a read-only value attempted at /home/public/dgold/acsim//Proc/Reliable.pm line 416.'
vec($rin, $fileno_getstdout, 1) = 1;
vec($rin, $fileno_getstderr, 1) = 1;
$blocksize = (stat(GETSTDOUT))[11];
$fdopen = 2; # stdout and stderr
if(defined($inputref)) {
# bug: same bug here
vec($win, $fileno_putstdin, 1) = 1;
$stdinlen = length($$inputref);
if($self->in_after_out_closed()) {
$fdopen++;
}
}
my $cbStdout = $self->{stdout_cb};
my $cbStderr = $self->{stderr_cb};
my ($outs,$oute);
while($fdopen) {
$nfound = select($rout=$rin, $wout=$win, $eout=$ein, undef);
if(defined($win) && vec($wout, $fileno_putstdin, 1)) { # ready to write
#print("write ready\n");
my($indone) = 0;
if($self->input_chunking()) {
if($gotread) {
$gotread = 0;
my($inputline) = shift(@inputlines) . "\n";
$stdinlen = length($inputline);
#print("writing $stdinlen '$inputline'\n");
Reliable.pm view on Meta::CPAN
#print("bytes processed: $stdindone $stdoutdone $stderrdone\n");
#if($self->input_chunking() && scalar(@inputlines)) {
# print(scalar(@inputlines) . " lines of stdin not fed\n");
#}
alarm(0);
return 1;
}; # end of eval
# check return status of eval()
if($@) { # exited from eval() via die()
if($@ =~ /SIG(ALRM|PIPE)/) {
my($sig) = $1;
if($sig eq "ALRM") {
$self->{msg} .= "Timed out after $t seconds\n";
}
else {
$self->{msg} .= "Pipe error talking to subprocess\n";
}
$redo++;
}
else { # only a code bug should get here
croak("unexpected error talking to subprocess: '$@'");
}
}
# wait until child exits, kill it if it doesn't.
# normally child will exit shortly unless eval failed via SIGALRM.
# if eval() succeeded, wait up to child_exit_time for child to exit
my($s) = 0;
while(!$redo && !defined($self->{status}) && kill(0, $pid) && ($s < $self->child_exit_time)) {
#print("waiting for exit\n");
select(undef, undef, undef, $_WAIT_INCR_SEC);
$s += $_WAIT_INCR_SEC;
}
# if child has not exited yet, send sigterm.
if(!defined($self->{status}) && kill(0, $pid) && $self->sigterm_exit_time) { # child still alive
#print("sending term\n");
kill('TERM', $pid);
}
# wait until process exits or wait-time is exceeded.
$s = 0;
while(!defined($self->{status}) && kill(0, $pid) && ($s < $self->sigterm_exit_time)) {
select(undef, undef, undef, $_WAIT_INCR_SEC);
$s += $_WAIT_INCR_SEC;
}
if(!defined($self->{status}) && kill(0, $pid) && $self->sigkill_exit_time) { # child still alive
#print("sending kill\n");
kill('KILL', $pid);
}
# wait until process exits or wait-time is exceeded.
$s = 0;
while(!defined($self->{status}) && kill(0, $pid) && ($s < $self->sigkill_exit_time)) {
select(undef, undef, undef, $_WAIT_INCR_SEC);
$s += $_WAIT_INCR_SEC;
}
$SIG{CHLD} = $oldsigchld; # why is this giving '-w' warning?
#print("sigs 3: ",$SIG{ALRM}," , ",$SIG{PIPE}," , ",$SIG{CHLD},"\n");
if(!defined($self->{status})) {
if(kill(0, $pid)) {
# get here if unable to kill or if coredump takes longer than sigkill_exit_time
$self->{msg} .= "unable to kill subprocess $pid";
}
$self->{status} = -1;
$self->{msg} .= "no return status from subprocess\n";
}
else {
if(kill(0, $pid)) {
# most likely coredumping?
$self->{msg} .= "got return status but subprocess still alive\n";
}
}
}
##### CHILD PROCESS #####
elsif(defined($pid)) { # if child process: $pid == 0
#jvr added
$SIG{CHLD} = 'DEFAULT';
close(GETSTDOUT); close(GETSTDERR);
if(defined($inputref)) {
close(PUTSTDIN);
}
open(STDOUT, ">&=PUTSTDOUT") || croak("Couldn't redirect STDOUT: $!");
if($do_single_list) {
open(STDERR, ">&=PUTSTDOUT") || croak("Couldn't redirect STDERR: $!");
}
else {
open(STDERR, ">&=PUTSTDERR") || croak("Couldn't redirect STDERR: $!");
}
if(defined($inputref)) {
open(STDIN, "<&=GETSTDIN") || croak("Couldn't redirect STDIN: $!");
}
my($status) = -1;
if(ref($cmd) eq "CODE") {
$status = &$cmd; # Start perl subroutine
}
elsif(ref($cmd) eq "ARRAY") { # direct exec(), no shell parsing
exec(@$cmd);
#croak("exec() failure: '$!'"); # causes warnings with '-w'
}
else { # start shell process
exec($cmd);
#croak("exec() failure: '$!'"); # causes warnings with '-w'
}
# we get here for the perl subroutine normally.
exit $status;
}
##### FORK FAILURES #####
elsif($! =~ /No more process/) { # temporary fork error
( run in 0.680 second using v1.01-cache-2.11-cpan-71847e10f99 )