in reply to fixed set of forked processes

My thanks to all who offered advice. Here is the solution now put into practice (most of the irrelevant code omitted). Note I had to use a homegrown alternative to FileHandle because its documentation didn't mention support of Open3. That was just an array of self-numbering filehandles for easy deletion.
package jiloader; use Parallel::ForkManager; use IPC::Open3; use Fcntl qw(:flock SEEK_END); sub new { ... $opt { MAXFORKS } ||= 31; $opt{ PM } = new Parallel::ForkManager( $opt{ MAXFORKS } ); $opt{ PMFH } = []; # filehandle pool ... # example of one of four files that track things unlink $opt{REJFILE}; open my $rejh, ">>$opt{REJFILE}" or die "$!: $opt{REJFILE}\n"; $opt{ REJH } = $rejh; ... bless \%opt; } ... sub put { # batch changes by outer box # for submission to fork scheduler my $self = shift; if ( $self -> { REST } ) { if ( $self -> { TOPBOX } ) { $self -> { BATCH } and $self -> sched; } $self -> { BATCH } .= $self -> { CHG }; } else { $self -> { BATCH } .= $self -> { CHG }; $self -> sched; } } sub sched { my $self = shift; my $pm = $self -> { PM }; my $rh = $self -> getfh; my $wh = $self -> getfh; my $eh = $self -> getfh; # parent has allocated fh's so has to # free them when child exits # child cannot do this whatever the fh pooling solution $pm -> run_on_finish( sub { $self -> killfh( $rh, $wh, $eh ); +} ); unless( $pm -> start ) { open3 $wh, $rh, $eh, $self -> { JILCOMMAND }; print $wh $self -> { BATCH }; close $wh; unless ( $self -> jiloutparse( $rh, $eh ) ) { my $errh = $self -> { ERRH }; flock $self -> { ERRFILE }, LOCK_EX; print $errh $self -> { LASTOUT }; print $errh $self -> { LASTERR }; flock $self -> { ERRFILE }, LOCK_UN } close $rh; close $eh; $pm -> finish; } $self -> { BATCH } = ''; } ...

One world, one people