EV-cares
view release on metacpan or search on metacpan
lib/EV/cares.pm view on Meta::CPAN
for my $node (@unique) {
$self->getaddrinfo($node, $service, $hints, sub {
my ($status, @addrs) = @_;
$res{$node} = { status => $status, addrs => \@addrs };
$cb->(\%res) if --$pending == 0;
});
}
return;
}
# is_busy: true iff there are pending queries on this resolver. Cheap
# wrapper for the most common active_queries comparison.
sub is_busy { $_[0]->active_queries > 0 }
# wait_idle($timeout_seconds): pump the EV loop until either all of this
# resolver's pending queries complete or the timeout elapses. Returns
# true if the channel drained, false on timeout. Useful in mostly-
# synchronous scripts that want to ensure callbacks have run before
# proceeding. Returns immediately if the resolver is already idle.
#
# Picks up a custom EV::Loop passed to new(loop => $loop): the timer and
# the run() call are dispatched on the same loop the resolver's watchers
# are armed on. Without this, custom-loop resolvers would hang because
lib/EV/cares.pm view on Meta::CPAN
=head2 active_queries
my $n = $r->active_queries;
Returns the number of outstanding queries. Remains callable after
C<destroy>; returns C<0> in that case (during interpreter global
destruction the count may reflect whatever was pending, since
C<ares_destroy> is intentionally skipped on the global-destruction
path).
=head2 is_busy
if ($r->is_busy) { ... }
Convenience wrapper for C<< $r->active_queries > 0 >>.
=head2 wait_idle
my $drained = $r->wait_idle; # default 30s timeout
my $drained = $r->wait_idle($seconds);
Pumps the EV loop until every pending query on this resolver has fired
its callback, or until the timeout elapses. Returns true if the
t/17_wait_idle.t view on Meta::CPAN
use strict;
use warnings;
use Test::More;
use EV;
use EV::cares qw(:all);
# --- is_busy: idle resolver ---
{
my $r = EV::cares->new;
ok(!$r->is_busy, 'is_busy false on fresh resolver');
is($r->active_queries, 0, 'active_queries matches');
}
# --- is_busy: with pending query ---
{
my $r = EV::cares->new(timeout => 5, tries => 1);
$r->resolve('unlikely-host-xyz.invalid', sub { });
ok($r->is_busy, 'is_busy true while query pending');
$r->cancel;
# drain the cancellation callback
EV::run(EV::RUN_NOWAIT) for 1..3;
ok(!$r->is_busy, 'is_busy false after cancel drains');
}
# --- wait_idle: already idle returns immediately ---
{
my $r = EV::cares->new;
my $t0 = EV::time;
my $drained = $r->wait_idle(5);
ok($drained, 'wait_idle on idle resolver returns true');
cmp_ok(EV::time - $t0, '<', 0.1, 'wait_idle on idle is immediate');
}
# --- wait_idle: drains pending queries (file lookup, fast) ---
{
my $r = EV::cares->new(lookups => 'f');
my @completed;
for (1..3) {
$r->resolve('localhost', sub { push @completed, $_[0] });
}
ok($r->is_busy || @completed == 3,
'queries queued (or completed synchronously)');
my $drained = $r->wait_idle(5);
ok($drained, 'wait_idle returns true when channel drains');
is(scalar @completed, 3, 'all 3 callbacks fired');
ok(!$r->is_busy, 'resolver is idle after wait_idle');
}
# --- wait_idle: timeout when a query is genuinely in-flight ---
# 192.0.2.1 is RFC 5737 TEST-NET-1 â a guaranteed black-hole address that
# routes nowhere on the public Internet. c-ares will sit on it until its
# own timeout fires. If the platform refuses outbound traffic to TEST-NET
# instantly (some hardened firewalls), the query may complete fast â skip
# the timing assertions in that case.
{
my $r = EV::cares->new(timeout => 10, tries => 1);
$r->set_servers('192.0.2.1');
my $done;
$r->resolve('unlikely-name.invalid', sub { $done = 1 });
my $t0 = EV::time;
my $drained = $r->wait_idle(1);
my $elapsed = EV::time - $t0;
SKIP: {
# If the query drained within the 1s window, the OS rejected the
# TEST-NET packet immediately (no route / hardened firewall) rather
# than black-holing it, so the timeout path can't be exercised.
# This must be detected AFTER wait_idle: a synchronous is_busy check
# before the loop runs still shows the query as in-flight.
skip 'TEST-NET query did not block on this platform', 2 if $drained;
ok(!$drained, 'wait_idle returns false when timeout elapses');
cmp_ok($elapsed, '>=', 0.9, "elapsed >=~1s (actual: ${elapsed}s)");
}
# clean up â query may still be in flight
$r->cancel if $r->is_busy;
$r->wait_idle(2);
}
# --- wait_idle: croaks on destroyed resolver ---
{
my $r = EV::cares->new;
$r->destroy;
eval { $r->wait_idle(1) };
like($@, qr/destroyed/, 'wait_idle croaks on destroyed resolver');
}
t/17_wait_idle.t view on Meta::CPAN
{
my $custom = EV::Loop->new;
my $r = EV::cares->new(loop => $custom, lookups => 'f');
is(ref $r->loop, 'EV::Loop', 'loop() returns the custom EV::Loop');
my $r2 = EV::cares->new(lookups => 'f');
ok(!defined $r2->loop, 'loop() is undef on default-loop resolver');
my $fired;
$r->resolve('localhost', sub { $fired = 1 });
ok($r->is_busy || $fired, 'query pending or fired synchronously');
my $drained = $r->wait_idle(3);
ok($drained, 'wait_idle drains on a custom EV::Loop');
ok($fired, 'callback ran during wait_idle on custom loop');
}
done_testing;
( run in 0.432 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )