IPC-Manager
view release on metacpan or search on metacpan
t/unit/sigkill_cleanup_ConnectionUnix.t view on Meta::CPAN
use Test2::V0;
use Test2::Require::Module 'IO::Socket::UNIX' => '1.55';
use File::Temp qw/tempdir/;
use File::Spec;
use IPC::Manager::Client::ConnectionUnix;
use IPC::Manager::Serializer::JSON;
my $S = 'IPC::Manager::Serializer::JSON';
my $C = 'IPC::Manager::Client::ConnectionUnix';
# Fork a child, run $code->($route) in it, then SIGKILL the child while
# whatever refs the callback returned are still in scope -- so DESTROY
# does not fire on them before SIGKILL delivers (DESTROY would call
# disconnect, which runs pre_disconnect_hook and is exactly what we want
# to skip to simulate ungraceful death). POSIX::_exit avoids the END
# / DESTROY cascade in case SIGKILL is somehow deferred.
sub kill_child_after {
my ($route, $code) = @_;
my $pid = fork;
die "fork failed: $!" unless defined $pid;
if (!$pid) {
my @keep = $code->($route);
kill 'KILL', $$;
require POSIX;
POSIX::_exit(0);
}
waitpid($pid, 0);
return $pid;
}
subtest 'reap-and-replace after SIGKILL' => sub {
my $dir = tempdir(CLEANUP => 1);
kill_child_after($dir, sub {
my $r = shift;
my $c = $C->new(serializer => $S, route => $r, id => 'victim');
return $c;
});
ok(-e File::Spec->catfile($dir, 'victim'), 'stale socket on disk after SIGKILL');
ok(-e File::Spec->catfile($dir, 'victim.pid'), 'stale pidfile on disk after SIGKILL');
my $con;
ok(
lives { $con = $C->new(serializer => $S, route => $dir, id => 'victim') },
'fresh registration with same id succeeds (reap-and-replace)',
) or note $@;
is($con->id, 'victim', 'id matches');
$con->disconnect;
};
subtest 'peer_left sweeps dead-pid artifacts' => sub {
my $dir = tempdir(CLEANUP => 1);
my $observer = $C->new(serializer => $S, route => $dir, id => 'observer');
kill_child_after($dir, sub {
my $r = shift;
my $c = $C->new(serializer => $S, route => $r, id => 'doomed');
return $c;
});
ok(-e File::Spec->catfile($dir, 'doomed'), 'stale socket on disk');
ok(-e File::Spec->catfile($dir, 'doomed.pid'), 'stale pidfile on disk');
my $removed = $observer->peer_left;
is($removed, 1, 'peer_left reaped one stale entry');
ok(!-e File::Spec->catfile($dir, 'doomed'), 'stale socket gone after peer_left');
ok(!-e File::Spec->catfile($dir, 'doomed.pid'), 'stale pidfile gone after peer_left');
$observer->disconnect;
};
subtest 'peers() filters dead-pid peers' => sub {
my $dir = tempdir(CLEANUP => 1);
( run in 0.670 second using v1.01-cache-2.11-cpan-9581c071862 )