IO::As...:Stream(3pm)
๐ IO::Async::Stream โ the perl manpage, but make it cute ๐
Ok bestie, gather round, because we're talking about IO::Async::Stream from the IO::Async::Stream bit of the User Contributed Perl Documentation. Grab a strawberry ๐ and let's go.
โจ NAME โจ
"IO::Async::Stream" โ she does event callbacks AND write buffering for a stream filehandle. Both. At once. Multitasking queen. ๐
๐ธ SYNOPSIS ๐ธ
use IO::Async::Stream;
use IO::Async::Loop;
my $loop = IO::Async::Loop->new;
my $stream = IO::Async::Stream->new(
read_handle => \*STDIN,
write_handle => \*STDOUT,
on_read => sub {
my ( $self, $buffref, $eof ) = @_;
while( $$buffref =~ s/^(.*\n)// ) {
print "Received a line $1";
}
if( $eof ) {
print "EOF; last partial line is $$buffref\n";
}
return 0;
}
);
$loop->add( $stream );
$stream->write( "An initial line here\n" );
Like, look at her. Adding herself to a loop, reading STDIN, writing STDOUT, just vibing. ๐ฆ
๐ DESCRIPTION ๐
So IO::Async::Stream is a subclass of IO::Async::Handle, and what she brings to the table is a filehandle that represents a byte-stream. She buffers for you โ both the incoming data AND the outgoing data โ because obviously, we're not doing that by hand. ๐
When new data gets read off the filehandle, she invokes the "on_read" handler. And when you wanna send data out? You just call the "write" method. That's it. No cap.
This class is the one for literally any kind of filehandle that gives you a possibly-bidirectional, reliable byte stream โ think a pipe, a TTY, or a "SOCK_STREAM" socket (so like TCP, or a byte-oriented UNIX local socket). Iconic, frankly. ๐
But bestie, listen: if you've got datagram or raw message-based sockets (UDP girlies, I see you), this is NOT the class for you. Go see IO::Async::Socket instead. Don't embarrass yourself at the function. ๐
๐ฅ EVENTS ๐ฅ
Here's the thing โ these events get invoked either via subclass methods OR via CODE references passed in as parameters. Your choice, your main character moment. โจ
๐ $ret = on_read \$buffer, $eof
Bestie. This one fires when more data is available in the internal receiving buffer. So exciting.
The first argument is a reference to a plain perl string. Your code should poke around in it and remove whatever data it wants. And here's the beautiful part โ you are not required to remove all of it, or honestly even any of it. Whatever data you leave behind in the buffer gets preserved for the next call, the next time more data comes in off the handle. She's patient. She's keeping it for you. ๐ซถ
The way this makes life easy: you can write code that grabs complete records of some form when they're finished and just ignores the partially-received ones until all the data has shown up. Blessed behaviour, honestly.
Now โ if the handler wants to be invoked immediately a second time, to take another crack at consuming more content, it should return 1. Otherwise, return 0, and the handler will next be invoked when more data has arrived from the underlying read handle and been appended to the buffer. That makes it super easy to write code that handles multiple incoming records all at the same time. Alternatively, if your handler function is already trying to consume as much as it possibly can from the buffer, you won't need to return 1 at all. (See the examples at the end of this documentation for more detail โ yes there's more, we eat. ๐ฝ๏ธ)
The second argument is a scalar telling you whether the stream has reported an end-of-file (EOF) condition. A reference to the buffer gets passed to the handler in the usual way, so it can inspect whatever data is sitting in there. Once the handler returns a false value, it will not be called again โ because the handle is at EOF now, and no more data can arrive. That's the end of the era. ๐ญ
Ooh, and this part is fun: the "on_read" code can also dynamically replace itself with a new callback by returning a CODE reference instead of 0 or 1. The original callback or method that the object first started with can be restored by returning "undef". And every single time the callback gets changed in this way, the new code is called again โ even if the read buffer is currently empty. (Again, examples at the end, more detail, we'll get there.)
Also, the "push_on_read" method can be used to insert new, temporary handlers that take precedence over the global "on_read" handler. This event is only used if there are no further pending handlers created by "push_on_read". So, like, know your queue. ๐
๐ on_read_eof
Optional, so no pressure. Invoked when the read handle indicates an end-of-file (EOF) condition. But โ important โ if there's any data in the buffer still to be processed, the "on_read" event will be invoked first, before this one. First things first, always. โ
๐ on_write_eof
Optional. Invoked when the write handle indicates an end-of-file (EOF) condition. Now listen, because this one's a little messy: this condition can only be detected after a "write" syscall returns the "EPIPE" error. And if there's no data pending to be written, then it simply won't be detected yet. Lowkey shy, that's what it is. ๐
๐ญ on_read_error $errno
Optional. Invoked when the "sysread" method on the read handle fails. She's not mad, she's just disappointed. ๐
๐ญ on_write_error $errno
Optional. Invoked when the "syswrite" method on the write handle fails.
A little note here that ACTUALLY matters, bestie: the "on_read_error" and "on_write_error" handlers are passed the value of $! at the time the error occurred. And here's why you should care โ the $! variable itself, by its very nature, may have changed from the original error by the time this handler runs. So always use the value that gets passed in. Always. Don't get caught out. ๐
Now, the dramatic part: if an error occurs when the corresponding error callback is not supplied, and there isn't a handler for it, then the "close" method is called instead. So like, handle your errors or she will handle them for you, and you will not enjoy how. ๐
๐ on_read_high_watermark $length
๐ on_read_low_watermark $length
Optional โ invoked when the read buffer grows larger than the high watermark, or smaller than the low watermark, respectively.
These are edge-triggered events, ok? Which means they only get triggered once per crossing, not continuously while the buffer keeps sitting above or below the given limit. One crossing, one event. She is not going to spam you about it. ๐
If these event handlers aren't defined, the default behaviour is to disable read-ready notifications when the read buffer grows larger than the high watermark โ this is so it doesn't just grow arbitrarily if nothing is consuming it (fr fr, nobody wants a runaway buffer) โ and then re-enable notifications again once something has read enough to make it drop.
But if you override these events, your overriding code has to perform that behaviour itself if you still want it, by using:
$self->want_readready_for_read(...);
๐ on_outgoing_empty
Optional. Invoked when the writing data buffer becomes empty. She's caught up. She's free. Slay. โจ
๐ฆ on_writeable_start
๐ฆ on_writeable_stop
Optional. This little duo keeps you informed about when the filehandle becomes writeable, and when it stops being writeable.
"on_writeable_start"is invoked by the"on_write_ready"event if previously it was known to be not writeable."on_writeable_stop"is invoked after a"syswrite"operation fails with"EAGAIN"or"EWOULDBLOCK".
These two events track the writeability state, and they make sure that only actual state changes cause events to be invoked. No redundant notifications up in here.
And a cheeky little detail to remember: a stream starts off being presumed writeable, which means the first of these two events you'll ever observe will be "on_writeable_stop". Bet you didn't expect that plot twist. ๐
kisses u, more parts coming, don't go anywhere ๐
๐ธ PARAMETERS ๐ธ
okay bestie, here's the tea on all the named params you can hand over to new or configure ๐
they're all optional-ish depending on the vibe, so let's go one by one:
๐ซถ read_handle => IO
The IO handle you're gonna read from. It's gotta have fileno and sysread methods, no cap.
โ๏ธ write_handle => IO
The IO handle you're writing to. This one needs fileno and syswrite methods.
๐ handle => IO
A lil shortcut โ set the same IO handle for both of the ones above at once. iconic, honestly.
๐ on_read => CODE
๐ on_read_error => CODE
๐ on_outgoing_empty => CODE
๐ on_write_error => CODE
๐ on_writeable_start => CODE
๐ on_writeable_stop => CODE
All of these are just CODE references for the event handlers. That's it, that's the whole thing ๐
๐ฅ autoflush => BOOL
Optional. If it's true, the write method will try to yeet data straight to the operating system immediately, without waiting for the loop to say the filehandle is write-ready. Super useful when you have a stream that needs up-to-date logging or console info, like you need that data now bestie.
It currently defaults to false for any file handle, but heads up: future versions of IO::Async may enable this by default on STDOUT and STDERR. So don't build your whole personality around it being false forever, k?
๐ฎ Future-behaviour heads up: the default may flip forSTDOUT/STDERRin later IO::Async versions. Not yet though.
๐ read_len => INT
Optional. Sets the buffer size for read calls. Defaults to 8 KiBytes. cute lil default honestly.
๐ฆ read_all => BOOL
Optional. If true, it'll attempt to read as much data from the kernel as possible when the handle becomes readable. By default this is turned off, which means at most one fixed-size buffer gets read. If there's still more data sitting in the kernel's buffer, the handle is still readable, and it'll get read from again.
The reason for this behaviour is that it lets multiple streams and sockets be multiplexed at the same time, so one big bulk transfer on one handle can't starve the other filehandles of processing time. Turning this option on may improve bulk data transfer rate โ but at the risk of delaying or stalling processing on other filehandles. So like, pick your fighter โจ
๐ write_len => INT
Optional. Sets the buffer size for write calls. Defaults to 8 KiBytes.
๐ write_all => BOOL
Optional. It's the same idea as the read_all option, but for writing. When autoflush is enabled, this one only affects deferred writing if the initial attempt failed due to buffer space.
๐ก๏ธ read_high_watermark => INT
๐ก๏ธ read_low_watermark => INT
Optional. If you define these, you get a way to implement flow control or any other behaviour that depends on how big Stream's read buffer is. Very main character energy ๐
Here's how it works:
- If, after more data is read from the underlying filehandle, the read buffer is now larger than the high watermark, the
on_read_high_watermarkevent gets triggered. By default, that will disable read-ready notifications and pause reading from the filehandle. - If, after data is consumed by an
on_readhandler, the read buffer is now smaller than the low watermark, theon_read_low_watermarkevent gets triggered. By default, that will re-enable read-ready notifications and resume reading from the filehandle. For that to even be possible, the read handler would have to be one added by thepush_on_readmethod, or one of the Future-returningread_*methods.
By default these options are not defined, so none of this behaviour happens. read_low_watermark may not be set to a larger value than read_high_watermark, but it may be set to a smaller value, which creates a hysteresis region. If either option is defined, then both must be.
๐ญ Be careful with these if you're using the default event handlers โ don't cause deadlocks by setting a high watermark low enough that a single on_read invocation might not consider itself finished yet.
๐ง reader => STRING|CODE
๐ง writer => STRING|CODE
Optional. If defined, this gives the name of a method, or a CODE reference, to use to actually implement the reading from or writing to the filehandle. They get invoked like this:
$stream->reader( $read_handle, $buffer, $len );
$stream->writer( $write_handle, $buffer, $len );
Each one is expected to modify the buffer that got passed in โ reader by appending to it, writer by removing a prefix from it. Each one is also expected to return a true value on success, zero on EOF, or undef with $! set for errors. If you don't provide them, they get substituted by implementations that use sysread and syswrite on the underlying handle, respectively. slay ๐
๐ close_on_read_eof => BOOL
Optional. Usually true, but if you set it to a false value then the stream will not be closed when an EOF condition happens on read. Normally this isn't useful, because at that point the underlying stream filehandle isn't useable anymore anyway โ but it can be handy for reading regular files, or interacting with TTY devices. iykyk.
๐ encoding => STRING
If you supply this, it sets the name of the encoding of the underlying stream. If an encoding is set, then:
- The
writemethod will expect to receive Unicode strings and encode them into bytes. - Incoming bytes will be decoded into Unicode strings for the
on_readevent.
If you don't supply an encoding, then write and on_read will work in byte strings. periodt.
โ ๏ธ IMPORTANT NOTE: in order to handle reads of UTF-8 content or other multibyte encodings, the code implementing theon_readevent uses a feature of Encode โ theSTOP_AT_PARTIALflag. While this flag has existed for a while and is used by the:encodingPerlIO layer itself for similar purposes, the flag is not officially documented by theEncodemodule. In principle this undocumented feature could be subject to change โ in practice, the author believes it to be reasonably stable. ๐ซ This note applies only to theon_readevent; data written using thewritemethod does not rely on any undocumented features ofEncode.
speaking of requirements, bestie, listen up:
If a read handle is given, it is required that either an on_read callback reference is configured, or that the object provides an on_read method. Whether either of those is true for on_outgoing_empty is optional โ if neither is supplied, then no action gets taken when the writing buffer becomes empty. ๐
An on_read handler may be supplied even if no read handle has been given yet, to be used when a read handle is eventually provided by the set_handles method. very forward-thinking of you ๐
This condition gets checked at the time the object is added to a Loop. So it's allowed to create a IO::Async::Stream object with a read handle but without an on_read handler โ provided that one is given later using configure before the stream is added to its containing Loop, either directly, or by being a child of another Notifier already in a Loop, or added to one. โจ
๐ METHODS ๐
Okay bestie, the methods listed down in those await expressions? They hand you back Future instances. Slay. ๐
๐ฆ want_readready_for_read / want_readready_for_write
$stream->want_readready_for_read( $set );
$stream->want_readready_for_write( $set );
These two are the mutators for the want_readready property over on IO::Async::Handle, and they're the ones calling the shots on whether the "read" or the "write" behaviour keeps going once the filehandle becomes ready for read. ๐ซถ
So normally? want_readready_for_read is always true (well โ the read watermark behaviour can still mess with it, iykyk), and want_readready_for_write just... isn't used. But if you roll in with a custom "writer" function, it might find this lowkey iconic, because then it can get invoked again when it can't push a write through until the filehandle becomes readable. Think transport negotiation, or SSL key management. โจ
๐ธ want_writeready_for_read / want_writeready_for_write
$stream->want_writeready_for_write( $set );
$stream->want_writeready_for_read( $set );
Same energy, different direction: these mutate the want_writeready property on IO::Async::Handle, controlling whether the "write" or "read" behaviour gets to continue once the filehandle is ready for write. ๐
Normally want_writeready_for_write is managed by the "write" method and all the associated flushing, and want_writeready_for_read is just sitting there unused. But โ again โ if you've got a custom "reader" function, it may find this super useful so it can be invoked again when it's stuck on a read until the filehandle goes writable. Transport negotiation, SSL key management, the whole vibe. ๐
๐ close
$stream->close;
Just a synonym for "close_when_empty". But listen โ do not use this if you need the deferred wait behaviour, because the behaviour of "close" may literally change in a future version of IO::Async. Instead? Call "close_when_empty" directly. Periodt.
๐จ fr fr,closeis the deprecated-ish vibes option here. Go straight toclose_when_empty.
๐ close_when_empty
$stream->close_when_empty;
If the write buffer is empty, this method calls "close" on the underlying IO handles and removes the stream from its containing loop. If the write buffer still has data in it? Then it's deferred until the buffer is empty. It's giving "write-then-close" one-shot streams. ๐ง
$stream->write( "Here is my final data\n" );
$stream->close_when_empty;
But because it's โจdeferredโจ like that, it might not be suitable for error handling. For that, go look at the "close_now" method instead.
๐ฅ close_now
$stream->close_now;
This one does not wait around. It immediately closes the underlying IO handles and removes the stream from the containing loop. It will not wait to flush whatever's left in the write buffer. Bye! ๐
โญ is_read_eof / is_write_eof
$eof = $stream->is_read_eof;
$eof = $stream->is_write_eof;
Returns true once an EOF condition has been reported โ on the read handle or the write handle respectively. Simple as that. ๐ฆ
๐ write
$stream->write( $data, %params );
This method adds data to the outgoing data queue, or writes it immediately โ depends on your "autoflush" parameter. ๐ฌ
If "autoflush" is set, it'll try to write the data to the underlying filehandle right now. If that succeeds, it's written and done by the time this method returns. If it can't write the whole thing, the data gets queued as if "autoflush" weren't set at all, and it'll flush like normal. No cap.
Now, $data can be a plain string, a Future, or a CODE reference:
- Plain string? Written immediately. ๐จ
- Not a plain string? Its value gets used to generate more
$datavalues, eventually leading to strings that get written. $datais a"Future"? The Stream waits until it's ready, then takes the single value it yields.$datais aCODEreference? It gets repeatedly invoked to generate new values. Every time the filehandle is ready for more data, the function is invoked. Once it's done generating data, it should returnundef. The function gets passed the Stream object as its first argument. ๐ซถ
And yes, it's allowed for "Future"s to yield CODE references, or for CODE references to return "Future"s, as well as plain old strings.
For example โ streaming the contents of an already-opened filehandle:
open my $fileh, "<", $path or die "Cannot open $path - $!";
$stream->write( sub {
my ( $stream ) = @_;
sysread $fileh, my $buffer, 8192 or return;
return $buffer;
} );
It takes these optional named parameters in %params:
๐ write_len => INT
Overrides the "write_len" parameter for the data written by this call.
๐ธ on_write => CODE
A CODE reference that gets invoked after every successful "syswrite" operation on the underlying filehandle. It's passed the number of bytes written by this call โ which may not be the entire length of the buffer, because if it takes more than one "syscall" operation to empty the buffer, this callback fires multiple times. ๐
$on_write->( $stream, $len );
๐ on_flush => CODE
A CODE reference invoked once the data queued by this "write" call has been flushed. It gets invoked even if the buffer itself isn't empty yet โ say, if more data has been queued since the call.
$on_flush->( $stream );
โจ on_error => CODE
A CODE reference invoked if a "syswrite" error happens while performing this write. Invoked just like the "Stream"'s "on_write_error" event.
$on_error->( $stream, $errno );
Now here's a lil situation for you: if the object isn't yet a member of a loop and doesn't yet have a "write_handle", then calls to the "write" method just queue the data and return. It gets flushed when the object is added to the loop. ๐ฆ
And if $data is a defined but empty string, the write is still queued, and the "on_flush" continuation gets invoked (if you supplied one). You can use this to grab a marker โ to invoke some code once the output queue has been flushed up to that exact point. Iconic.
๐ write (scalar)
await $stream->write( ... );
If you call this in non-void context, it returns a Future that completes (with no value) when the write operation has been flushed. Use it as an alternative to, or combined with, the "on_flush" callback. ๐
๐ง push_on_read
$stream->push_on_read( $on_read );
Pushes a new temporary "on_read" handler onto the end of the queue. If that queue is non-empty, it provides the "on_read" event handling code in preference to using the object's main event handler or method. You can supply new handlers at any time, and they get used in first-in first-out (FIFO) order. ๐
Same rules as the main "on_read" event handler: each one can return a (defined) boolean to say whether it wants to be invoked again, another "CODE" reference to replace itself with, or "undef" to say it's done and should be removed. When a temporary handler returns "undef", it gets shifted off the queue and the next one โ if there is one โ gets invoked instead. And if there's nothing left? The object's main handler takes over. Slay. ๐
๐ฎ FUTURE-RETURNING READ METHODS ๐ฎ
Okay so these methods all return a Future that becomes ready once enough data has been read by the Stream into its buffer. At that point the data gets removed from the buffer and handed to the "Future" object to complete it.
my $string = await $stream->read_...
And unlike the "on_read" event handlers, these methods do not allow access to "partial" results โ you only get the final result once it's ready. ๐
If a "Future" gets cancelled before it completes, it's removed from the read queue without consuming any data. So each "Future" either atomically completes or is cancelled. No in-between. ๐
Now โ since you can totally drive a readable "Stream" using only these "Future"-returning methods instead of the "on_read" event, it may be useful to configure a trivial return-false event handler to keep it from consuming any input, and to let it be added to a "Loop" in the first place. ๐
my $stream = IO::Async::Stream->new( on_read => sub { 0 }, ... );
$loop->add( $stream );
my $f = $stream->read_...
If a read EOF or error condition happens while there are read "Future"s pending, they all get completed. Read EOF? They're done with "undef". Read error? They're failed using the $! error value as the failure. ๐ญ
$f->fail( $message, sysread => $! )
And if a read EOF condition happens to the read "Future" that's currently processing, it returns a partial result. Your calling code can detect this because the returned data won't be complete per the spec โ too short in "read_exactly"'s case, or missing the ending pattern in "read_until"'s case. Plus, each "Future" yields the $eof value in its results. ๐
my ( $string, $eof ) = await ...;
๐ read_atmost / read_exactly
( $string, $eof ) = await $stream->read_atmost( $len );
( $string, $eof ) = await $stream->read_exactly( $len );
Completes the "Future" when the read buffer contains $len or more characters of input. "read_atmost" will also complete after the first invocation of "on_read", even if fewer characters are available โ whereas "read_exactly" will wait until at least $len are available. Patience is a virtue, bestie. ๐ง
๐ฆ read_until
( $string, $eof ) = await $stream->read_until( $end );
Completes the "Future" when the read buffer contains a match for $end โ which can be either a plain string or a compiled "Regexp" reference. It yields the prefix of the buffer up to and including this match. ๐
๐ read_until_eof
( $string, $eof ) = await $stream->read_until_eof;
Completes the "Future" when the stream is eventually closed at EOF, and yields all the data that was available. The whole thing. โจ
๐ UTILITY CONSTRUCTORS ๐
๐ new_for_stdin / new_for_stdout / new_for_stdio
$stream = IO::Async::Stream->new_for_stdin;
$stream = IO::Async::Stream->new_for_stdout;
$stream = IO::Async::Stream->new_for_stdio;
Returns an "IO::Async::Stream" object preconfigured with the correct "read_handle", "write_handle", or both. Convenience king behavior. ๐
๐ connect
$future = $stream->connect( %args );
A convenient wrapper for calling the "connect" method on the underlying IO::Async::Loop object, passing the "socktype" hint as "stream" if it wasn't otherwise supplied. ๐ซถ
๐ DEBUGGING FLAGS ๐
These flags in "IO_ASYNC_DEBUG_FLAGS" unlock extra logging:
"Sr"โ log byte buffers as data is read from a Stream"Sw"โ log byte buffers as data is written to a Stream
Turn them on when you need to see exactly what's flowing and you're lowkey losing your mind. ๐๐
๐ฆ EXAMPLES ๐ฆ
Okay bestie, this is the part where it all CLICKS. Real code, real vibes. Let's get into it ๐
๐ A line-based on_read method
So imagine you wanna grab incoming lines, each one ending in \n, and just yeet them straight to the program's STDOUT stream. Boom:
sub on_read
{
my $self = shift;
my ( $buffref, $eof ) = @_;
while( $$buffref =~ s/^(.*\n)// ) {
print "Received a line: $1";
}
return 0;
}
The way this works is iconic: since a reference to the buffer itself gets passed in, it's super easy to slap a s/// regular expression on the scalar it's pointing at โ and that one move does double duty. It checks whether data is actually ready (like, is there a whole line in there?), and it removes it from the buffer. Two birds, one regex ๐ฆ๐ฆ
And because it always strips out as many complete lines as it possibly can, there's no need to call it again once it's done โ so it can just return a constant 0. Simple. Clean. Periodt. โจ
๐ Reading binary data
Okay now different vibe. This on_read method takes incoming records in 16-byte chunks and prints each one:
sub on_read
{
my ( $self, $buffref, $eof ) = @_;
if( length $$buffref >= 16 ) {
my $record = substr( $$buffref, 0, 16, "" );
print "Received a 16-byte record: $record\n";
return 1;
}
if( $eof and length $$buffref ) {
print "EOF: a partial record still exists\n";
}
return 0;
}
So this time we did not go with a while() loop โ nope, we decided the handler just processes one record at a time, and we use the return 1 mechanism to ask "hey, run me again if there's still data left that might hold another record." And it only stops with return 0 once we know we can't find one. No cap ๐งข
And the 4-argument form of substr()? That's the move that extracts the 16-byte record out of the buffer and assigns it to the $record variable โ but only if there was enough data in the buffer to extract it in the first place. ๐
Now, a lot of protocols roll like this: a fixed-size header first, then a variable-sized body of data, where the body's size is given by one of the header's fields. Here's the on_read method that pulls messages out of a protocol like that:
sub on_read
{
my ( $self, $buffref, $eof ) = @_;
return 0 unless length $$buffref >= 8; # "N n n" consumes 8 bytes
my ( $len, $x, $y ) = unpack "N n n", $$buffref;
return 0 unless length $$buffref >= 8 + $len;
substr( $$buffref, 0, 8, "" );
my $data = substr( $$buffref, 0, $len, "" );
print "A record with values x=$x y=$y\n";
return 1;
}
In this one, the header gets unpack()ed first to pull out the body length, and then the body gets extracted. If the buffer doesn't have enough data yet for a complete message, it returns 0 and the buffer is left completely unmodified for next time. Only once there are enough bytes in total does it bring in substr() to remove them. Chef's kiss ๐จโ๐ณ๐
๐ง Dynamic replacement of on_read
Okay this one is the main character moment. Imagine a protocol (inspired by IMAP, iykyk) made of \n-terminated lines that might also have an optional data block attached. Whether that data block exists โ and how big it is โ is indicated by the line prefix.
sub on_read
{
my $self = shift;
my ( $buffref, $eof ) = @_;
if( $$buffref =~ s/^DATA (\d+):(.*)\n// ) {
my $length = $1;
my $line = $2;
return sub {
my $self = shift;
my ( $buffref, $eof ) = @_;
return 0 unless length $$buffref >= $length;
# Take and remove the data from the buffer
my $data = substr( $$buffref, 0, $length, "" );
print "Received a line $line with some data ($data)\n";
return undef; # Restore the original method
}
}
elsif( $$buffref =~ s/^LINE:(.*)\n// ) {
my $line = $1;
print "Received a line $line with no data\n";
return 1;
}
else {
print STDERR "Unrecognised input\n";
# Handle it somehow
}
}
In the case where trailing data is supplied, a brand-new temporary on_read callback gets provided inside a closure. That closure captures the $length variable so it knows how much data to expect. And it also captures the $line variable so it can use it in the event report. Then, once this method has finished reading the data, it reports the event and restores the original method by returning undef. Slay ๐ซถ
๐ SEE ALSO ๐
- IO::Handle โ Supply object methods for I/O handles
๐ AUTHOR ๐
Paul Evans <leonerd@leonerd.org.uk>
And that's the whole page, bestie! ๐ This was documented as perl v5.40.1, dated 2026-02-21 โ so if you're reading this from the future, hiii, the lore is intact ๐ Go forth and stream your I/O like the icon you are โจ๐ฆ
๐ฆ 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