$self->{routers} = []; # list of registered routers
$self->{active_list} = []; # list of active children
$self->{idle_list} = []; # list of idle children
+ $self->{zombie_list} = []; # list of reaped children to clean up
$self->{sighup_pending} = [];
$self->{pid_map} = {}; # map of child pid to child for cleaner access
$self->{sig_pipe} = 0; # true if last syswrite failed
my $from_network = 0;
$self->check_status;
+ $self->squash_zombies;
$self->{child_died} = 0;
my $msg = shift(@max_children_msg_queue);
}
# ----------------------------------------------------------------
+# Finish destroying objects for reaped children
+# ----------------------------------------------------------------
+sub squash_zombies {
+ my $self = shift;
+
+ my $squashed = 0;
+ while (my $child = shift @{$self->{zombie_list}}) {
+ delete $child->{$_} for keys %$child; # destroy with a vengeance
+ $squashed++;
+ }
+ $chatty and $logger->internal("server: squashed $squashed zombies");
+}
+
+# ----------------------------------------------------------------
# Launch a new spare child or kill an extra spare child. To
# prevent large-scale spawning or die-offs, spawn or kill only
# 1 process per idle maintenance loop.
$self->{num_children}--;
delete $self->{pid_map}->{$pid};
- delete $child->{$_} for keys %$child; # destroy with a vengeance
+
+ # since we may be in the middle of check_status(),
+ # stash the remnants of the child for later cleanup
+ # after check_status() has finished; otherwise, we may crash
+ # the parent with a "Use of freed value in iteration" error
+ push @{ $self->{zombie_list} }, $child;
}
$self->spawn_children unless $shutdown;