sub _scalar_to_string
{
my $s = shift;
return 'null' unless defined $s;
if (ref $s) # object other than plain HASH, ARRAY, or SCALAR?
{
#... (this is something JSON doesn't handle)
}
elsif (looks_like_number($s))
{
return $s
}
else
{
eval {
$s = "" . $s;
$s =~ s[\\][\\\\]g; ### error points here
$s =~ s["][\\"]g;
$s =~ s[\n][\\n]g;
$s =~ s[[\b]][\\b]g;
$s =~ s[\f][\\f]g;
$s =~ s[\r][\\r]g;
$s =~ s[\t][\\t]g;
$s =~ s[([^[:print:]])][sprintf "\\u%04x", ord $1]eg;
1;
} and return qq["$s"];
return qq[":$@:$s"];
}
}
####
End process handler: ":Modification of a read-only value attempted at $file line 228.
:172.23.1.10"
####
# keep track of the reaped children so we can deal with them
# later.
push @reaped, delete $pids{$child};
####
#!/usr/bin/perl
use JSON;
sub UNIVERSAL::TO_JSON { "@_" }
use strict;
use warnings;
use Time::HiRes qw(sleep);
use Scalar::Util qw(refaddr weaken readonly);
use POSIX qw(:sys_wait_h);
my $sub_kids = 30;
$|=1;
sub run
{
my %rcs;
my %pids;
my $reaper;
my @reaped;
$reaper = sub {
my $child = shift;
my $rc = shift;
if ($pids{$child})
{
$rcs{$pids{$child}}{rc} = $rc;
print "Child: $child RC: $rc ITER: $pids{$child}\n";
# keep track of the reaped children so we can deal with them
# later.
push @reaped, delete $pids{$child};
print 'reaped list: ', encode_json(\@reaped), "\n"; # JSON is very compact cf Data::Dump et al
return 1;
}
return 0;
};
register_sigchild($reaper);
my $dealt_with = 0;
my $deal_with_children = sub {
++$dealt_with;
my $child = shift @reaped;
if(readonly($child))
{
print "********child readonly?**************\n" ;
die;
}
print "Dealing with: ", encode_json([$child]), "\n";
print " - got: ", encode_json($rcs{$child}), "\n"# if $child;
# code shouldn't make a difference here.
};
for my $kid (1..$sub_kids)
{
my $pid = run_async($kid % 2 ? '/bin/true' : '/bin/false');
$pids{$pid} = $kid;
}
while (@reaped or keys %pids)
{
print "Waiting for children to end: ", encode_json(\%pids), "\n";
sleep 10 unless @reaped;
while (@reaped)
{
$deal_with_children->();
}
}
unless ($dealt_with == $sub_kids)
{
warn "Only dealt with $dealt_with of $sub_kids?";
}
unregister_sigchild($reaper);
}
sub run_async
{
# normally, I use IPC::Open3, but here we're just being quick/dirty.
# with that, we're assuming fork will work.
my $pid = fork();
return $pid if $pid;
die "failed to fork" unless defined $pid;
sleep 0.01 * rand(50);
exec @_;
die "Huh? exec failed?"
}
my %sigs;
sub register_sigchild
{
my $sig = shift;
$sigs{refaddr $sig} = {
sig => $sig,
from => (caller(1))[3],
};
weaken($sigs{refaddr $sig}{sig});
$SIG{CHLD} = \&handle_sigchild;
}
my @buffer;
sub handle_sigchild
{
my $child;
unregister_sigchild();
while (($child = 0+ waitpid(-1, WNOHANG)) > 0)
{
my $rc = 0+ $?;
print "Child: $child RC: $?\n";
push @buffer, [$child, $rc];
}
my @exited = @buffer;
@buffer = ();
for my $exit (@exited)
{
for my $sig (values %sigs)
{
my $last;
my $ref = $sig->{sig}; # ensure strong reference now
next unless $ref;
if (
eval {
$last = 1 if $ref->(@$exit);
1;
}
)
{
last if $last;
}
else
{
delete $sigs{refaddr $ref};
}
# no one handled yet?
push @buffer, $exit;
}
}
unregister_sigchild();
}
sub unregister_sigchild
{
if (@_)
{
delete $sigs{refaddr $_[0]} if $_[0];
$_[0] = undef; # action at a distance!
}
for my $addr (keys %sigs)
{
delete $sigs{$addr} unless defined $sigs{$addr}{sig};
}
print 'About to set $SIG{CHLD}', "\n";
$SIG{CHLD} = keys %sigs ? \&handle_sigchild : 'DEFAULT';
#print 'SIGS: ', encode_json(\%sigs), "\n";
}
run();