summaryrefslogtreecommitdiff
path: root/lib/IO/Async
diff options
context:
space:
mode:
Diffstat (limited to 'lib/IO/Async')
-rw-r--r--lib/IO/Async/Channel.pm2
-rw-r--r--lib/IO/Async/Debug.pm2
-rw-r--r--lib/IO/Async/File.pm2
-rw-r--r--lib/IO/Async/FileStream.pm2
-rw-r--r--lib/IO/Async/Function.pm2
-rw-r--r--lib/IO/Async/Future.pm2
-rw-r--r--lib/IO/Async/Handle.pm4
-rw-r--r--lib/IO/Async/Internals/ChildManager.pm2
-rw-r--r--lib/IO/Async/Internals/Connector.pm40
-rw-r--r--lib/IO/Async/Listener.pm4
-rw-r--r--lib/IO/Async/Loop.pm40
-rw-r--r--lib/IO/Async/Loop/Poll.pm2
-rw-r--r--lib/IO/Async/Loop/Select.pm2
-rw-r--r--lib/IO/Async/LoopTests.pm162
-rw-r--r--lib/IO/Async/Notifier.pm2
-rw-r--r--lib/IO/Async/OS.pm39
-rw-r--r--lib/IO/Async/OS/MSWin32.pm2
-rw-r--r--lib/IO/Async/OS/cygwin.pm2
-rw-r--r--lib/IO/Async/OS/linux.pm2
-rw-r--r--lib/IO/Async/PID.pm2
-rw-r--r--lib/IO/Async/Process.pm2
-rw-r--r--lib/IO/Async/Protocol.pm2
-rw-r--r--lib/IO/Async/Protocol/LineStream.pm2
-rw-r--r--lib/IO/Async/Protocol/Stream.pm2
-rw-r--r--lib/IO/Async/Resolver.pm2
-rw-r--r--lib/IO/Async/Routine.pm2
-rw-r--r--lib/IO/Async/Signal.pm2
-rw-r--r--lib/IO/Async/Socket.pm2
-rw-r--r--lib/IO/Async/Stream.pm2
-rw-r--r--lib/IO/Async/Test.pm2
-rw-r--r--lib/IO/Async/Timer.pm2
-rw-r--r--lib/IO/Async/Timer/Absolute.pm2
-rw-r--r--lib/IO/Async/Timer/Countdown.pm2
-rw-r--r--lib/IO/Async/Timer/Periodic.pm2
34 files changed, 209 insertions, 136 deletions
diff --git a/lib/IO/Async/Channel.pm b/lib/IO/Async/Channel.pm
index baa1dc6..512fdc0 100644
--- a/lib/IO/Async/Channel.pm
+++ b/lib/IO/Async/Channel.pm
@@ -9,7 +9,7 @@ use strict;
use warnings;
use base qw( IO::Async::Notifier );
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use Carp;
diff --git a/lib/IO/Async/Debug.pm b/lib/IO/Async/Debug.pm
index 47fc9f3..3548b2d 100644
--- a/lib/IO/Async/Debug.pm
+++ b/lib/IO/Async/Debug.pm
@@ -8,7 +8,7 @@ package IO::Async::Debug;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
our $DEBUG = $ENV{IO_ASYNC_DEBUG} || 0;
our $DEBUG_FD = $ENV{IO_ASYNC_DEBUG_FD};
diff --git a/lib/IO/Async/File.pm b/lib/IO/Async/File.pm
index a4dddbf..d6ef72d 100644
--- a/lib/IO/Async/File.pm
+++ b/lib/IO/Async/File.pm
@@ -8,7 +8,7 @@ package IO::Async::File;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use base qw( IO::Async::Timer::Periodic );
diff --git a/lib/IO/Async/FileStream.pm b/lib/IO/Async/FileStream.pm
index b7f3671..bcc000b 100644
--- a/lib/IO/Async/FileStream.pm
+++ b/lib/IO/Async/FileStream.pm
@@ -8,7 +8,7 @@ package IO::Async::FileStream;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use base qw( IO::Async::Stream );
diff --git a/lib/IO/Async/Function.pm b/lib/IO/Async/Function.pm
index 8f17cc7..38695a6 100644
--- a/lib/IO/Async/Function.pm
+++ b/lib/IO/Async/Function.pm
@@ -8,7 +8,7 @@ package IO::Async::Function;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use base qw( IO::Async::Notifier );
use IO::Async::Timer::Countdown;
diff --git a/lib/IO/Async/Future.pm b/lib/IO/Async/Future.pm
index 84b2ec0..7e8de42 100644
--- a/lib/IO/Async/Future.pm
+++ b/lib/IO/Async/Future.pm
@@ -8,7 +8,7 @@ package IO::Async::Future;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use base qw( Future );
Future->VERSION( '0.05' ); # to respect subclassing
diff --git a/lib/IO/Async/Handle.pm b/lib/IO/Async/Handle.pm
index 07bad9d..864a177 100644
--- a/lib/IO/Async/Handle.pm
+++ b/lib/IO/Async/Handle.pm
@@ -9,7 +9,7 @@ use strict;
use warnings;
use base qw( IO::Async::Notifier );
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use Carp;
@@ -497,7 +497,7 @@ sub new_close_future
my $self = shift or return;
my $future = shift;
- @{ $self->{close_futures} } = grep { $_ != $future } @{ $self->{close_futures} };
+ @{ $self->{close_futures} } = grep { $_ and $_ != $future } @{ $self->{close_futures} };
})
);
diff --git a/lib/IO/Async/Internals/ChildManager.pm b/lib/IO/Async/Internals/ChildManager.pm
index c9e6399..bca19b7 100644
--- a/lib/IO/Async/Internals/ChildManager.pm
+++ b/lib/IO/Async/Internals/ChildManager.pm
@@ -8,7 +8,7 @@ package IO::Async::Internals::ChildManager;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
# Not a notifier
diff --git a/lib/IO/Async/Internals/Connector.pm b/lib/IO/Async/Internals/Connector.pm
index 83b774b..79111b8 100644
--- a/lib/IO/Async/Internals/Connector.pm
+++ b/lib/IO/Async/Internals/Connector.pm
@@ -9,9 +9,9 @@ package # hide from CPAN
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
-use Scalar::Util qw( weaken );
+use Scalar::Util qw( weaken blessed );
use POSIX qw( EINPROGRESS );
use Socket qw( SOL_SOCKET SO_ERROR );
@@ -23,7 +23,10 @@ use IO::Async::OS;
use Carp;
-use constant CONNECT_EWOULDLBOCK => IO::Async::OS->HAVE_CONNECT_EWOULDBLOCK;
+use constant {
+ CONNECT_EWOULDLBOCK => IO::Async::OS->HAVE_CONNECT_EWOULDBLOCK,
+ HAVE_SOCKADDR_IN6 => IO::Async::OS->HAVE_SOCKADDR_IN6,
+};
# Internal constructor
sub new
@@ -186,7 +189,36 @@ sub connect
);
}
elsif( exists $params{addrs} or exists $params{addr} ) {
- $peeraddrfuture = $loop->new_future->done( exists $params{addrs} ? @{ $params{addrs} } : ( $params{addr} ) );
+ my @addrs = exists $params{addrs} ? @{ $params{addrs} } : ( $params{addr} );
+
+ # Warn about some common mistakes
+ foreach my $peer ( @addrs ) {
+ my ( $p_family, undef, undef, $p_addr ) = IO::Async::OS->extract_addrinfo( $peer );
+
+ local our @CARP_NOT = qw( IO::Async::Loop IO::Async::Handle );
+
+ if( $p_family == Socket::AF_INET ) {
+ carp "Connecting to 0.0.0.0 is non-portable and ill-advised"
+ if ( Socket::unpack_sockaddr_in $p_addr )[1] eq Socket::INADDR_ANY;
+ }
+ elsif( HAVE_SOCKADDR_IN6 and $p_family == Socket::AF_INET6 ) {
+ carp "Connecting to :: is non-portable and ill-advised"
+ if ( Socket::unpack_sockaddr_in6 $p_addr )[1] eq Socket::IN6ADDR_ANY;
+ }
+ }
+
+ $peeraddrfuture = $loop->new_future->done( @addrs );
+ }
+ elsif( exists $params{peer} ) {
+ my $peer = delete $params{peer};
+ croak "Expected 'peer' to be an IO::Socket or subclass"
+ unless blessed $peer and $peer->isa( "IO::Socket" );
+
+ my $p_family = $peer->sockdomain;
+
+ $peeraddrfuture = $loop->new_future->done(
+ [ $p_family, $peer->socktype, $peer->protocol, IO::Async::OS->make_addr_for_peer( $p_family, $peer->sockname ) ]
+ );
}
else {
croak "Expected 'host' and 'service' or 'addrs' or 'addr' arguments";
diff --git a/lib/IO/Async/Listener.pm b/lib/IO/Async/Listener.pm
index c7350fa..dc1bf23 100644
--- a/lib/IO/Async/Listener.pm
+++ b/lib/IO/Async/Listener.pm
@@ -9,7 +9,7 @@ use strict;
use warnings;
use base qw( IO::Async::Handle );
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use IO::Async::Handle;
use IO::Async::OS;
@@ -412,7 +412,7 @@ sub socktype
=head2 listen
- $listener->listen( %params )
+ $listener->listen( %params )->get
This method sets up a listening socket and arranges for the acceptor callback
to be invoked each time a new connection is accepted on the socket.
diff --git a/lib/IO/Async/Loop.pm b/lib/IO/Async/Loop.pm
index 0cfac00..f671b0a 100644
--- a/lib/IO/Async/Loop.pm
+++ b/lib/IO/Async/Loop.pm
@@ -1,14 +1,14 @@
# You may distribute under the terms of either the GNU General Public License
# or the Artistic License (the same terms as Perl itself)
#
-# (C) Paul Evans, 2007-2020 -- leonerd@leonerd.org.uk
+# (C) Paul Evans, 2007-2021 -- leonerd@leonerd.org.uk
package IO::Async::Loop;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
# When editing this value don't forget to update the docs below
use constant NEED_API_VERSION => '0.33';
@@ -23,6 +23,9 @@ use constant _CAN_SUBSECOND_ACCURATELY => 0;
# Does the loop implementation support IO_ASYNC_WATCHDOG?
use constant _CAN_WATCHDOG => 0;
+# Does the loop support ->watch_process on PID 0 to observe all exits?
+use constant _CAN_WATCH_ALL_PIDS => 1;
+
# Watchdog configuration constants
use constant WATCHDOG_ENABLE => $ENV{IO_ASYNC_WATCHDOG};
use constant WATCHDOG_INTERVAL => $ENV{IO_ASYNC_WATCHDOG_INTERVAL} || 10;
@@ -873,6 +876,8 @@ sub detach_signal
$loop->later( $code )
+ $f = $loop->later
+
Schedules a code reference to be invoked as soon as the current round of IO
operations is complete.
@@ -886,6 +891,11 @@ This method is implemented using the C<watch_idle> method, with the C<when>
parameter set to C<later>. It will return an ID value that can be passed to
C<unwatch_idle> if required.
+I<Since version 0.78>: If no C<$code> value is passed, a L<Future> will be
+returned instead. This allows for constructs such as:
+
+ await $loop->later;
+
=cut
sub later
@@ -893,7 +903,17 @@ sub later
my $self = shift;
my ( $code ) = @_;
- return $self->watch_idle( when => 'later', code => $code );
+ return $self->watch_idle( when => 'later', code => $code )
+ if $code;
+
+ my $f = $self->new_future;
+ my $id = $self->watch_idle( when => 'later', code => sub {
+ $f->done unless $f->is_ready;
+ } );
+ $f->on_cancel( sub {
+ $self->unwatch_idle( $id );
+ } );
+ return $f;
}
=head2 spawn_child
@@ -1519,6 +1539,13 @@ This example shows another way to connect to a UNIX socket at F<echo.sock>.
...
);
+=item peer => IO
+
+Shortcut for constructing an address to connect to the given IO handle, which
+must be a L<IO::Socket> or subclass, and is presumed to be a local listening
+socket (perhaps on C<PF_UNIX> or C<PF_INET>). This is convenient for
+connecting to a local filehandle, for example during a unit test or similar.
+
=item local_addrs => ARRAY
=item local_addr => HASH or ARRAY
@@ -2939,6 +2966,13 @@ look for exited child processes.
If both a PID-specific and an all-process watch are installed, there is no
ordering guarantee as to which will be called first.
+B<NOTE> that not all loop classes may be able to support the all-child watch.
+The basic Select and Poll-based classes provided by this distribution do, and
+those built on top of similar OS-specific mechanisms such as Linux's Epoll
+probably will, but typically those built on top of other event systems such
+as F<glib> or F<libuv> may not be able, as the underlying event system may not
+provide the necessary hooks to support it.
+
=cut
sub watch_process
diff --git a/lib/IO/Async/Loop/Poll.pm b/lib/IO/Async/Loop/Poll.pm
index 6229cf2..8c1dd97 100644
--- a/lib/IO/Async/Loop/Poll.pm
+++ b/lib/IO/Async/Loop/Poll.pm
@@ -8,7 +8,7 @@ package IO::Async::Loop::Poll;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use constant API_VERSION => '0.49';
use base qw( IO::Async::Loop );
diff --git a/lib/IO/Async/Loop/Select.pm b/lib/IO/Async/Loop/Select.pm
index f405109..9cb91f5 100644
--- a/lib/IO/Async/Loop/Select.pm
+++ b/lib/IO/Async/Loop/Select.pm
@@ -8,7 +8,7 @@ package IO::Async::Loop::Select;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use constant API_VERSION => '0.49';
use base qw( IO::Async::Loop );
diff --git a/lib/IO/Async/LoopTests.pm b/lib/IO/Async/LoopTests.pm
index cbe9b0b..91d6e80 100644
--- a/lib/IO/Async/LoopTests.pm
+++ b/lib/IO/Async/LoopTests.pm
@@ -1,7 +1,7 @@
# You may distribute under the terms of either the GNU General Public License
# or the Artistic License (the same terms as Perl itself)
#
-# (C) Paul Evans, 2009-2020 -- leonerd@leonerd.org.uk
+# (C) Paul Evans, 2009-2021 -- leonerd@leonerd.org.uk
package IO::Async::LoopTests;
@@ -28,7 +28,7 @@ use POSIX qw( SIGTERM );
use Socket qw( sockaddr_family AF_UNIX );
use Time::HiRes qw( time );
-our $VERSION = '0.77';
+our $VERSION = '0.78';
# Abstract Units of Time
use constant AUT => $ENV{TEST_QUICK_TIMERS} ? 0.1 : 1;
@@ -428,58 +428,71 @@ Tests the Loop's ability to handle timer events
sub run_tests_timer
{
- my $done = 0;
# New watch/unwatch API
cmp_ok( abs( $loop->time - time ), "<", 0.1, '$loop->time gives the current time' );
- $loop->watch_time( after => 2 * AUT, code => sub { $done = 1; } );
+ # ->watch_time after
+ {
+ my $done;
+ $loop->watch_time( after => 2 * AUT, code => sub { $done = 1; } );
- is_oneref( $loop, '$loop has refcount 1 after watch_time' );
+ is_oneref( $loop, '$loop has refcount 1 after watch_time' );
- time_between {
- my $now = time;
- $loop->loop_once( 5 * AUT );
+ time_between {
+ my $now = time;
+ $loop->loop_once( 5 * AUT );
- # poll might have returned just a little early, such that the TimerQueue
- # doesn't think anything is ready yet. We need to handle that case.
- while( !$done ) {
- die "It should have been ready by now" if( time - $now > 5 * AUT );
- $loop->loop_once( 0.1 * AUT );
- }
- } 1.5, 2.5, 'loop_once(5) while waiting for watch_time after';
-
- $loop->watch_time( at => time + 2 * AUT, code => sub { $done = 2; } );
+ # poll might have returned just a little early, such that the TimerQueue
+ # doesn't think anything is ready yet. We need to handle that case.
+ while( !$done ) {
+ die "It should have been ready by now" if( time - $now > 5 * AUT );
+ $loop->loop_once( 0.1 * AUT );
+ }
+ } 1.5, 2.5, 'loop_once(5) while waiting for watch_time after';
+ }
- time_between {
- my $now = time;
- $loop->loop_once( 5 * AUT );
+ # ->watch_time at
+ {
+ my $done;
+ $loop->watch_time( at => time + 2 * AUT, code => sub { $done = 1; } );
- # poll might have returned just a little early, such that the TimerQueue
- # doesn't think anything is ready yet. We need to handle that case.
- while( !$done ) {
- die "It should have been ready by now" if( time - $now > 5 * AUT );
- $loop->loop_once( 0.1 * AUT );
- }
- } 1.5, 2.5, 'loop_once(5) while waiting for watch_time at';
+ time_between {
+ my $now = time;
+ $loop->loop_once( 5 * AUT );
- my $cancelled_fired = 0;
- my $id = $loop->watch_time( after => 1 * AUT, code => sub { $cancelled_fired = 1 } );
- $loop->unwatch_time( $id );
- undef $id;
+ # poll might have returned just a little early, such that the TimerQueue
+ # doesn't think anything is ready yet. We need to handle that case.
+ while( !$done ) {
+ die "It should have been ready by now" if( time - $now > 5 * AUT );
+ $loop->loop_once( 0.1 * AUT );
+ }
+ } 1.5, 2.5, 'loop_once(5) while waiting for watch_time at';
+ }
- $loop->loop_once( 2 * AUT );
+ # cancelled timer
+ {
+ my $cancelled_fired = 0;
+ my $id = $loop->watch_time( after => 1 * AUT, code => sub { $cancelled_fired = 1 } );
+ $loop->unwatch_time( $id );
+ undef $id;
- ok( !$cancelled_fired, 'unwatched watch_time does not fire' );
+ $loop->loop_once( 2 * AUT );
- $loop->watch_time( after => -1, code => sub { $done = 1 } );
+ ok( !$cancelled_fired, 'unwatched watch_time does not fire' );
+ }
- $done = 0;
+ # ->watch_after negative time
+ {
+ my $done;
+ $loop->watch_time( after => -1, code => sub { $done = 1 } );
- time_between {
- $loop->loop_once while !$done;
- } 0, 0.1, 'loop_once while waiting for negative interval timer';
+ time_between {
+ $loop->loop_once while !$done;
+ } 0, 0.1, 'loop_once while waiting for negative interval timer';
+ }
+ # self-cancellation
{
my $done;
@@ -497,25 +510,6 @@ sub run_tests_timer
is( $done, 1, 'Other timers still fire after self-cancelling one' );
}
- # Legacy enqueue/requeue/cancel API
- $done = 0;
-
- $loop->enqueue_timer( delay => 2 * AUT, code => sub { $done = 1; } );
-
- is_oneref( $loop, '$loop has refcount 1 after enqueue_timer' );
-
- time_between {
- my $now = time;
- $loop->loop_once( 5 * AUT );
-
- # poll might have returned just a little early, such that the TimerQueue
- # doesn't think anything is ready yet. We need to handle that case.
- while( !$done ) {
- die "It should have been ready by now" if( time - $now > 5 * AUT );
- $loop->loop_once( 0.1 * AUT );
- }
- } 1.5, 2.5, 'loop_once(5) while waiting for timer';
-
SKIP: {
skip "Unable to handle sub-second timers accurately", 3 unless $loop->_CAN_SUBSECOND_ACCURATELY;
@@ -525,7 +519,7 @@ sub run_tests_timer
my $count = 0;
my $start = time;
- $loop->enqueue_timer( delay => $delay, code => sub { $done++ } );
+ $loop->watch_timer( delay => $delay, code => sub { $done++ } );
while( !$done ) {
$loop->loop_once( 1 );
@@ -536,38 +530,6 @@ sub run_tests_timer
is( $count, 1, "One ->loop_once(1) sufficient for a single $delay second timer" );
}
}
-
- $cancelled_fired = 0;
- $id = $loop->enqueue_timer( delay => 1 * AUT, code => sub { $cancelled_fired = 1 } );
- $loop->cancel_timer( $id );
- undef $id;
-
- $loop->loop_once( 2 * AUT );
-
- ok( !$cancelled_fired, 'cancelled timer does not fire' );
-
- $id = $loop->enqueue_timer( delay => 1 * AUT, code => sub { $done = 2; } );
- $id = $loop->requeue_timer( $id, delay => 2 * AUT );
-
- $done = 0;
-
- time_between {
- $loop->loop_once( 1 * AUT );
-
- is( $done, 0, '$done still 0 so far' );
-
- my $now = time;
- $loop->loop_once( 5 * AUT );
-
- # poll might have returned just a little early, such that the TimerQueue
- # doesn't think anything is ready yet. We need to handle that case.
- while( !$done ) {
- die "It should have been ready by now" if( time - $now > 5 * AUT );
- $loop->loop_once( 0.1 * AUT );
- }
- } 1.5, 2.5, 'requeued timer of delay 2';
-
- is( $done, 2, '$done is 2 after requeued timer' );
}
=head2 signal
@@ -696,6 +658,10 @@ sub run_tests_idle
$loop->unwatch_idle( $id );
+ # Some loop types (e.g. UV) need to clear a pending queue first and thus the
+ # first loop_once will take zero time
+ $loop->loop_once( 0 );
+
time_between { $loop->loop_once( 1 * AUT ) } 0.5, 1.5, 'loop_once(1) with unwatched deferral';
is( $called, 2, 'unwatched deferral not called' );
@@ -777,16 +743,20 @@ sub run_tests_process
is( ($exitcode & 0x7f), SIGTERM, 'WTERMSIG($exitcode) after SIGTERM' );
}
- my %kids;
+ SKIP: {
+ my %kids;
- $loop->watch_process( 0 => sub { my ( $kid ) = @_; delete $kids{$kid} } );
+ $loop->_CAN_WATCH_ALL_PIDS or skip "Loop cannot watch_process for all PIDs", 2;
- %kids = map { run_in_child { exit 0 } => 1 } 1 .. 3;
+ $loop->watch_process( 0 => sub { my ( $kid ) = @_; delete $kids{$kid} } );
- is( scalar keys %kids, 3, 'Waiting for 3 child processes' );
+ %kids = map { run_in_child { exit 0 } => 1 } 1 .. 3;
- wait_for { !keys %kids };
- ok( !keys %kids, 'All child processes reclaimed' );
+ is( scalar keys %kids, 3, 'Waiting for 3 child processes' );
+
+ wait_for { !keys %kids };
+ ok( !keys %kids, 'All child processes reclaimed' );
+ }
# Legacy API name
$kid = run_in_child { exit 2 };
diff --git a/lib/IO/Async/Notifier.pm b/lib/IO/Async/Notifier.pm
index 6b251bf..3335205 100644
--- a/lib/IO/Async/Notifier.pm
+++ b/lib/IO/Async/Notifier.pm
@@ -8,7 +8,7 @@ package IO::Async::Notifier;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use Carp;
use Scalar::Util qw( weaken );
diff --git a/lib/IO/Async/OS.pm b/lib/IO/Async/OS.pm
index e9f46e8..d5ede3d 100644
--- a/lib/IO/Async/OS.pm
+++ b/lib/IO/Async/OS.pm
@@ -8,7 +8,7 @@ package IO::Async::OS;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
our @ISA = qw( IO::Async::OS::_Base );
@@ -527,6 +527,43 @@ sub _extract_addrinfo_unix
=cut
+=head2 make_addr_for_peer
+
+ $connectaddr = IO::Async::OS->make_addr_for_peer( $family, $listenaddr )
+
+Given the C<sockdomain> and C<sockname> of a listening socket. creates an
+address suitable to C<connect()> to it.
+
+This method will handle specially any C<AF_INET> address bound to
+C<INADDR_ANY> or any C<AF_INET6> address bound to C<IN6ADDR_ANY>, as some OSes
+do not allow C<connect(2)>ing to those and would instead insist on receiving
+C<INADDR_LOOPBACK> or C<IN6ADDR_LOOPBACK> respectively.
+
+This method is used by the C<< ->connect( peer => $sock ) >> parameter of
+handle and loop connect methods.
+
+=cut
+
+sub make_addr_for_peer
+{
+ shift;
+ my ( $p_family, $p_addr ) = @_;
+
+ if( $p_family == Socket::AF_INET ) {
+ my @params = Socket::unpack_sockaddr_in $p_addr;
+ $params[1] = Socket::INADDR_LOOPBACK if $params[1] eq Socket::INADDR_ANY;
+ return Socket::pack_sockaddr_in @params;
+ }
+ if( HAVE_SOCKADDR_IN6 and $p_family == Socket::AF_INET6 ) {
+ my @params = Socket::unpack_sockaddr_in6 $p_addr;
+ $params[1] = Socket::IN6ADDR_LOOPBACK if $params[1] eq Socket::IN6ADDR_ANY;
+ return Socket::pack_sockaddr_in6 @params;
+ }
+
+ # Most other cases should be fine
+ return $p_addr;
+}
+
=head1 LOOP IMPLEMENTATION METHODS
The following methods are provided on C<IO::Async::OS> because they are likely
diff --git a/lib/IO/Async/OS/MSWin32.pm b/lib/IO/Async/OS/MSWin32.pm
index ecda8ab..9edef9f 100644
--- a/lib/IO/Async/OS/MSWin32.pm
+++ b/lib/IO/Async/OS/MSWin32.pm
@@ -8,7 +8,7 @@ package IO::Async::OS::MSWin32;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
our @ISA = qw( IO::Async::OS::_Base );
diff --git a/lib/IO/Async/OS/cygwin.pm b/lib/IO/Async/OS/cygwin.pm
index 945fefd..4258fdb 100644
--- a/lib/IO/Async/OS/cygwin.pm
+++ b/lib/IO/Async/OS/cygwin.pm
@@ -8,7 +8,7 @@ package IO::Async::OS::cygwin;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
our @ISA = qw( IO::Async::OS::_Base );
diff --git a/lib/IO/Async/OS/linux.pm b/lib/IO/Async/OS/linux.pm
index 28e1f1a..79482ea 100644
--- a/lib/IO/Async/OS/linux.pm
+++ b/lib/IO/Async/OS/linux.pm
@@ -8,7 +8,7 @@ package IO::Async::OS::linux;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
our @ISA = qw( IO::Async::OS::_Base );
diff --git a/lib/IO/Async/PID.pm b/lib/IO/Async/PID.pm
index 479925c..4f9876c 100644
--- a/lib/IO/Async/PID.pm
+++ b/lib/IO/Async/PID.pm
@@ -9,7 +9,7 @@ use strict;
use warnings;
use base qw( IO::Async::Notifier );
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use Carp;
diff --git a/lib/IO/Async/Process.pm b/lib/IO/Async/Process.pm
index fd92be5..c8f0493 100644
--- a/lib/IO/Async/Process.pm
+++ b/lib/IO/Async/Process.pm
@@ -9,7 +9,7 @@ use strict;
use warnings;
use base qw( IO::Async::Notifier );
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use Carp;
diff --git a/lib/IO/Async/Protocol.pm b/lib/IO/Async/Protocol.pm
index b8342ab..772fe1c 100644
--- a/lib/IO/Async/Protocol.pm
+++ b/lib/IO/Async/Protocol.pm
@@ -8,7 +8,7 @@ package IO::Async::Protocol;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use base qw( IO::Async::Notifier );
diff --git a/lib/IO/Async/Protocol/LineStream.pm b/lib/IO/Async/Protocol/LineStream.pm
index 2335d23..4ea49a7 100644
--- a/lib/IO/Async/Protocol/LineStream.pm
+++ b/lib/IO/Async/Protocol/LineStream.pm
@@ -8,7 +8,7 @@ package IO::Async::Protocol::LineStream;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use base qw( IO::Async::Protocol::Stream );
diff --git a/lib/IO/Async/Protocol/Stream.pm b/lib/IO/Async/Protocol/Stream.pm
index db98494..f6f5206 100644
--- a/lib/IO/Async/Protocol/Stream.pm
+++ b/lib/IO/Async/Protocol/Stream.pm
@@ -8,7 +8,7 @@ package IO::Async::Protocol::Stream;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use base qw( IO::Async::Protocol );
diff --git a/lib/IO/Async/Resolver.pm b/lib/IO/Async/Resolver.pm
index 22f6d93..c6de4d3 100644
--- a/lib/IO/Async/Resolver.pm
+++ b/lib/IO/Async/Resolver.pm
@@ -9,7 +9,7 @@ use strict;
use warnings;
use base qw( IO::Async::Function );
-our $VERSION = '0.77';
+our $VERSION = '0.78';
# Socket 2.006 fails to getaddrinfo() AI_NUMERICHOST properly on MSWin32
use Socket 2.007 qw(
diff --git a/lib/IO/Async/Routine.pm b/lib/IO/Async/Routine.pm
index 4b1f69b..18671e1 100644
--- a/lib/IO/Async/Routine.pm
+++ b/lib/IO/Async/Routine.pm
@@ -8,7 +8,7 @@ package IO::Async::Routine;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use base qw( IO::Async::Notifier );
diff --git a/lib/IO/Async/Signal.pm b/lib/IO/Async/Signal.pm
index a5749e8..4eac03d 100644
--- a/lib/IO/Async/Signal.pm
+++ b/lib/IO/Async/Signal.pm
@@ -9,7 +9,7 @@ use strict;
use warnings;
use base qw( IO::Async::Notifier );
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use Carp;
diff --git a/lib/IO/Async/Socket.pm b/lib/IO/Async/Socket.pm
index 9b194ca..149d608 100644
--- a/lib/IO/Async/Socket.pm
+++ b/lib/IO/Async/Socket.pm
@@ -8,7 +8,7 @@ package IO::Async::Socket;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use base qw( IO::Async::Handle );
diff --git a/lib/IO/Async/Stream.pm b/lib/IO/Async/Stream.pm
index 42833f0..0d928c1 100644
--- a/lib/IO/Async/Stream.pm
+++ b/lib/IO/Async/Stream.pm
@@ -8,7 +8,7 @@ package IO::Async::Stream;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use base qw( IO::Async::Handle );
diff --git a/lib/IO/Async/Test.pm b/lib/IO/Async/Test.pm
index eeac822..78a0512 100644
--- a/lib/IO/Async/Test.pm
+++ b/lib/IO/Async/Test.pm
@@ -8,7 +8,7 @@ package IO::Async::Test;
use strict;
use warnings;
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use Exporter 'import';
our @EXPORT = qw(
diff --git a/lib/IO/Async/Timer.pm b/lib/IO/Async/Timer.pm
index 6271957..f00fd8a 100644
--- a/lib/IO/Async/Timer.pm
+++ b/lib/IO/Async/Timer.pm
@@ -9,7 +9,7 @@ use strict;
use warnings;
use base qw( IO::Async::Notifier );
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use Carp;
diff --git a/lib/IO/Async/Timer/Absolute.pm b/lib/IO/Async/Timer/Absolute.pm
index 5fde51a..6869430 100644
--- a/lib/IO/Async/Timer/Absolute.pm
+++ b/lib/IO/Async/Timer/Absolute.pm
@@ -9,7 +9,7 @@ use strict;
use warnings;
use base qw( IO::Async::Timer );
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use Carp;
diff --git a/lib/IO/Async/Timer/Countdown.pm b/lib/IO/Async/Timer/Countdown.pm
index 9ddbebf..138ea3e 100644
--- a/lib/IO/Async/Timer/Countdown.pm
+++ b/lib/IO/Async/Timer/Countdown.pm
@@ -9,7 +9,7 @@ use strict;
use warnings;
use base qw( IO::Async::Timer );
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use Carp;
diff --git a/lib/IO/Async/Timer/Periodic.pm b/lib/IO/Async/Timer/Periodic.pm
index b9741c8..07e1b99 100644
--- a/lib/IO/Async/Timer/Periodic.pm
+++ b/lib/IO/Async/Timer/Periodic.pm
@@ -9,7 +9,7 @@ use strict;
use warnings;
use base qw( IO::Async::Timer );
-our $VERSION = '0.77';
+our $VERSION = '0.78';
use Carp;