POE::W...adWrite(3pm)
π POE::Wheel::ReadWrite β the manual page, bestie edition π
(this is the POE::Wheel::ReadWrite(3pm) page from the User Contributed Perl Documentation, and yes we're doing the whole thing)
β¨ NAME β¨
POE::Wheel::ReadWrite β a non-blocking, buffered I/O mix-in for POE::Session. It's giving "your session can read and write without ever blocking" energy. π¦
πΈ SYNOPSIS πΈ
Okay so here's the whole vibe in code. Copy it, it's literally the example:
#!perl
use warnings;
use strict;
use IO::Socket::INET;
use POE qw(Wheel::ReadWrite);
POE::Session->create(
inline_states => {
_start => sub {
# Note: IO::Socket::INET will block. We recommend
# POE::Wheel::SocketFactory or POE::Component::Client::TCP if
# blocking is contraindicated.
$_[HEAP]{client} = POE::Wheel::ReadWrite->new(
Handle => IO::Socket::INET->new(
PeerHost => 'www.yahoo.com',
PeerPort => 80,
),
InputEvent => 'on_remote_data',
ErrorEvent => 'on_remote_fail',
);
print "Connected. Sending request...\n";
$_[HEAP]{client}->put(
"GET / HTTP/0.9",
"Host: www.yahoo.com",
"",
);
},
on_remote_data => sub {
print "Received: $_[ARG0]\n";
},
on_remote_fail => sub {
print "Connection failed or ended. Shutting down...\n";
delete $_[HEAP]{client};
},
},
);
POE::Kernel->run();
exit;
β οΈ heads up bestie βIO::Socket::INETwill block, the comment literally says so. If blocking is a no for you (contraindicated, even), the recommended move isPOE::Wheel::SocketFactoryorPOE::Component::Client::TCP. π
π DESCRIPTION π
POE::Wheel::ReadWrite wraps up one specific design pattern like a cute little gift: dealing with buffered I/O in a non-blocking, event driven fashion. That's the whole thing. π
The pattern goes something like this:
Given a filehandle, watch it for incoming data. The moment you get notified that data has arrived, you read it, buffer it, and parse it according to some low-level protocol (line-by-line, for instance). Then you generate higher-level "here be lines" events β one per parsed line. β¨
In the other direction? You accept whole chunks of data (lines, say) for output. You reformat them according to some low-level protocol (adding newlines, whatever), and you buffer them for output. Then you flush that buffered data whenever the filehandle is actually ready to transmit it. No cap. π«Ά
π PUBLIC METHODS π
π§ Constructor
POE::Wheel subclasses tend to do a lot of setup so that they run lighter and faster later. POE::Wheel::ReadWrite's constructor is no exception β it's putting in the work so you don't have to. πͺ
β new
new() creates and returns a brand new POE:Wheel::ReadWrite instance. In most circumstances, the wheel will keep reading and writing to one or more filehandles until it gets destroyed. Periodt.
β Handle
Handle defines the filehandle that a POE::Wheel::ReadWrite object is going to read from and write to. The "SYNOPSIS" up there has an example using Handle, go look. π
A single POE::Wheel::ReadWrite object can totally read from and write to different filehandles though. See "InputHandle" for more info and an example. β¨
β InputHandle
InputHandle and OutputHandle are how you specify different handles for input and output. Like, input from STDIN and output to STDOUT:
$_[HEAP]{console} = POE::Wheel::ReadWrite->new(
InputHandle => \*STDIN,
OutputHandle => \*STDOUT,
InputEvent => "console_input",
);
π« do NOT do this βInputHandleandOutputHandlemay not be used together withHandle. Pick a lane bestie.
β OutputHandle
InputHandle and OutputHandle are used to specify different handles for input and output. Please see "InputHandle" for more info and an example β it's the same story, just the other end of the pipe. π
β Driver
Driver says how POE::Wheel::ReadWrite will actually read from and write to its filehandle (or filehandles). Driver has to be an object that inherits from POE::Driver. No knockoffs. π
POE::Driver::SysRW, which implements sysread() and syswrite(), is the default. It's used in nearly all cases, so honestly there's no point in specifying it. It's already the moment.
β Filter
Filter is the parser POE::Wheel::ReadWrite uses to recognize input data, and also the serializer it uses to prepare data for writing. It defaults to a new POE::Filter::Line instance, since so many network protocols are line based. π
β InputFilter
InputFilter and OutputFilter may be used to specify different filters for input and output. Different vibes for different directions, y'know? π¦
β OutputFilter
InputFilter and OutputFilter may be used to specify different filters for input and output. Please see "InputFilter" for more information and an example. π
β InputEvent
InputEvent specifies the name of the event that gets sent for every complete input unit (as parsed by InputFilter or Filter). Every input event comes with two parameters:
"ARG0"contains the parsed input unit."ARG1"contains the unique ID for thePOE::Wheel::ReadWriteobject that generated the event.
InputEvent is optional. If you omit it, the POE::Wheel::ReadWrite object will not watch its Handle or InputHandle for input, and no input events will be generated. Silence. π€«
A sample InputEvent handler:
sub handle_input {
my ($heap, $input, $wheel_id) = @_[HEAP, ARG0, ARG1];
print "Echoing input from wheel $wheel_id: $input\n";
$heap->{wheel}->put($input); # Put... the input... beck!
}
β FlushedEvent
FlushedEvent specifies the event a POE::Wheel::ReadWrite object emits whenever its output buffer transitions from containing data to becoming empty. The little glow-up from "has stuff" to "has nothing". β¨
FlushedEvent comes with a single parameter: "ARG0" contains the unique ID for the POE::Wheel::ReadWrite object that generated the event. Use it to match the event to a particular wheel. π
"Flushed" events are often used to shut down I/O after a "goodbye" message has been sent. For example, this input_handler() responds to "quit" by telling the wheel to say "Goodbye." and then to send a "shutdown" event once that's been flushed to the socket:
sub handle_input {
my ($input, $wheel_id) = @_[ARG0, ARG1];
my $wheel = $_[HEAP]{wheel}{$wheel_id};
if ($input eq "quit") {
$wheel->event( FlushedEvent => "shutdown" );
$wheel->put("Goodbye.");
}
else {
$wheel->put("Echo: $input");
}
}
And here's the shutdown handler. It literally just destroys the wheel to end the connection. Bye. π
sub handle_flushed {
my $wheel_id = $_[ARG0];
delete $_[HEAP]{wheel}{$wheel_id};
}
β ErrorEvent
ErrorEvent names the event a POE::Wheel::ReadWrite object will emit whenever an error happens. Every ErrorEvent includes four parameters:
"ARG0"describes what failed, either"read"or"write". It doesn't name a particular function, becausePOE::Wheel::ReadWritedelegates actual reading and writing to aPOE::Driverobject."ARG1"and"ARG2"hold the numeric and string values for$!at the time of failure. Application code cannot test$!directly, since its value may have changed between the time of the error and the time the error event is dispatched. π"ARG3"contains the wheel's unique ID. The wheel's ID is how you tell apart the many wheels managed by a single session.
ErrorEvent may also indicate EOF on a FileHandle by returning operation "read" error 0. For sockets, that means the remote end has closed the connection. π
A sample ErrorEvent handler:
sub error_state {
my ($operation, $errnum, $errstr, $id) = @_[ARG0..ARG3];
if ($operation eq "read" and $errnum == 0) {
print "EOF from wheel $id\n";
}
else {
warn "Wheel $id encountered $operation error $errnum: $errstr\n";
}
delete $_[HEAP]{wheels}{$id}; # shut down that wheel
}
β HighEvent
HighEvent and LowEvent are used along with HighMark and LowMark to control the flow of streamed output. Flow control, but make it β¨dramaticβ¨.
A HighEvent is sent when the output buffer of a POE::Wheel::ReadWrite object exceeds a certain size (the "high water" mark, aka HighMark). This advises the application to stop streaming output. POE and Perl really don't care if the application keeps going, but it's possible the process may run out of memory if a buffer grows without bounds. π
A POE::Wheel::ReadWrite object will continue to flush its buffer even after an application stops streaming data, until the buffer is empty. Some streaming applications may require the buffer to always be primed with data, though. For example, a media server would encounter stutters if it waited for a FlushedEvent before sending more data.
LowEvent solves the stutter problem. A POE::Wheel::ReadWrite object will send a LowEvent when its output buffer drains below a certain level (the "low water" mark, aka LowMark). This notifies the application that the buffer is small enough that it may resume streaming. π
The stutter problem is solved because the output buffer never quite reaches empty. Chef's kiss. π¨βπ³π
HighEvent and LowEvent are edge-triggered, not level-triggered. Which means they're emitted once whenever a POE::Wheel::ReadWrite object's output buffer crosses the HighMark or LowMark. If an application continues to put() data after the HighMark is reached, it will not cause another HighEvent to be sent. One and done.
HighEvent is generally not needed. The put() method will return the high watermark state: true if the buffer is at or above the high watermark, or false if the buffer has room for more data. Here's a quick way to prime a POE::Wheel::ReadWrite object's output buffer:
1 while not $_[HEAP]{readwrite}->put(get_next_data());
POE::Wheel::ReadWrite objects always start in a low-water state. π±
HighEvent and LowEvent are optional. Omit them if flow control is not needed.
β LowEvent
HighEvent and LowEvent are used along with HighMark and LowMark to control the flow of streamed output. Please see "HighEvent" for more information and examples β it's all up there, bestie. π
π put RECORDS π
put() accepts a list of RECORDS, which will be serialized by the wheel's Filter and then buffered and written by its Driver. Send it. π¨
put() returns true if a HighMark has been set and the Driver's output buffer has reached or exceeded the limit. It returns false if HighMark has not been set, or if the Driver's buffer is smaller than that limit.
π± real talk βput()'s return value is purely advisory. An application may continue buffering data beyond theHighMarkβ at the risk of exceeding the process' memory limits. And do not use<1 while not $wheel-"put()>>syntax ifHighMarkisn't set: the application will fail spectacularly! πππ
π event EVENT_TYPE => EVENT_NAME, ... π
event() lets an application modify the events emitted by a POE::Wheel::ReadWrite object. Every constructor parameter ending in "Event" may be changed at run time: "InputEvent", "FlushedEvent", "ErrorEvent", "HighEvent", and "LowEvent". Glow-up on demand. β¨
Setting an event to undef will disable the code within the wheel that generates that event. So for example, stopping InputEvent will also stop reading from the filehandle. That said, "pause_input" and "resume_input" may be a better way to manage input events. Just saying. π
set_filter POE_FILTER
okay so this one is the glow-up button for how your wheel reads and writes π
set_filter() changes the way a POE::Wheel::ReadWrite object parses input and serializes output. Any pending data that hasn't been dispatched to the application yet will get parsed with the new POE_FILTER. But heads up bestie β info that's been put() but not flushed will not be reserialized. it just... stays as it was. π
set_filter() performs the same act as calling set_input_filter() and set_output_filter() with the same POE::Filter object.
Switching filters can be tricky. Please see the discussion ofget_pending()inPOE::Filter. Some filters may not support being dynamically loaded or unloaded.
like, not every filter is built for that kind of chaos, and that's on them. π«Ά
set_input_filter POE_FILTER
set_input_filter() changes a POE::Wheel::ReadWrite object's input filter while leaving the output filter unchanged. So this alters the way data is parsed without affecting how it's serialized for output. in-and-out are two different journeys, bestie. β¨
set_output_filter POE_FILTER
set_output_filter() changes how a POE::Wheel::ReadWrite object serializes its output but does not affect the way data is parsed. same deal, just the other direction. π
get_input_filter
get_input_filter() returns the POE::Filter object currently used by a POE::Wheel::ReadWrite object to parse incoming data. The returned object may be introspected or altered via its own methods. it's yours, go snoop. π
There is no get_filter() method because there is no sane return value when input and output filters differ. fr fr, what would it even return. π
get_output_filter
get_output_filter() returns the POE::Filter object currently used by a POE::Wheel::ReadWrite object to serialize outgoing data. The returned object may be introspected or altered via its own methods. iconic.
There is no get_filter() method because there is no sane return value when input and output filters differ. (yes it says this twice. because it's true twice. π«Ά)
set_high_mark HIGH_MARK_OCTETS
Sets the high water mark β the number of octets that designates a "full enough" output buffer. A POE::Wheel::ReadWrite object will emit a HighEvent when its output buffer expands to reach this point. All put() calls will return true when the output buffer is equal or greater than HIGH_MARK_OCTETS.
Both HighEvent and put() indicate that it's unsafe to continue writing when the output buffer expands to at least HIGH_MARK_OCTETS.
bestie this is the "we are SO full, please stop" alarm π¨
set_low_mark LOW_MARK_OCTETS
Sets the low water mark β the number of octets that designates an "empty enough" output buffer. This event lets an application know that it's safe to resume writing again. π
POE::Wheel::ReadWrite objects will emit a LowEvent when their output buffers shrink to LOW_MARK_OCTETS after having reached HIGH_MARK_OCTETS. so like, full β panic β drained β okay we're chill again. π
ID
ID() returns a POE::Wheel::ReadWrite object's unique ID. ID() is usually called after the object is created so that the object may be stashed by its ID. Events generated by the POE::Wheel::ReadWrite object will include the ID of the object, so that they may be matched back to their sources. it's giving name tag at a party, everyone knows who's who. π·οΈβ¨
pause_input
pause_input() instructs a POE::Wheel::ReadWrite object to stop watching for input, and thus stop emitting InputEvent events. It's much more efficient than destroying the object outright, especially if an application intends to resume_input() later. like don't burn the whole house down just to take a nap. π
resume_input
resume_input() turns a POE::Wheel::ReadWrite object's input watcher back on. It's used to resume watching for input, and thus resume sending InputEvent events. pause_input() and resume_input() implement a form of input flow control, driven by the application itself. you're the main character here, you decide. π
get_input_handle
get_input_handle() returns the filehandle being watched for input.
Manipulating filehandles that are managed by POE may cause nasty side effects, which may change from one POE release to the next. Please use caution.
not me telling you to be careful but like... be careful π
get_output_handle
get_output_handle() returns the filehandle being watched for output.
Manipulating filehandles that are managed by POE may cause nasty side effects, which may change from one POE release to the next. Please use caution.
same warning, same energy, same "please don't" π
shutdown_input
Call shutdown($fh,0) on a POE::Wheel::ReadWrite object's input filehandle. This only works for sockets; nothing will happen for other types of filehandle.
Occasionally, the POE::Wheel::ReadWrite object will stop monitoring its input filehandle for new data. This occurs regardless of the filehandle type.
shutdown_output
Call shutdown($fh,1) on a POE::Wheel::ReadWrite object's output filehandle. This only works for sockets; nothing will happen for other types of filehandle.
Occasionally, the POE::Wheel::ReadWrite object will stop monitoring its output filehandle for new data. This occurs regardless of the filehandle type.
get_driver_out_octets
POE::Driver objects contain output buffers that are flushed asynchronously. get_driver_out_octets() returns the number of octets remaining in the wheel's driver's output buffer. how much is still in the queue, basically. π¬
get_driver_out_messages
POE::Driver objects' output buffers may be message based. Every put() call may be buffered individually. get_driver_out_messages() will return the number of pending put() messages that remain to be sent.
Stream-based drivers will simply return 1 if any data remains to be flushed. This is because they operate with one potentially large message. one big slay, no count. π
flush
flush() manually attempts to flush a wheel's output in a synchronous fashion. This can be used to flush small messages. Note, however, that complete flushing is not guaranteed β to do so would mean potentially blocking indefinitely, which is undesirable in most POE applications. π¬
If an application must guarantee a full buffer flush, it may loop flush() calls:
$wheel->flush() while $wheel->get_driver_out_octets();
However it would be prudent to check for errors as well. A flush() failure may be permanent, and an infinite loop is probably not what most developers have in mind here. π
It should be obvious by now that this method is experimental. Its behavior may change or it may disappear outright. Please let us know whether it's useful.
experimental queen, she might ghost us entirely. tell the devs if she's serving. π§
β¨ SEE ALSO β¨
POE::Wheel describes wheels in general.
The SEE ALSO section in POE contains a table of contents covering the entire POE distribution. go explore bestie, it's a whole world. π
π BUGS π
None known. (we love a clean record, no cap. π₯)
π AUTHORS & COPYRIGHTS π
Please see POE for more information about authors and contributors. giving credit where credit is due, periodt. π
and that's a wrap on the page, bestie π this was written up against perl v5.34.0, dated 2022-03-25, and it's the POE::W...adWrite(3pm) manual page β the one and only. go forth and slay your I/O π
β¨
π¦ where this page comes from
- debian bookworm: libpoe-perl 2:1.3700-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libpoe-perl 2:1.3700-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libpoe-perl 2:1.3700-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu questing: libpoe-perl 2:1.3700-1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms