EV-cares
view release on metacpan or search on metacpan
t/14_concurrent.t view on Meta::CPAN
use EV::cares qw(:status :types);
# Multiple resolver instances on the SAME EV (default) loop must not
# cross-talk: each has its own c-ares channel, its own hosts file, its
# own per-channel options. This catches any per-loop-vs-per-channel
# assumption mistakes.
# Two distinct hosts files: identical query name, different answers
my $tmp_a = File::Temp->new(SUFFIX => '.hosts');
print $tmp_a "10.0.0.1 shared-host\n";
close $tmp_a;
my $tmp_b = File::Temp->new(SUFFIX => '.hosts');
print $tmp_b "10.0.0.2 shared-host\n";
close $tmp_b;
my $r_a = EV::cares->new(lookups => 'f', hosts_file => $tmp_a->filename);
my $r_b = EV::cares->new(lookups => 'f', hosts_file => $tmp_b->filename);
isnt("$r_a", "$r_b", 'two resolvers are distinct instances');
# Fan out simultaneously
my (@got_a, @got_b);
my ($done_a, $done_b);
$r_a->resolve('shared-host', sub { @got_a = @_; $done_a = 1 });
$r_b->resolve('shared-host', sub { @got_b = @_; $done_b = 1 });
my $t = EV::timer 5, 0, sub { $done_a = $done_b = 1 };
EV::run until $done_a && $done_b;
is($got_a[0], ARES_SUCCESS, 'resolver A succeeded');
is($got_b[0], ARES_SUCCESS, 'resolver B succeeded');
ok(grep({ $_ eq '10.0.0.1' } @got_a[1..$#got_a]),
'A gets its own answer (10.0.0.1)');
ok(grep({ $_ eq '10.0.0.2' } @got_b[1..$#got_b]),
'B gets its own answer (10.0.0.2)');
ok(!grep({ $_ eq '10.0.0.2' } @got_a[1..$#got_a]),
'A did not see B answer');
ok(!grep({ $_ eq '10.0.0.1' } @got_b[1..$#got_b]),
'B did not see A answer');
is($r_a->active_queries, 0, 'A active_queries == 0 after callback');
is($r_b->active_queries, 0, 'B active_queries == 0 after callback');
# Many resolvers concurrently
{
my @resolvers = map EV::cares->new(lookups => 'f'), 1..5;
my $count = 0;
for my $r (@resolvers) {
$r->resolve('localhost', sub { $count++ });
}
my $t2 = EV::timer 5, 0, sub { EV::break };
EV::run until $count >= 5;
is($count, 5, '5 concurrent resolvers each got their callback');
is($_->active_queries, 0, 'each instance settles to 0') for @resolvers;
}
# Independent destroy: destroying one resolver leaves others alive
{
my $live = EV::cares->new(lookups => 'f');
my $doomed = EV::cares->new(lookups => 'f');
$doomed->destroy;
is($doomed->is_destroyed, 1, 'doomed is destroyed');
is($live->is_destroyed, 0, 'live is unaffected');
my $done;
$live->resolve('localhost', sub { $done = 1 });
my $t3 = EV::timer 5, 0, sub { $done = 1 };
EV::run until $done;
pass('live resolver still works after sibling destroy');
}
done_testing;
( run in 0.415 second using v1.01-cache-2.11-cpan-a1f116cd669 )