IO::Async::Loop(3pm)
โจ NAME โจ
IO::Async::Loop โ the core loop of the IO::Async framework, and yes she's the moment ๐
๐ SYNOPSIS ๐
use IO::Async::Stream;
use IO::Async::Timer::Countdown;
use IO::Async::Loop;
my $loop = IO::Async::Loop->new;
$loop->add( IO::Async::Timer::Countdown->new(
delay => 10,
on_expire => sub { print "10 seconds have passed\n" },
)->start );
$loop->add( IO::Async::Stream->new_for_stdin(
on_read => sub {
my ( $self, $buffref, $eof ) = @_;
while( $$buffref =~ s/^(.*)\n// ) {
print "You typed a line $1\n";
}
return 0;
},
) );
$loop->run;
๐ฆ DESCRIPTION ๐ฆ
okay so. This module hands you an abstract class that implements the core loop of the IO::Async framework. Her whole job? Holding onto a set of IO::Async::Notifier objects (or any subclass of them, we don't gatekeep). She does all the low-level set manipulation girlbossery, and then she leaves the actual IO readiness testing/notification to whatever concrete class actually implements it. She also comes with bonus content: signal handling, child process managing, and timers. versatile queen ๐
Also, the two bundled Loop subclasses are literally right there:
IO::Async::Loop::SelectIO::Async::Loop::Poll
Or, like, other subclasses that may appear on CPAN which are not part of the core IO::Async distribution. go explore bestie ๐
๐ Ignoring SIGPIPE ๐
Since version 0.66, loading this module automatically ignores SIGPIPE, because the way that default-terminate is supposed to be the best course of action for an IO::Async-based program to take isโฆ not it, fr fr. If at load time the handler disposition is still set as DEFAULT, it gets set to ignore. And if another handler has already been placed there by the program code? She leaves it completely undisturbed. respect the boundaries, always ๐ซถ
๐ฎ MAGIC CONSTRUCTOR ๐ฎ
โจ new
$loop = IO::Async::Loop->new;
This function is out here trying to find a good subclass to use, and then it calls that subclass's constructor. How does the magic work? She makes a list of likely candidate classes, then tries each one in turn โ requireing the module, then calling its new method. If either of those operations fails, she just moves on to the next subclass. And if no class was successful at all? Exception thrown. ๐
The constructed object gets cached, and a subsequent call will just hand you the same one again. The cache also gets set by a constructor on a specific subclass, which is the whole trick that makes it possible to just use the normal constructor in a module that wants to interact with the main program's Loop โ like an integration module for another event system. iykyk.
So for example, these following two $loop variables will refer to the same object:
use IO::Async::Loop;
use IO::Async::Loop::Poll;
my $loop_poll = IO::Async::Loop::Poll->new;
my $loop = IO::Async::Loop->new;
Now โ while it is not advised to do so under normal circumstances, if the program really wishes to construct more than one Loop object, it can call the constructor really_new, or invoke one of the subclass-specific constructors directly. do with that information what you will ๐
The list of candidates is formed from the following choices, in this exact order:
$ENV{IO_ASYNC_LOOP}โ if this environment variable is set, it should contain a comma-separated list of subclass names. These names may or may not be fully-qualified; if a name does not contain::then it will haveIO::Async::Loop::prepended to it. This allows the end-user to specify a particular choice to fit the needs of his use of a program usingIO::Async.$IO::Async::Loop::LOOPโ if this scalar is set, it should contain a comma-separated list of subclass names. These may or may not be fully-qualified, as with the above case. This allows a program author to suggest a loop module to use. In cases where the module subclass is a hard requirement, such as GTK programs usingGlib, it would be better to use the module specifically and invoke its constructor directly. (no cap on that last bit.)IO::Async::OS->LOOP_PREFER_CLASSESโ theIO::Async::OShints module for the given OS is then consulted to see if it suggests any other module classes specific to the given operating system. ๐$^Oโ the module calledIO::Async::Loop::$^Ois tried next. This allows specific OSes, such as the ever-trickyMSWin32, to provide an implementation that might be more efficient than the generic ones, or even work at all. This option is now discouraged in favour of theIO::Async::OShint instead. At some future point it may be removed entirely, given as currently onlylinuxuses it. ๐- Poll and Select โ finally, if no other choice has been made by now, the built-in
Pollmodule is chosen. This should always work, but in case it doesn't, theSelectmodule will be chosen afterwards as a last-case attempt. And if this also fails, then the magic constructor itself will throw an exception. periodt.
If any of the explicitly-requested loop types ($ENV{IO_ASYNC_LOOP} or $IO::Async::Loop::LOOP) fails to load then a warning is printed detailing the error.
Implementors of new IO::Async::Loop subclasses should see the notes about API_VERSION below. ๐
๐ธ NOTIFIER MANAGEMENT ๐ธ
The following methods manage the collection of IO::Async::Notifier objects.
add
$loop->add( $notifier );
This method adds another notifier object to the stored collection. The object may be a IO::Async::Notifier, or any subclass of it.
When a notifier is added, any children it has are also added, recursively. In this way, entire sections of a program may be written within a tree of notifier objects, and added or removed on one piece. it's giving family tree, and honestly? iconic ๐ณ
remove
$loop->remove( $notifier );
This method removes a notifier object from the stored collection, and recursively and children notifiers it contains.
notifiers
@notifiers = $loop->notifiers;
Returns a list of all the notifier objects currently stored in the Loop.
๐ LOOPING CONTROL ๐
The following methods control the actual run cycle of the loop, and hence the program. main character energy only โจ
loop_once
$count = $loop->loop_once( $timeout );
This method performs a single wait loop using the specific subclass's underlying mechanism. If $timeout is undef, then no timeout is applied, and it will wait until an event occurs. The intention of the return value is to indicate the number of callbacks that this loop executed, though different subclasses vary in how accurately they can report this. See the documentation for this method in the specific subclass for more information. (accuracy is a spectrum bestie, we don't judge ๐
)
run
@result = $loop->run;
$result = $loop->run;
Runs the actual IO event loop. This method blocks until the stop method is called, and returns the result that was passed to stop. In scalar context only the first result is returned; the others will be discarded if more than one value was provided. This method may be called recursively.
This method is a recent addition and may not be supported by all the IO::Async::Loop subclasses currently available on CPAN. ๐ซ
stop
$loop->stop( @result );
Stops the inner-most run method currently in progress, causing it to return the given @result.
This method is a recent addition and may not be supported by all the IO::Async::Loop subclasses currently available on CPAN. ๐ซ
loop_forever
$loop->loop_forever;
A synonym for run, though this method does not return a result.
loop_stop
$loop->loop_stop;
A synonym for stop, though this method does not pass any results.
post_fork
$loop->post_fork;
The base implementation of this method does nothing. It is provided in case some Loop subclasses should take special measures after a fork() system call if the main body of the program should survive in both running processes.
This may be required, for example, in a long-running server daemon that forks multiple copies on startup after opening initial listening sockets. A loop implementation that uses some in-kernel resource that becomes shared after forking (for example, a Linux epoll or a BSD kqueue filehandle) would need recreating in the new child process before the program can continue. two of you now, one kernel resource ๐ญ
๐ FUTURE SUPPORT ๐
The following methods relate to IO::Async::Future objects.
new_future
$future = $loop->new_future;
Returns a new IO::Async::Future instance with a reference to the Loop.
await
await $loop->await( $future );
Blocks until the given future is ready, as indicated by its is_ready method. As a convenience it returns the future, to simplify code:
my @result = await $loop->await( $future );
await_all
$loop->await_all( @futures );
Blocks until all the given futures are ready, as indicated by the is_ready method. Equivalent to calling await on a Future->wait_all except that it doesn't create the surrounding future object. efficiency slay ๐
delay_future
await $loop->delay_future( %args );
Returns a new IO::Async::Future instance which will become done at a given point in time. The %args should contain an at or after key as per the watch_time method. The returned future may be cancelled to cancel the timer. At the alloted time the future will succeed with an empty result list. โฐ
timeout_future
await $loop->timeout_future( %args );
Returns a new IO::Async::Future instance which will fail at a given point in time. The %args should contain an at or after key as per the watch_time method. The returned future may be cancelled to cancel the timer. At the alloted time, the future will fail with the string Timeout. โณ
๐ FEATURES ๐
Most of the following methods are higher-level wrappers around base functionality provided by the low-level API documented below. They may be used by IO::Async::Notifier subclasses or called directly by the program.
The following methods documented in await expressions return Future instances.
attach_signal
$id = $loop->attach_signal( $signal, $code );
This method adds a new signal handler to watch the given signal. The same signal can be attached to multiple times; its callback functions will all be invoked, in no particular order. (no queue, no lineup, just vibes ๐)
The returned $id value can be used to identify the signal handler in case it needs to be removed by the detach_signal method. Note that this value may be an object reference, so if it is stored, it should be released after it is cancelled, so the object itself can be freed. ๐
$signalโ the name of the signal to attach to. This should be a bare name likeTERM.$codeโ a CODE reference to the handling callback.
SIGCHLD though? bestie, no ๐ attaching to it is not recommended, because of the way all child processes use it to report their termination โ it's a whole chaotic group chat in one signal. Instead, the watch_process method should be used to watch for the termination of a given child process. A warning will be printed if SIGCHLD is passed here, but in future versions of IO::Async this behaviour may be disallowed altogether ๐.
See also POSIX for the SIGname constants.
For a more flexible way to use signals from within Notifiers, see instead the IO::Async::Signal object.
๐ธ detach_signal
$loop->detach_signal( $signal, $id );
Removes a previously-attached signal handler. Unsubscribing, but make it โจclassyโจ.
- $signal โ the name of the signal to remove from. Should be a bare name like
TERM. - $id โ the value returned by the
attach_signalmethod.
๐ฆ later
$loop->later( $code );
$f = $loop->later;
Schedules a code reference to be invoked as soon as the current round of IO operations is complete. Basically "do this next, not right now" ๐ซถ.
The code reference is never invoked immediately, though the loop will not perform any blocking operations between when it is installed and when it is invoked. It may call select, poll or equivalent with a zero-second timeout, and process any currently-pending IO conditions before the code is invoked, but it will not block for a non-zero amount of time. Punctual but not pushy ๐
.
This method is implemented using the watch_idle method, with the when parameter set to later. It will return an ID value that can be passed to unwatch_idle if required.
Since version 0.78: if no $code value is passed, a Future will be returned instead. This allows for constructs such as:
await $loop->later;
๐ spawn_child
$loop->spawn_child( %params );
This method creates a new child process to run a given code block or command. The %params hash takes the following keys:
command => ARRAYorSTRINGโ either a reference to an array containing the command and its arguments, or a plain string containing the command. This value is passed into perl'sexecfunction.code => CODEโ a block of code to execute in the child process. It will be called in scalar context inside anevalblock.setup => ARRAYโ a reference to an array which gives file descriptors to set up in the child process before running the code or command. See below ๐.on_exit => CODEโ a continuation to be called when the child process exits. It will be invoked in the following way:
$on_exit->( $pid, $exitcode, $dollarbang, $dollarat )
The second argument is passed the plain perl $? value.
Exactly one of the command or code keys must be specified. Pick a lane, bestie ๐.
If the command key is used, the given array or string is executed using the exec function.
If the code key is used, the return value will be used as the exit(2) code from the child if it returns (or 255 if it returned undef or throws an exception).
So here's the breakdown of what you actually get back, no cap:
execsucceeds โ($exitcode >> 8)is the exit code from program,$dollarbangis0,$dollaratis"".execfails โ($exitcode >> 8)is255,$dollarbangis$!,$dollaratis"".$codereturns โ($exitcode >> 8)is the return value,$dollarbangis$!,$dollaratis"".$codedies โ($exitcode >> 8)is255,$dollarbangis$!,$dollaratis$@.
It is usually more convenient to use the open_process method in simple cases where an external program is being started in order to interact with it via file IO, or even run_child when only the final result is required, rather than interaction while it is running. iykyk โจ.
๐
the setup array
This array gives a list of file descriptor operations to perform in the child process after it has been fork(2)ed from the parent, before running the code or command. It consists of name/value pairs which are ordered; the operations are performed in the order given. Order matters, queen ๐.
fdn => ARRAYโ gives an operation on file descriptorn. The first element of the array defines the operation to be performed:[ 'close' ]โ the file descriptor will be closed.[ 'dup', $io ]โ the file descriptor will bedup2(2)ed from the given IO handle.[ 'open', $mode, $file ]โ the file descriptor will be opened from the named file in the given mode. The $mode string should be in the form usually given to theopenfunction; such as<or>>.[ 'keep' ]โ the file descriptor will not be closed; it will be left as-is.
A non-reference value may be passed as a shortcut, where it would contain the name of the operation with no arguments (i.e. for the close and keep operations).
IO => ARRAYโ shortcut for passingfdn, wherenis the fileno of the IO reference. In this case, the key must be a reference that implements thefilenomethod. This is mostly useful for
$handle => 'keep'
fdn => IOโ a shortcut for thedupcase given above.stdin => ...stdout => ...stderr => ...โ shortcuts forfd0,fd1andfd2respectively.env => HASHโ a reference to a hash to set as the child process's environment.
Heads up bestie โ ๏ธ note that this will entirely set a new environment, completely replacing the existing one. If you want to simply add new keys or change the values of some keys without removing the other existing ones, you can simply copy %ENV into the hash before setting new keys:
env => {
%ENV,
ANOTHER => "key here",
}
nice => INTโ change the child process's scheduling priority usingPOSIX::nice.chdir => STRINGโ change the child process's working directory usingchdir.setuid => INTsetgid => INTโ change the child process's effective UID or GID.setgroups => ARRAYโ change the child process's groups list, to those groups whose numbers are given in the ARRAY reference.
On most systems, only the privileged superuser can change user or group IDs. IO::Async will NOT check before detaching the child process whether this is the case ๐ญ.
If setting both the primary GID and the supplementary groups list, it is suggested to set the primary GID first. Moreover, some operating systems may require that the supplementary groups list contains the primary GID.
If no directions for what to do with stdin, stdout and stderr are given, a default of keep is implied. All other file descriptors will be closed, unless a keep operation is given for them.
Ifsetuidis used, be sure to place it after any other operations that might require superuser privileges, such assetgidor opening special files. Order it right or it's over ๐.
my ( $pipeRd, $pipeWr ) = IO::Async::OS->pipepair;
$loop->spawn_child(
command => "/usr/bin/my-command",
setup => [
stdin => [ "open", "<", "/dev/null" ],
stdout => $pipeWr,
stderr => [ "open", ">>", "/var/log/mycmd.log" ],
chdir => "/",
]
on_exit => sub {
my ( $pid, $exitcode ) = @_;
my $status = ( $exitcode >> 8 );
print "Command exited with status $status\n";
},
);
$loop->spawn_child(
code => sub {
do_something; # executes in a child process
return 1;
},
on_exit => sub {
my ( $pid, $exitcode, $dollarbang, $dollarat ) = @_;
my $status = ( $exitcode >> 8 );
print "Child process exited with status $status\n";
print " OS error was $dollarbang, exception was $dollarat\n";
},
);
๐ง open_process
$process = $loop->open_process( %params );
Since version 0.72.
This creates a new child process to run the given code block or command, and attaches filehandles to it that the parent will watch ๐. This method is a light wrapper around constructing a new IO::Async::Process object, adding it to the loop, and returning it.
The %params hash is passed directly to the IO::Async::Process constructor.
๐ open_child
$pid = $loop->open_child( %params );
A back-compatibility wrapper to calling open_process and returning the PID of the newly-constructed IO::Async::Process instance. The on_finish continuation likewise will be invoked with the PID rather than the process instance.
$on_finish->( $pid, $exitcode );
Similarly, a on_error continuation is accepted, though note its arguments come in a different order to those of the Process's on_exception:
$on_error->( $pid, $exitcode, $errno, $exception );
This method should not be used in new code; instead use open_process directly. It's giving legacy ๐, respectfully.
๐ run_process
@results = await $loop->run_process( %params );
( $exitcode, $stdout ) = await $loop->run_process( ... ); # by default
Since version 0.73.
Creates a new child process to run the given code block or command, optionally capturing its STDOUT and STDERR streams. By default the returned future will yield the exit code and content of the STDOUT stream, but the capture argument can be used to alter what is requested and returned ๐ฆ.
command => ARRAYorSTRINGcode => CODEโ the command or code to run in the child process (as per thespawn_childmethod).stdin => STRINGโ optional. String to pass in to the child process's STDIN stream.setup => ARRAYโ optional reference to an array to pass to the underlyingspawnmethod.capture => ARRAYโ optional reference to an array giving a list of names of values which should be returned by resolving future. Values will be returned in the same order as in the list. Valid choices are:exitcode,stdout,stderr.cancel_signal => STRINGโ optional. Name (or number) of the signal to send to the process if the returned future is cancelled. Defaults toTERM. Use empty string or zero to disable sending a signal on cancellation.fail_on_nonzero => BOOLโ optional. If true, the returned future will fail if the process exits with a nonzero status. The failure will contain a message, theprocesscategory name, and the capture values that were requested.
Future->fail( $message, process => @captures );
This method is intended mainly as an IO::Async-compatible replacement for the perl readpipe function (`backticks`), allowing it to replace
my $output = `command here`;
with
my ( $exitcode, $output ) = await $loop->run_process(
command => "command here",
);
my ( $exitcode, $stdout ) = await $loop->run_process(
command => "/bin/ps",
);
my $status = ( $exitcode >> 8 );
print "ps exited with status $status\n";
๐ง run_child
$pid = $loop->run_child( %params );
Okay so this one is a back-compatibility wrapper for run_process โ like, it's the old-school version that hands you back the PID and wants an on_finish continuation instead of returning a Future. Bestie it's giving vintage ๐
It spins up a brand new child process to run whatever code block or command you gave it, and it's capturing its STDOUT and STDERR streams. Then when the process dips out (exits), a continuation gets invoked with the exitcode and the content of those streams. โจ
On top of everything run_process takes already, it also takes these named args:
on_finish => CODEโ a continuation that gets called when the child process exits and has closed itsSTDOUTandSTDERRstreams. It gets invoked like this:
$on_finish->( $pid, $exitcode, $stdout, $stderr );
The second argument is the plain perl $? value. fr fr.
๐จ Not me saying this but: this method should not be used in new code, okay? Like, please just use run_process directly. It's giving deprecated energy. ๐
๐ resolver
$resolver = $loop->resolver;
Hands you back the internally-stored IO::Async::Resolver object โ the one it uses for name resolution stuff by the resolve, connect and listen methods. ๐
๐ช set_resolver
$loop->set_resolver( $resolver );
Sets the internally-stored IO::Async::Resolver object. In most cases you literally won't need this method at all, but it's there if you wanna provide an alternative resolver for some special use-case. Iconic for the edge-case girlies. โจ
๐ฎ resolve
@result = await $loop->resolve( %params );
This does a single name resolution operation, using an internally-stored IO::Async::Resolver object. Wanna know more? Go check out the resolve method on the IO::Async::Resolver class, bestie. ๐
๐ connect
$handle|$socket = await $loop->connect( %params );
This method does a non-blocking connection to a given address (or a whole set of addresses, no cap), and returns an IO::Async::Future that represents the whole operation. When it's done, the future yields the connected socket handle โ or the given IO::Async::Handle object.
Okay so there are two modes of operation:
- You can hand it a list of addresses and it'll try them one by one.
- Or, for convenience, if you give it a host and service name instead of a list of addresses, those get resolved using the underlying loop's
resolvemethod and turned into the list of addresses. ๐ฆ
So when you're trying to connect to any address among a list, there might be failures among the first attempts before a valid connection actually happens. Like, the resolver might've handed back some IPv6 addresses, but only IPv4 routes are valid on the system โ and in that case, the first connect(2) syscall fails. But that's not a fatal error yet, because there are more addresses to try, maybe some IPv4 ones. ๐
That's why the operation can eventually succeed even though some system calls initially failed. If you wanna be aware of each individual failure, there's an optional on_fail callback you can use โ it gets invoked on every individual socket(2) or connect(2) failure, which is super useful for debugging or logging. ๐ฅ
And because this module just uses the getaddrinfo resolver, it'll be fully IPv6-aware if the underlying platform's resolver is. Which means your programs can be fully IPv6-capable too. Slay. ๐
In plain address mode, the %params hash takes these keys:
addrs => ARRAYโ a reference to an array of (possibly-multiple) address structures to try connecting to. Each one should be in the layout described foraddr. That layout is what thegetaddrinfonamed resolver gives you back.addr => HASH or ARRAYโ shortcut for passing a single address to connect to. You can pass it directly with this key instead of wrapping it in its own array. It should be in a format recognised by IO::Async::OS'sextract_addrinfomethod.
Here's how you use the Socket functions to construct one for TCP port 8001 on address 10.0.0.1:
$loop->connect(
addr => {
family => "inet",
socktype => "stream",
port => 8001,
ip => "10.0.0.1",
},
...
);
And here's another way to connect to a UNIX socket at echo.sock:
$loop->connect(
addr => {
family => "unix",
socktype => "stream",
path => "echo.sock",
},
...
);
peer => IOโ shortcut for constructing an address to connect to the given IO handle. It has to be a IO::Socket or subclass, and it's presumed to be a local listening socket (maybe onPF_UNIXorPF_INET). Super convenient for connecting to a local filehandle, like during a unit test or something similar. ๐งlocal_addrs => ARRAYlocal_addr => HASH or ARRAYโ optional. Just like theaddrsoraddrparameters, these specify a local address (or set of addresses) tobind(2)the socket to beforeconnect(2)ing it. ๐ธ
When you're doing the resolution step too, the addrs or addr keys get ignored, and instead these keys are taken:
host => STRINGservice => STRINGโ the hostname and service name to connect to.local_host => STRINGlocal_service => STRINGโ optional. The hostname and/or service name tobind(2)the socket to locally before connecting to the peer.family => INTsocktype => INTprotocol => INTflags => INTโ optional. Other arguments to pass along withhostandserviceto thegetaddrinfocall.socktype => STRINGโ optionally you can instead use one of the values'stream','dgram'or'raw'to stand forSOCK_STREAM,SOCK_DGRAMorSOCK_RAW. This utility is here so you can avoid a separateuse Socketjust to import these constants. ๐
So it's necessary to pass the socktype hint to the resolver when you're resolving the host/service names into an address, because some OS's getaddrinfo functions require that hint. A warning gets emitted if neither socktype nor protocol hint is defined when doing a getaddrinfo lookup. And to avoid that warning while still specifying no particular socktype hint (maybe to invoke some OS-specific behaviour), just pass 0 as the socktype value. ๐ซถ
In either case, it also accepts these arguments:
handle => IO::Async::Handleโ optional. If you give it a IO::Async::Handle object or a subclass (like IO::Async::Stream or IO::Async::Socket), its handle will be set to the newly-connected socket on success, and that handle gets used as the result of the future instead. โจon_fail => CODEโ optional. After an individualsocket(2)orconnect(2)syscall has failed, this callback gets invoked to let you know about the error. It's passed the name of the syscall that failed, the arguments that were passed to it, and the error it generated. I.e.:
$on_fail->( "socket", $family, $socktype, $protocol, $! );
$on_fail->( "bind", $sock, $address, $! );
$on_fail->( "connect", $sock, $address, $! );
Because of that "try all" nature when you're given a list of multiple addresses, this callback might get invoked multiple times โ even before an eventual success. no cap. ๐
This method accepts an extensions parameter; see the EXTENSIONS section below. ๐
๐ connect (void)
$loop->connect( %params );
When you're not returning a future, you can pass extra parameters containing the continuations to invoke on success or failure. ๐
on_connected => CODEโ a continuation that's invoked on a successfulconnect(2)call to a valid socket. It gets passed the connected socket handle, as anIO::Socketobject.
$on_connected->( $handle );
on_stream => CODEโ an alternative toon_connected. A continuation that's passed an instance of IO::Async::Stream when the socket is connected. This is here as a convenience for the common case where you need a Stream object as the transport for a Protocol object.
$on_stream->( $stream )
on_socket => CODEโ similar toon_stream, but it constructs an instance of IO::Async::Socket. Super useful forSOCK_DGRAMorSOCK_RAWsockets.
$on_socket->( $socket );
on_connect_error => CODEโ a continuation that's invoked after all the addresses have been tried and none of them succeeded. It gets passed the most significant error that occurred, and the name of the operation it occurred in. Errors from theconnect(2)syscall count as most significant, thenbind(2), and then finallysocket(2). ๐
$on_connect_error->( $syscall, $! );
on_resolve_error => CODEโ a continuation that's invoked when the name resolution attempt fails. It's invoked in the same way as theon_errorcontinuation for theresolvemethod.
๐ listen
$listener = await $loop->listen( %params );
This method sets up a listening socket and arranges for an acceptor callback to get invoked every time a new connection is accepted on the socket. Internally it creates an instance of IO::Async::Listener and adds it to the Loop โ unless you gave it one in the arguments. ๐
Addresses can be given directly, or they can be looked up using the system's name resolver, or a socket handle can be given directly. Three options, bestie. โจ
If multiple addresses are given โ or resolved from the service and hostname โ then each one gets attempted in turn until one succeeds, periodt.
In named resolver mode, the %params hash takes these keys:
service => STRINGโ the service name to listen on.host => STRINGโ the hostname to listen on. Optional. It'll listen on all addresses if you don't supply it.family => INTsocktype => INTprotocol => INTflags => INTโ optional. Other arguments to pass along withhostandserviceto thegetaddrinfocall.socktype => STRINGโ optionally you can instead use one of the values'stream','dgram'or'raw'to stand forSOCK_STREAM,SOCK_DGRAMorSOCK_RAW. This utility is here so you can avoid a separateuse Socketjust to import these constants. ๐
It's lowkey essential to pass the socktype hint to the resolver when you're turning host/service names into an address, because some OSes' getaddrinfo functions literally require it. ๐ซ And bestie, if you define neither a socktype nor a protocol hint when doing a getaddrinfo lookup, a warning gets emitted at you. If you want the warning to go away but still genuinely specify no particular socktype hint (like, maybe you're trying to trigger some OS-specific behaviour on purpose, iykyk), just pass 0 as the socktype value. Easy.
In plain address mode, the %params hash takes these keys:
addrs=> ARRAY โ a reference to an array of (possibly multiple) address structures to try listening on. Each one should be in the layout described foraddr. That layout is exactly what thegetaddrinfonamed resolver hands back. ๐ซaddr=> ARRAY โ a shortcut for passing just one address to listen on; you can pass it straight in with this key instead of wrapping it in another array of its own. It should be in a format thatIO::Async::OS'sextract_addrinfomethod recognises. Peep theEXAMPLESsection too. ๐
In direct socket handle mode, these keys get taken:
handle=> IO โ the listening socket handle.
And in either case, these keys are also taken:
on_fail=> CODE โ optional. A callback that fires if a syscall fails while it's trying to create a listening socket. It gets handed the name of the syscall that failed, the arguments that were passed to it, and the error that got generated. Like so:
$on_fail->( "socket", $family, $socktype, $protocol, $! );
$on_fail->( "sockopt", $sock, $optname, $optval, $! );
$on_fail->( "bind", $sock, $address, $! );
$on_fail->( "listen", $sock, $queuesize, $! );
queuesize=> INT โ optional. The queue size to pass along to thelisten(2)calls. If you don't supply it,3gets given instead. ๐reuseaddr=> BOOL โ optional. If it's true or not supplied at all, theSO_REUSEADDRsocket option gets set. Wanna prevent that? Pass a false value, like0.v6only=> BOOL โ optional. If it's defined, it sets or clears theIPV6_V6ONLYsocket option onPF_INET6sockets. That option disables the ability of aPF_INET6socket to accept connections fromAF_INETaddresses. Not every operating system lets you disable this option, jsyk. ๐
If you want more control over the listener, there's another route: just create the IO::Async::Listener object directly and add it explicitly to the Loop. Main character energy, your call bestie. โจ
This method accepts an "extensions" parameter; check the "EXTENSIONS" section below for the whole tea.
๐ธ listen (void) ๐ธ
$loop->listen( %params );
When it's not returning a future, you can hand over extra parameters containing the continuations to invoke on success or on failure. ๐ซถ
on_notifier=> CODE โ optional. A callback that gets invoked when the Listener object is ready to receive connections. It's passed the Listener object itself:
$on_notifier->( $listener );
If you find yourself needing this callback, it might honestly be better to construct the Listener object directly instead. ๐
on_listen=> CODE โ optional. A callback that fires when the listening socket is ready. Typically you'd use this in the name resolver case, so you can inspect the socket's sockname address, or otherwise poke at the filehandle:
$on_listen->( $socket )
on_listen_error=> CODE โ a continuation invoked after all of the addresses have been tried and none of them succeeded. ๐ญ It gets passed the most significant error that happened, plus the name of the operation it happened in. Errors from thelisten(2)syscall count as most significant, thenbind(2), thensockopt(2), and finallysocket(2).on_resolve_error=> CODE โ a continuation invoked when the name resolution attempt fails. It's invoked the exact same way as the"on_error"continuation for the"resolve"method.
๐ OS ABSTRACTIONS ๐
Because the Magic Constructor goes looking for OS-specific subclasses of the Loop, a whole handful of OS service abstractions are provided, just in case specific OSes need to give different implementations on that OS. โจ
๐ signame2num
$signum = $loop->signame2num( $signame );
Legacy wrappers around IO::Async::OS functions. Blast from the past, no cap. ๐
โฐ time
$time = $loop->time;
Returns the current UNIX time in fractional seconds. Right now it's equivalent to Time::HiRes::time, but it's provided here as a little utility so programs can grab the current time that IO::Async uses for its own timing purposes. So cute, so useful. ๐
๐ fork
$pid = $loop->fork( %params );
This method creates a new child process to run a given code block, and returns its process ID. ๐ฆ
code=> CODE โ a block of code to execute in the child process. It gets called in scalar context inside anevalblock. The return value will be used as theexit(2)code from the child if it returns โ or255if it returnedundefor throws an exception. ๐on_exit=> CODE โ an optional continuation to be called when the child process exits. It gets invoked like this:
$on_exit->( $pid, $exitcode );
The second argument is passed the plain perl $? value.
This key is optional; if you don't supply it, the calling code should install a handler using the "watch_process" method instead.
keep_signals=> BOOL โ optional boolean. If it's missing or false, anyCODEreferences in the%SIGhash will be removed and restored back to"DEFAULT"in the child process. If it's true, no adjustment of the%SIGhash gets performed at all.
๐ง create_thread
$tid = $loop->create_thread( %params );
This method creates a new (non-detached) thread to run the given code block, and returns its thread ID. ๐
code=> CODE โ a block of code to execute in the thread. It gets called in the context given by the"context"argument, and its return value will be available to the"on_joined"callback. It's called inside anevalblock; if it fails, the exception gets caught. ๐ฅcontext=>"scalar"|"list"|"void"โ optional. Gives the calling context thatcodeis invoked in. Defaults to"scalar"if you don't supply it.on_joined=> CODE โ callback to invoke when the thread function returns or throws an exception. If it returned, this callback gets invoked with its result:
$on_joined->( return => @result );
If it threw an exception, the callback gets invoked with the value of $@:
$on_joined->( died => $! );
๐ ๏ธ LOW-LEVEL METHODS ๐ ๏ธ
okay so IO::Async::Loop is an abstract base class, which is fancy-speak for "the blueprint, not the building". So specific subclasses of it are required to implement certain methods that form the base level of functionality. Like, these are the load-bearing walls. ๐งฑ
These are not recommended for applications to use, bestie, please don't. Go look at the various event objects or the higher level methods listed above instead, they're right there and they love you. ๐
But also these methods should be considered part of the interface contract required to implement a IO::Async::Loop subclass. So if you're building a subclass? This is the homework. ๐
๐ฎ API_VERSION
IO::Async::Loop->API_VERSION;
The magic constructor calls this method on the class before it's constructed, to make sure the specific implementation will support the required API. Basically a vibe check before the loop even exists. ๐ญ
This method should return the API version that the loop implementation supports. The magic constructor will use that class, provided it declares a version at least as new as the version documented here.
The current API version is 0.49. โจ
You can implement this with constant, like so:
use constant API_VERSION => '0.49';
๐ watch_io
$loop->watch_io( %params );
This method installs callback functions which will be invoked when the given IO handle becomes read- or write-ready. It's giving "text me the second you're free" energy. ๐ฑ
The %params hash takes the following keys:
handle=> IO โ the IO handle to watch. ๐๏ธon_read_ready=> CODE โ optional. ACODEreference to call when the handle becomes read-ready.on_write_ready=> CODE โ optional. ACODEreference to call when the handle becomes write-ready.
There can only be one filehandle of any given fileno registered at any one time. For any one filehandle, there can only be one read-readiness and/or one write-readiness callback at any one time. Registering a new one will remove an existing one of that type. It is not required that both are provided.
๐จ Applications should use aIO::Async::HandleorIO::Async::Streaminstead of using this method. Hand-rolling this one is lowkey not the move, bestie.
If the filehandle does not yet have the O_NONBLOCK flag set, it will be enabled by this method. This will ensure that any subsequent sysread, syswrite, or similar will not block on the filehandle. no blocking on my watch, periodt โ
Since version 0.805 it is permitted to watch for read-readiness and write-readiness of the same filehandle via two separate calls to this method. Prior versions may have implemented it, but it was not specifically documented as being permitted, nor subject to integration testing. so before that it was pure vibes, not officially blessed ๐
๐ unwatch_io
$loop->unwatch_io( %params );
This method removes a watch on an IO handle which was previously installed by watch_io. So it's the un-invite. โ๏ธ
The %params hash takes the following keys:
handle=> IO โ the IO handle to remove the watch for.on_read_ready=> BOOL โ if true, remove the watch for read-readiness.on_write_ready=> BOOL โ if true, remove the watch for write-readiness.
Either or both callbacks may be removed at once. It is not an error to attempt to remove a callback that is not present. If both callbacks were provided to the watch_io method and only one is removed by this method, the other shall remain. so like, no takebacks on the one you didn't mention. ๐
๐ก watch_signal
$loop->watch_signal( $signal, $code );
This method adds a new signal handler to watch the given signal. ๐
$signalโ the name of the signal to watch to. This should be a bare name likeTERM.$codeโ aCODEreference to the handling callback.
There can only be one callback per signal name. Registering a new one will remove an existing one.
Applications should use aIO::Async::Signalobject, or callattach_signalinstead of using this method. for real, there's a nicer door right there ๐ช
This and unwatch_signal are optional; a subclass may implement neither, or both. If it implements neither then signal handling will be performed by the base class using a self-connected pipe to interrupt the main IO blocking. iconic workaround, honestly ๐
๐ด unwatch_signal
$loop->unwatch_signal( $signal );
This method removes the signal callback for the given signal.
$signalโ the name of the signal to watch to. This should be a bare name likeTERM.
โฐ watch_time
$id = $loop->watch_time( %args );
This method installs a callback which will be called at the specified time. The time may either be specified as an absolute value (the at key), or as a delay from the time it is installed (the after key). pick your fighter ๐ฅ
The returned $id value can be used to identify the timer in case it needs to be cancelled by the unwatch_time method. Note that this value may be an object reference, so if it is stored, it should be released after it has been fired or cancelled, so the object itself can be freed. don't hold onto it forever, it's not a keepsake ๐
The %params hash takes the following keys:
at=> NUM โ the absolute system timestamp to run the event.after=> NUM โ the delay after now at which to run the event, ifatis not supplied. A zero or negative delayed timer should be executed as soon as possible; the next time theloop_oncemethod is invoked.now=> NUM โ the time to consider as now if calculating an absolute time based onafter; defaults totime()if not specified.code=> CODE โCODEreference to the continuation to run at the allotted time.
Either one of at or after is required. You don't get to pick neither, bestie. ๐ซถ
For more powerful timer functionality as a IO::Async::Notifier (so it can be used as a child within another Notifier), see instead the IO::Async::Timer object and its subclasses.
These *_time methods are optional; a subclass may implement neither or both of them. If it implements neither, then the base class will manage a queue of timer events. This queue should be handled by the loop_once method implemented by the subclass, using the _adjust_timeout and _manage_queues methods.
This is the newer version of the API, replacing enqueue_timer. It is unspecified how this method pair interacts with the older enqueue/requeue/cancel_timer triplet. so likeโฆ nobody's saying. it's a mystery. โจ
โณ unwatch_time
$loop->unwatch_time( $id );
Removes a timer callback previously created by watch_time. bye bestie ๐
This is the newer version of the API, replacing cancel_timer. It is unspecified how this method pair interacts with the older enqueue/requeue/cancel_timer triplet. again: unspecified. we simply do not know. ๐
๐ฐ๏ธ enqueue_timer
$id = $loop->enqueue_timer( %params );
An older version of watch_time. This method should not be used in new code but is retained for legacy purposes. For simple watch/unwatch behaviour use instead the new watch_time method; though note it has differently-named arguments. For requeueable timers, consider using an IO::Async::Timer::Countdown or IO::Async::Timer::Absolute instead. it's giving vintage, not giving recommended ๐
๐๏ธ cancel_timer
$loop->cancel_timer( $id );
An older version of unwatch_time. This method should not be used in new code but is retained for legacy purposes. legacy, babes, legacy. ๐ซ
๐ requeue_timer
$newid = $loop->requeue_timer( $id, %params );
Reschedule an existing timer, moving it to a new time. The old timer is removed and will not be invoked.
The %params hash takes the same keys as enqueue_timer, except for the code argument.
The requeue operation may be implemented as a cancel + enqueue, which may mean the ID changes. Be sure to store the returned $newid value if it is required. like, genuinely store it, don't be cute about it. ๐ฅ
This method should not be used in new code but is retained for legacy purposes. For requeueable, consider using an IO::Async::Timer::Countdown or IO::Async::Timer::Absolute instead.
๐ watch_idle
$id = $loop->watch_idle( %params );
This method installs a callback which will be called at some point in the near future. soooon. ๐ธ
The %params hash takes the following keys:
when=> STRING โ specifies the time at which the callback will be invoked. See below. ๐code=> CODE โCODEreference to the continuation to run at the allotted time.
The when parameter defines the time at which the callback will later be invoked. Must be one of the following values:
laterโ callback is invoked after the current round of IO events have been processed by the loop's underlyingloop_oncemethod.
If a new idle watch is installed from within a later callback, the installed one will not be invoked during this round. It will be deferred for the next time loop_once is called, after any IO events have been handled. no queue-jumping, babe. ๐ซ
If there are pending idle handlers, then the loop_once method will use a zero timeout; it will return immediately, having processed any IO events and idle handlers.
The returned $id value can be used to identify the idle handler in case it needs to be removed, by calling the unwatch_idle method. Note this value may be a reference, so if it is stored it should be released after the callback has been invoked or cancelled, so the referrant itself can be freed. free your references, free your soul ๐ฆ
This and unwatch_idle are optional; a subclass may implement neither, or both. If it implements neither then idle handling will be performed by the base class, using the _adjust_timeout and _manage_queues methods.
๐ค unwatch_idle
$loop->unwatch_idle( $id );
Cancels a previously-installed idle handler. shhh, it's sleeping now ๐ด
๐ถ watch_process
$loop->watch_process( $pid, $code );
This method adds a new handler for the termination of the given child process PID, or all child processes.
$pidโ the PID to watch. Will report on all child processes if this is0.$codeโ aCODEreference to the exit handler. It will be invoked as
$code->( $pid, $? )
The second argument is passed the plain perl $? value.
After invocation, the handler for a PID-specific watch is automatically removed. The all-child watch will remain until it is removed by unwatch_process. one-and-done vs. subscribed-forever, ikyk. ๐
This and unwatch_process are optional; a subclass may implement neither, or both. If it implements neither then child watching will be performed by using watch_signal to install a SIGCHLD handler, which will use waitpid to 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. it's a race, and honestly? may the best callback win. ๐
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 glib or libuv may not be able, as the underlying event system may not provide the necessary hooks to support it. so likeโฆ check before you commit, fr fr. ๐ญ
๐ช unwatch_process
$loop->unwatch_process( $pid );
This method removes a watch on an existing child process PID. closing that door, respectfully. ๐ซถ
๐ METHODS FOR SUBCLASSES ๐
okay so these next ones are for the subclass girlies only โ the internal bits that specific subclasses need to actually make the loop do its loop thing. Each one's whole deal gets documented up in the sections above, so peek there if you're lost. ๐ซถ
_adjust_timeout
$loop->_adjust_timeout( \$timeout );
So you pass in a scalar reference holding a timeout, and this method goes "hmm, actually" and shortens it if that timeout is longer (in seconds) than the time remaining until the next queued event sitting on the timer queue. And listen โ if there are idle handlers pending? The timeout just gets yeeted all the way down to zero. Bye. ๐
_manage_queues
$loop->_manage_queues;
This one goes snooping through the timer queue for callbacks that should have fired by now, runs every single one of them, and removes them from the queue. It also fires off all the pending idle handlers. Tiny lil caveat bestie: any brand-new idle handlers that get installed by those handlers won't run yet โ they gotta wait for the next time this method gets called. patience, queen. โจ
๐ EXTENSIONS ๐
An Extension is a Perl module that just... adds extra methods into IO::Async::Loop or other packages. The whole vibe is giving "bonus functionality that slots in smooth with the rest of the code." ๐ฆ
Certain base methods take an extensions parameter โ that's an ARRAY reference, a lil list of extension names. When you hand that list to a method, it immediately calls a method named after the base method with the first extension name glued on the front, separated by an _. And if the extensions list has more names left over, those get passed along in another extensions parameter. it's giving relay race. ๐โโ๏ธ
Like, for example:
$loop->connect(
extensions => [qw( FOO BAR )],
%args
);
becomes this:
$loop->FOO_connect(
extensions => [qw( BAR )],
%args
);
The reason this exists? So extension modules โ like IO::Async::SSL, hey bestie โ can get invoked indirectly, just by passing extra args to connect methods or whatever, without every single module having to know the SSL extension exists. No cap. And this whole thing is generic โ it's not SSL-only, any other extension can use it too. ๐
These are the methods that take an extensions parameter:
$loop->connect
$loop->listen
Oh and heads up: if an extension listen method gets invoked, it'll receive a listener parameter even if nobody passed one to the original $loop->listen call, and it will not receive any of the on_* event callbacks. Instead it should use the acceptor parameter on the listener object. iykyk. ๐
๐จ STALL WATCHDOG ๐จ
fr fr, a well-behaved IO::Async program should be spending basically all its time blocked on input through the underlying IO::Async::Loop instance. like, that's the whole aesthetic. The stall watchdog is an optional debugging feature that helps you catch CPU spinlocks and other bugs where control just... never gets handed back to the loop. ๐
Here's the drama: if the watchdog is enabled and an event handler eats up more than a given amount of real time before returning to the event loop, it gets interrupted โ it prints a stack trace and terminates the program. ๐ And important detail bestie: the watchdog is only in effect while the loop itself is not blocking. It won't throw hands just because the loop instance is waiting on input or timers. Chill.
It's implemented using SIGALRM, so if you enable it, that signal is no longer available to your user code. (Though honestly, most uses of alarm() and SIGALRM are better served by one of the IO::Async::Timer subclasses anyway, so. ๐โโ๏ธ)
These environment variables control how it behaves:
IO_ASYNC_WATCHDOG=> BOOL โ turns the stall watchdog on if you set it to a non-zero value. โจIO_ASYNC_WATCHDOG_INTERVAL=> INT โ the watchdog interval in seconds, passed straight to thealarm(2)call. Defaults to10seconds.IO_ASYNC_WATCHDOG_SIGABRT=> BOOL โ if enabled, the watchdog's signal handler raises aSIGABRTinstead, which usually yeets you out of a running program inside debuggers like gdb. If it's not set, the process gets terminated by throwing an exception withdie.
๐ AUTHOR ๐
Paul Evans <leonerd@leonerd.org.uk> โ icon behavior honestly, thank u for ur service. ๐ซก๐
and that's a wrap on this whole page bestie!! ๐ this came from the perl v5.40.1 release, dated 2026-02-21, and it's the IO::Async::Loop(3pm) page โ so like, remember the version and the date when you cite it, mwah. ๐ go build something gorgeous with it, slay, periodt. ๐
โจ
๐ฆ where this page comes from
- ubuntu stonking: libio-async-perl 0.805-1 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms