File Coverage

blib/lib/Future/IO/Resolver/Using/LibAsyncNS.pm
Criterion Covered Total %
statement 30 156 19.2
branch 0 20 0.0
condition 0 6 0.0
subroutine 10 25 40.0
pod 0 4 0.0
total 40 211 18.9


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2026 -- leonerd@leonerd.org.uk
5              
6             package Future::IO::Resolver::Using::LibAsyncNS 0.04;
7              
8 6     6   76 use v5.20;
  6         18  
9 6     6   22 use warnings;
  6         8  
  6         258  
10              
11 6     6   22 use feature qw( postderef signatures );
  6         7  
  6         591  
12 6     6   24 no warnings qw( experimental::postderef experimental::signatures );
  6         17  
  6         270  
13              
14 6     6   28 use Future::IO 0.19 qw( POLLIN );
  6         72  
  6         265  
15 6     6   23 use Future::Utils qw( repeat );
  6         9  
  6         370  
16              
17 6     6   26 use constant HAVE_LIBASYNCNS => eval { require Net::LibAsyncNS; };
  6         13  
  6         21  
  6         974  
18              
19 6     6   27 use constant RESOLVER_PRIORITY => 25;
  6         45  
  6         265  
20              
21 6     6   23 use Future::IO::Resolver;
  6         7  
  6         233  
22             HAVE_LIBASYNCNS and
23             Future::IO::Resolver->ADD_BACKEND( __PACKAGE__ );
24              
25 6     6   19 use constant NS_CLASS_IN => 1;
  6         12  
  6         7559  
26              
27             my $asyncns;
28             my $runf;
29             my $pending_failuref;
30              
31             =head1 NAME
32              
33             C - implement L using F
34              
35             =head1 DESCRIPTION
36              
37             This module provides a backend implementation for L
38             which uses F (via the L module) to perform its
39             lookups.
40              
41             This should not be used directly, but is instead made available via the main
42             dispatch methods in C itself.
43              
44             =cut
45              
46 0     0     sub _asyncns ()
  0            
47             {
48 0 0         return $asyncns if $asyncns;
49              
50 0           $asyncns = Net::LibAsyncNS->new( 1 );
51              
52 0           my $fh = $asyncns->new_handle_for_fd;
53              
54             ## This would be much neater with async/await
55             $runf = Future::Utils::repeat {
56             Future::IO->poll( $fh, POLLIN )->then( sub ( $ ) {
57 0 0         $asyncns->wait( 0 ) or
58             warn( "Future::IO::Resolver::Using::LibAsyncNS IO failure $!\n" ), return Future->done;
59              
60 0           while( my $q = $asyncns->getnext ) {
61 0           my $f = $q->getuserdata;
62 0           $f->done( $q );
63             }
64 0           Future->done;
65             } )
66 0     0     } while => sub ( $f ) { !$f->failure };
  0            
  0            
  0            
  0            
  0            
67              
68 0     0     $runf->on_fail( sub ( $err, @ ) {
  0            
  0            
69 0           say "Future::IO::Resolver::Using::LibAsyncNS run future failed - $err";
70 0           $pending_failuref = $runf;
71 0           undef $runf;
72 0           } );
73              
74 0           return $asyncns;
75             }
76              
77             sub getaddrinfo ( $, %args )
78 0     0 0   {
  0            
  0            
79 0           my $asyncns = _asyncns();
80              
81 0 0         if( $pending_failuref ) {
82 0           my $f = $pending_failuref;
83 0           undef $pending_failuref;
84 0           return $f;
85             }
86              
87 0           my $host = delete $args{host};
88 0           my $service = delete $args{service};
89              
90 0           my $q = $asyncns->getaddrinfo( $host, $service, \%args );
91              
92 0           my $f = $runf->new;
93 0           $q->setuserdata( $f );
94              
95 0     0     my $queryf = $f->then( sub ( $q ) {
  0            
  0            
96 0           my ( $err, @res ) = $asyncns->getaddrinfo_done( $q );
97              
98 0 0         if( !$err ) {
99 0           Future->done( @res );
100             }
101             else {
102 0           Future->fail( "$err\n", getaddrinfo => );
103             }
104 0     0     })->on_cancel( sub ( $ ) { $asyncns->cancel( $q ); } );
  0            
  0            
  0            
105              
106 0           return Future->wait_any( $queryf, $runf->without_cancel );
107             }
108              
109             sub getnameinfo ( $, %args )
110 0     0 0   {
  0            
  0            
111 0           my $asyncns = _asyncns();
112              
113 0 0         if( $pending_failuref ) {
114 0           my $f = $pending_failuref;
115 0           undef $pending_failuref;
116 0           return $f;
117             }
118              
119 0           my $addr = delete $args{addr};
120 0   0       my $flags = delete $args{flags} // 0;
121              
122 0           my $q = $asyncns->getnameinfo( $addr, $flags, 1, 1 );
123              
124 0           my $f = $runf->new;
125 0           $q->setuserdata( $f );
126              
127 0     0     my $queryf = $f->then( sub ( $q ) {
  0            
  0            
128 0           my ( $err, $host, $service ) = $asyncns->getnameinfo_done( $q );
129              
130 0 0         if( !$err ) {
131 0           Future->done( $host, $service );
132             }
133             else {
134 0           Future->fail( "$err\n", getnameinfo => );
135             }
136 0     0     })->on_cancel( sub ( $ ) { $asyncns->cancel( $q ); } );
  0            
  0            
  0            
137              
138 0           return Future->wait_any( $queryf, $runf->without_cancel );
139             }
140              
141             sub res_query ( $, %args )
142 0     0 0   {
  0            
  0            
143 0           my $asyncns = _asyncns();
144              
145 0 0         if( $pending_failuref ) {
146 0           my $f = $pending_failuref;
147 0           undef $pending_failuref;
148 0           return $f;
149             }
150              
151 0           my $dname = delete $args{dname};
152 0   0       my $class = delete $args{class} // NS_CLASS_IN;
153 0           my $type = delete $args{type};
154              
155 0           my $q = $asyncns->res_query( $dname, $class, $type );
156              
157 0           my $f = $runf->new;
158 0           $q->setuserdata( $f );
159              
160 0     0     my $queryf = $f->then( sub ( $q ) {
  0            
  0            
161 0           my $answer = $asyncns->res_done( $q );
162              
163 0 0         if( defined $answer ) {
164 0           Future->done( $answer );
165             }
166             else {
167 0           Future->fail( "$!", res_query => );
168             }
169 0     0     })->on_cancel( sub ( $ ) { $asyncns->cancel( $q ); } );
  0            
  0            
  0            
170              
171 0           return Future->wait_any( $queryf, $runf->without_cancel );
172             }
173              
174             sub res_search ( $, %args )
175 0     0 0   {
  0            
  0            
176 0           my $asyncns = _asyncns();
177              
178 0 0         if( $pending_failuref ) {
179 0           my $f = $pending_failuref;
180 0           undef $pending_failuref;
181 0           return $f;
182             }
183              
184 0           my $dname = delete $args{dname};
185 0   0       my $class = delete $args{class} // NS_CLASS_IN;
186 0           my $type = delete $args{type};
187              
188 0           my $q = $asyncns->res_search( $dname, $class, $type );
189              
190 0           my $f = $runf->new;
191 0           $q->setuserdata( $f );
192              
193 0     0     my $queryf = $f->then( sub ( $q ) {
  0            
  0            
194 0           my $answer = $asyncns->res_done( $q );
195              
196 0 0         if( defined $answer ) {
197 0           Future->done( $answer );
198             }
199             else {
200 0           Future->fail( "$!", res_search => );
201             }
202 0     0     })->on_cancel( sub ( $ ) { $asyncns->cancel( $q ); } );
  0            
  0            
  0            
203              
204 0           return Future->wait_any( $queryf, $runf->without_cancel );
205             }
206              
207             =head1 AUTHOR
208              
209             Paul Evans
210              
211             =cut
212              
213             0x55AA;