Net-DNS-Async
view release on metacpan or search on metacpan
lib/Net/DNS/Async.pm view on Meta::CPAN
use Time::HiRes;
use Storable qw(freeze thaw);
$VERSION = '1.07';
$_LEVEL = 0;
sub new {
my $class = shift;
my $self = ($#_ == 0) ? { %{ (shift) } } : { @_ };
$self->{Pending} = [ ];
$self->{Queue} = { };
$self->{QueueSize} = 20 unless $self->{QueueSize};
$self->{Timeout} = 4 unless $self->{Timeout};
$self->{Resolver} = new Net::DNS::Resolver();
$self->{Selector} = new IO::Select();
$self->{Retries} = 3 unless $self->{Retries};
return bless $self, $class;
}
sub add {
my ($self, $params, @query) = @_;
my ($callback, @ns);
if (ref($params) eq 'HASH') {
@query = @{ $params->{Query} } if exists $params->{Query};
$callback = $params->{Callback};
@ns = @{ $params->{Nameservers} }
if exists $params->{Nameservers};
}
else {
$callback = $params;
}
unless (ref($callback) eq 'CODE') {
die "add() requires a CODE reference for a callback";
}
unless (@query) {
die "add() requires a DNS query";
}
my $frozen = freeze(\@query);
unless (@ns) {
# It's a regular boring query, we can fold it.
# I wouldn't like to do this in a multi-threaded environment.
for my $data (values %{ $self->{Queue} }) {
if ($frozen eq $data->[NDS_FQUERY]) {
# Allow the use of slot 0 for custom hacks.
unless ($data->[NDS_RESOLVER]) {
push(@{ $data->[NDS_CALLBACKS] }, $callback);
return;
}
}
}
}
# if ($_LEVEL) { add to Pending } else { recv/send }
$self->recv(0); # Perform fast case unconditionally.
# print "Queue size " . scalar(keys %{ $self->{Queue} });
while (scalar(keys %{ $self->{Queue} }) > $self->{QueueSize}) {
# I'm fairly sure this can't busy wait since it must
# either time out an entry or receive an entry when called
# with no arguments.
$self->recv();
}
# [ [ $callback ], $frozen, 0, undef, undef ];
my $data = [ ];
$data->[NDS_CALLBACKS] = [ $callback ];
$data->[NDS_RESOLVER] = new Net::DNS::Resolver(
nameservers => \@ns
) if @ns;
$data->[NDS_FQUERY] = $frozen;
$data->[NDS_RETRIES] = 0;
$self->send($data);
}
sub cleanup {
my ($self, $data) = @_;
my $socket = $data->[NDS_SOCKET];
if ($socket) {
$self->{Selector}->remove($socket);
delete $self->{Queue}->{$socket->fileno};
$socket->close();
}
}
sub send {
my ($self, $data) = @_;
my @query = @{ thaw($data->[NDS_FQUERY]) };
my $resolver = $data->[NDS_RESOLVER] || $self->{Resolver};
my $socket = $resolver->bgsend(@query);
unless ($socket) {
die "No socket returned from bgsend()";
}
unless ($socket->fileno) {
die "Socket returned from bgsend() has no fileno";
}
$data->[NDS_SENDTIME] = time();
$data->[NDS_SOCKET] = $socket;
$self->{Queue}->{$socket->fileno} = $data;
$self->{Selector}->add($socket);
}
sub recv {
my $self = shift;
my $time = shift;
unless (defined $time) {
$time = time();
# Find first timer.
for (values %{ $self->{Queue} }) {
$time = $_->[NDS_SENDTIME] if $_->[NDS_SENDTIME] < $time;
}
# Add timeout, and compute delay until then.
$time = $time + $self->{Timeout} - time();
( run in 0.902 second using v1.01-cache-2.11-cpan-39bf76dae61 )