POE::Session - an event driven abstract state machine
# Import POE::Session constants. use POE::Session; # The older, more DWIMmy constructor. POE::Session->new( # Inline or coderef states. state_one => \&coderef_one, state_two => sub { ... }, # Plain object and package states. $object_one => [ 'state_three', 'state_four', 'state_five' ], $package_one => [ 'state_six', 'state_seven', 'state_eight' ], # Mapped object and package states. $object_two => { state_nine => 'method_nine', ... }, $package_two => { state_ten => 'method_ten', ... }, # Parameters for the session's _start state. \@start_args, ); # The newer, more explicit and safer constructor. POE::Session->create( # Inline or coderef states. inline_states => { state_one => \&coderef_one, state_two => sub { ... }, }, # Plain and mapped object states. object_states => [ $object_one => [ 'state_three', 'state_four', 'state_five' ], $object_two => { state_nine => 'method_nine' }, ], # Plain and mapped package states. package_states => [ $package_one => [ 'state_six', 'state_seven', 'state_eight' }, $package_two => { state_ten => 'method_ten' }, ], # Parameters for the session's _start state. args => [ argument_zero, argument_one, ... ], # Initial options. See the option() method. options => \%options, # Change the session's heap representation. heap => [ ], );
Other methods:
# Retrieve a session's unique identifier. $session_id = $session->ID; # Retrieve a reference to the session's heap. $session_heap = $session->get_heap(); # Set or clear session options. $session->option( trace => 1, default => 1 ); $session->option( trace ); # Create a postback, then invoke it and pass back additional # information. $postback_coderef = $session->postback( $state_name, @state_args ); &{ $postback_coderef }( @additional_args );
POE::Session combines a runtime context with an event driven state machine. Together they implement a simple cooperatively timesliced thread.
Sessions receive their timeslices as events from POE::Kernel. Each event has two fields, a state name and a session identifier. These fields describe the code to run and the context to run it in, respectively. Events carry several other fields which will be discussed in the "Predefined Event Fields" section.
States are re-entrant since they are invoked with their runtime contexts. Although it's not usually necessary, this re-entrancy allows a single function to be bound to several different sessions, under several different state names.
As sessions run, they post new events through the Kernel. These events may be for themselves or other sessions, in which case they act as a form of inter-session communications. The Kernel can also generate events based on external conditions such as file activity or the passage of time.
POE provides some convenient built-in states with special meanings. They will be covered later on in the "Predefined States" section.
ID() returns the session instance's unique identifier. This is a number that starts with 1 and counts up forever, or until something causes the number to wrap. It's theoretically possible that session IDs may collide after about 4.29 billion sessions have been created.
create() is the recommended Session constructor. It binds states to their corresponding event names, initalizes other parts of the session, and then fires off its _start state, possibly with some parameters.
_start
create's parameters look like a hash of name/value pairs, but it's really just a list. create() is preferred over the older, more DWIMmy new() constructor because each kind of parameter is explicitly named. This makes it easier for maintainers to understand the constructor call, and it lets the constructor unambiguously recognize and validate parameters.
The args parameter accepts a reference to a list of parameters that will be passed to the machine's _start state. They are passed in the _start event's ARG0..$#_ fields.
args
ARG0..$#_
args => [ 'arg0', 'arg1', 'etc.' ], sub _start { my @args = @_[ARG0..#$_]; print "I received these parameters from create()'s args: @args\n"; }
The heap parameter defines a session's heap. The heap is passed into states as the $_[HEAP] field. Heaps are anonymous hash references by default.
heap
POE::Session->create( ..., heap => { runstate_variable => 1 }, ... ); sub state_function { my $heap = $_[HEAP]; print "runstate variable is $heap->{runstate_variable}\n"; }
It's also possible to use create's heap parameter to change the heap into something completely different, such as a list reference or even an object.
sub RUNSTATE_VARIABLE () { 0 } # offset into the heap POE::Session->create( ..., heap => [ 1 ], ... ); sub state_function { my $heap = $_[HEAP]; print "runstate variable is ", $heap->[RUNSTATE_VARIABLE], "\n"; }
inline_states maps events names to the plain coderefs which will handle them. Its value is a reference to a hash of event names and corresponding coderefs.
inline_states
inline_states => { _start => sub { print "arg0=$_[ARG0], arg1=$_[ARG1], etc.=$_[ARG2]\n"; } _stop => \&stop_handler, },
These states are called "inline" because they can be inline anonymous subs.
object_states maps event names to the object methods which will handle them. Its value is a listref of object references and the methods to use. It's a listref because using a hashref would stringify its keys, and the object references would become unusable.
object_states
The object's methods can be specified in two ways.
The first form associates a listref to each object reference. This form maps each event to an object method with the same name. In this example, event_one is handled by $object's event_one() method.
event_one
$object
event_one()
object_states => [ $object => [ 'event_one', 'event_two' ], ];
The second form associates a hashref to each object reference. In turn, the hashref maps each event name to a method in the object. In this form, the object's method names needn't match the event names they'll handle. For example, event_four is handled by $object's handler_four() method.
event_four
$object's
handler_four()
object_states => [ $object => { event_three => 'handler_three', event_four => 'handler_four', } ];
options contains a new session's initial options. It's equivalent to creating the session and then calling its option() method to set them. HASHREF contains a set of option/value pairs.
options
These two statements are equivalent:
POE::Session->create( ..., options => { trace => 1, debug => 1 }, ..., ); POE::Session->create( ..., )->option( trace => 1, debug => 1 );
See the option() method for a list of options and values.
package_states maps event names to the package methods which will handle them. It's very similar to object_states. package_states' value is a listref of package names and the methods to use. It's a listref for consistency with object_states.
package_states
The package's methods can be specified in two ways.
The first form associates a listref to each package name. This form maps each event to a package method with the same name. In this example, event_ten is handled by Package's event_ten() method.
event_ten
Package
event_ten()
package_states => [ Package => [ 'event_ten', 'event_eleven' ], ];
The second form associates a hashref to each package n ame. In turn, the hashref maps each event name to a method in the package. In this form, the package's method names needn't match the event names they'll handle. For example, event_twelve is handled by Package's handler_twelve() method.
event_twelve
handler_twelve()
package_states => [ Package => { event_twelve => 'handler_twelve', event_thirteen => 'handler_thirteen', } ];
new() is Session's older constructor. Its design was clever at the time, but it didn't expand well. It's still useful for quick one-line hacks, but consider using create() for more complex sessions.
new()
create()
Inline states, object states, package states, and _start arguments are all inferred by their contexts. This context sensitivity makes it harder for maintainers to understand what's going on, and it allows errors to be interpreted as different behavior.
Inline states are specified as a scalar mapped to a coderef.
event_one => \&state_one, event_two => sub { ... },
Object states are specified as object references mapped to list or hash references. Objects that are mapped to listrefs will handle events with identically named methods.
$object_one => [ 'event_one', 'event_two' ],
Objects that are mapped to hashrefs can handle events with differently named methods.
$object_two => { event_ten => 'method_foo', event_eleven => 'method_bar' },
Packgae states are specified as package names mapped to list or hash references. Package names that are mapped to listrefs will handle events with identically named methods.
PackageOne => [ 'event_five', 'event_six' ],
Package names that are mapped to hashrefs can handle events with differently named methods.
PackageTwo => { event_seven => 'method_baz', event_eight => 'method_quux' },
Arguments for the _start state are specified as listrefs.
[ 'arg0', 'arg1', ... ],
So, in summary, the rules for this constructor are:
If a scalar appears as the "key" field ... If a coderef appears as its "value" ... Then it's an inline event handler. If a listref appears as its "value" ... Then it's a set of package states with the same names. If a hashref appears as its "value" ... Then it's a set of package states with possibly different names. Otherwise, it's an error. If an object reference appears as the "key" field ... If a listref appears as its "value" ... Then it's a set of object states with the same names. If a hashref appears as its "value" ... Then it's a set of object states with possibly different names. Otherwise, it's an error. If a listref appears as the "key" field ... Then it's a set of C<_start> arguments, and it has no "value".
option() sets and/or retrieves options' values.
option()
The first form returns the value of a single option, OPTION_NAME, without changing it.
my $trace_value = $_[SESSION]->option( 'trace' );
The second form sets OPTION_NAME to OPTION_VALUE, returning the previous value of OPTION_NAME.
my $old_trace_value = $_[SESSION]->option( trace => $new_trace_value );
The final form sets several options, returning a hashref containing pairs of option names and their previous values.
my $old_values = $_[SESSION]->option( trace => $new_trace_value, debug => $new_debug_value, ); print "Old option values:\n"; while (my ($option, $old_value) = each %$old_values) { print "$option = $old_value\n"; }
postback() creates anonymous coderefs which, when called, post EVENT_NAME events back to the session whose postback() method was called. Postbacks hold external references on the sessions they're created for, so they keep their sessions alive.
The EVENT_NAME event includes two fields, both of which are list references. ARG0 contains a reference to the PARAMETER_LIST passed to postback(). This is the "request" field. ARG1 holds a reference to the parameters passed to the coderef when it's called. That's the "response" field.
ARG0
postback()
ARG1
This creates a Tk button that posts an "ev_counters_begin" event to $session whenever it's pressed.
$session
$poe_tk_main_window->Button ( -text => 'Begin Slow and Fast Alarm Counters', -command => $session->postback( 'ev_counters_begin' ) )->pack;
postback() works wherever a callback does. Another good use of postbacks is for request/response protocols between sessions. For example, a client session will post an event to a server session. The client may include a postback as part of its request event, or the server may build a postback based on $_[SENDER] and an event name either pre-arranged or provided by the client.
$_[SENDER]
Since postback() is a Session method, you can call it on $_[SESSION] to create a postback for the current session. In this case, the client gives its postback to the server, and the server would call the postback to return a response event.
$_[SESSION]
# This code is in a client session. SESSION is this session, so it # refers to the client. my $client_postback = $_[SESSION]->postback( reply_event_name => $data );
The other case is where the server creates a postback to respond to a client. Here, it calls postback() on $_[SENDER] to create a postback that will respond to the request's sender.
# This code is in a server session. SENDER is the session that sent # a request event: the client session. my $client_postback = $_[SENDER]->postback( reply_event_name => $data );
In the following code snippets, Servlet is a session that acts like a tiny daemon. It receives requests from "client" sessions, performs som long-running task, and eventually posts responses back. Client sends requests to Servlet and eventually receives its responses.
# Aliases are a common way for daemon sessions to advertise # themselves. They also provide convenient targets for posted # requests. Part of Servlet's initialization is setting its alias. sub Servlet::_start { ...; $_[KERNEL]->alias_set( 'server' ); } # This function accepts a request event. It creates a postback # based on the sender's information, and it saves the postback until # it's ready to be used. Postbacks keep their sessions alive, so # this also ensures that the client will wait for a response. sub Servlet::accept_request_event { my ($heap, $sender, $reply_to, @request_args) = @_[HEAP, SENDER, ARG0, ARG1..$#_]; # Set the request in motion based on @request_args. This may take # a while. ...; # Create a postback, and hold onto it so we'll have a way to # respond back to the client session when the request has # finished. $heap->{postback}->{$sender} = $sender->postback( $reply_to, @request_args ); } # When the server is ready to respond, it retrieves the postback and # calls it with the response's values. The postback acts like a # "wormhole" back to the client session. Letting the postback fall # out of scope destroys it, so it will stop keeping the session # alive. The response event, however, will take up where the # postback left off, so the client will still linger at least as # long as it takes to receive its response. sub Servlet::ready_to_respond { my ($heap, $sender, @response_values) = @_[HEAP, ARG0, ARG1..$#_]; my $postback = delete $heap->{postback}->{$sender}; $postback->( @response_values ); } # This is the client's side of the transaction. Here it posts a # request to the "server" alias. sub Client::request { my $kernel = $_[KERNEL]; # Assemble a request for the server. my @request = ( 1, 2, 3 ); # Post the request to the server. $kernel->post( server => accept_request_event => reply_to => @request ); } # Here's where the client receives its response. Postback events # have two parameters: a request block and a response block. Both # are array references containing the parameters given to the # postback at construction time and at use time, respectively. sub Client::reply_to { my ($session, $request, $response) = @_[SESSION, ARG0, ARG1]; print "Session ", $session->ID, " requested: @$request\n"; print "Session ", $session->ID, " received : @$response\n"; }
get_heap() returns a reference to a session's heap. It's the same value that's passed to every state via the HEAP field, so it's not necessary within states.
get_heap()
HEAP
Combined with the Kernel's get_active_session() method, get_heap() lets libraries access a Session's heap without having to be given it. It's convenient, for example, to write a function like this:
get_active_session()
sub put_stuff { my @stuff_to_put = @_; $poe_kernel->get_active_session()->heap()->{wheel}->put( @stuff_to_put ); } sub some_state { ...; &put_stuff( @stuff_to_put ); }
While it's more efficient to pass HEAP along, it's also less convenient.
sub put_stuff { my ($heap, @stuff_to_put) = @_; $heap->{wheel}->put( @stuff_to_put ); } sub some_state { ...; &put_stuff( $_[HEAP], @stuff_to_put ); }
Although if you expect to have a lot of calls to &put_a_wheel() in your program, you may want to optimize for programmer efficiency by using the first form.
Each session maintains its unique runtime context. Sessions pass their contexts on to their states through a series of standard parameters. These parameters tell each state about its Kernel, its Session, itself, and the events that invoke it.
State parameters' offsets into @_ are never used directly. Instead they're referenced by symbolic constant. This lets POE to change their order without breaking programs, since the constants will always be correct.
These are the @_ fields that make up a session's runtime context.
ARG0..ARG9 are a state's first ten custom parameters. They will always be at the end of @_, so it's possible to access more than ten parameters with $_[ARG9+1] or even this:
ARG0..ARG9
@_
$_[ARG9+1]
my @args = @_[ARG0..$#_];
The custom parameters often correspond to PARAMETER_LIST in many of the Kernel's methods. This passes the words "zero" through "four" to some_state as @_[ARG0..ARG4]:
some_state
@_[ARG0..ARG4]
$_[KERNEL]->yield( some_state => qw( zero one two three four ) );
Only ARG0 is really needed. ARG1 is just ARG0+1, and so on.
ARG0+1
HEAP is a session's unique runtime storage space. It's separate from everything else so that Session authors don't need to worry about namespace collisions.
States that store their runtime values in the HEAP will always be saving it in the correct session. This makes them re-entrant, which will be a factor when Perl's threading stops being experimental.
sub _start { $_[HEAP]->{start_time} = time(); } sub _stop { my $elapsed_runtime = time() - $_[HEAP]->{start_time}; print 'Session ', $_[SESSION]->ID, " elapsed runtime: $elapsed_runtime\n"; }
KERNEL is a reference to the Kernel. It's used to access the Kernel's methods from within states.
KERNEL
# Fire a "time_is_up" event in ten seconds. $_[KERNEL]->delay( time_is_up => 10 );
It can also be used with SENDER to make sure Kernel events have actually come from the Kernel.
SENDER
OBJECT is only meaningful in object and package states.
OBJECT
In object states, it contains a reference to the object whose method is being invoked. This is useful for invoking plain object methods once an event has arrived.
sub ui_update_everything { my $object = $_[OBJECT]; $object->update_menu(); $object->update_main_window(); $object->update_status_line(); }
In package states, it contains the name of the package whose method is being invoked. Again, it's useful for invoking plain package methods once an event has arrived.
sub Package::_stop { $_[PACKAGE]->shutdown(); }
OBJECT is undef in inline states.
SENDER is a reference to the session that sent an event. It can be used as a return address for service requests. It can also be used to validate events and ignore them if they've come from unexpected places.
This example shows both common uses. It posts a copy of an event back to its sender unless the sender happens to be itself. The condition is important in preventing infinite loops.
sub echo_event { $_[KERNEL]->post( $_[SENDER], $_[STATE], @_[ARG0..$#_] ) unless $_[SENDER] == $_[SESSION]; }
SESSION is a reference to the current session. This lets states access their own session's methods, and it's a convenient way to determine whether SENDER is the same session.
SESSION
sub enable_trace { $_[SESSION]->option( trace => 1 ); print "Session ", $_[SESSION]->ID, ": dispatch trace is now on.\n"; }
STATE contains the event name that invoked a state. This is useful in cases where a single state handles several different events.
STATE
sub some_state { print( "some_state in session ", $_[SESSION]-ID, " was invoked as ", $_[STATE], "\n" ); } POE::Session->create( inline_states => { one => \&some_state, two => \&some_state, six => \&some_state, ten => \&some_state, } );
my ($caller_file, $caller_line) = @_[CALLER_FILE,CALLER_LINE];
The file and line number from which this state was called.
POE contains helpers which, in order to help, need to emit predefined events. These events all being with a single leading underscore, and it's recommended that sessions not post leading-underscore events unless they know what they're doing.
Predefined events generally have serious side effects. The _start event, for example, performs a lot of internal session initialization. Posting a redundant _start event may try to allocate a session that already exists, which in turn would do terrible, horrible things to the Kernel's internal data structures. Such things would normally be outlawed outright, but the extra overhead to check for them would slow everything down all the time. Please be careful! The clock cycles you save may be your own.
These are the predefined events, why they're emitted, and what their parameters mean.
_child is a job-control event. It notifies a parent session when its set of child sessions changes.
_child
ARG0 contains one of three strings describing what is happening to the child session.
A child session has just been created, and the current session is its original parent.
This session is gaining a new child from a child session that has stopped. A grandchild session is being passed one level up the inheritance tree.
This session is losing a child which has stopped.
ARG1 is a reference to the child session. It will still be valid, even if the child is in its death throes, but it won't last long enough to receive posted events. If the parent must interact with this child, it should do so with call() or some other means.
call()
ARG2 is only valid when a new session has been created. When ARG0 is 'create', this holds the new session's _start state's return value.
ARG2
_default is the event that's delivered whenever an event isn't handled. The unhandled event becomes parameters for _default.
_default
It's perfectly okay to post events to a session that can't handle them. When this occurs, the session's _default handler is invoked instead. If the session doesn't have a _default handler, then the event is quietly discarded.
Quietly discarding events is a feature, but it makes catching mistyped event names kind of hard. There are a couple ways around this: One is to define event names as symbolic constants. Perl will catch typos at compile time. The second way around it is to turn on a session's debug option (see Session's option() method). This makes unhandled events hard runtime errors.
debug
As was previously mentioned, unhandled events become _default's parameters. The original state's name is preserved in ARG0 while its custom parameter list is preserved as a reference in ARG1.
sub _default { print "Default caught an unhandled $_[ARG0] event.\n"; print "The $_[ARG0] event was given these parameters: @{$_[ARG1]}\n"; }
All the other _default parameters are the same as the unhandled event's, with the exception of STATE, which becomes _default.
Beware! A _default handler will catch unhandled signal events. In this case, _default's return value is used to determine whether the signal has been handled. If any _default handler always returns true, then it will catch all but the nonmaskable signals, and only SIGKILL may stop them.
POE::Kernel discusses signal handlers in "Signal Watcher Methods". It also covers the pitfals of _default states in more detail
_parent It notifies child sessions that their parent sessions are in the process of changing. It is the complement to _child.
_parent
ARG0 contains the session's previous parent, and ARG1 contains its new parent.
_signal is a session's default signal handler. Every signal that isn't mapped to a specific state will be delivered to this one.
_signal
ARG0 contains the signal's name as it appears in Perl's %SIG hash. That is, it's the root name of the signal without the SIG prefix.
Unhandled _signal events will be forwarded to _default. In this case, the _default handler's return value becomes significant. It's possible to accidentally write unkillable programs this way.
If _signal and _default handlers don't exist, then signals will always be unhandled.
POE::Kernel's "Signal Watcher Methods" section is recommended reading before using _signal or _default. It discusses the different signal levels, the mechanics of signal propagation, and why it's always important to return an explicit value from a signal handler, among other things.
_start is a session's initialization event. It tells a session that the Kernel has allocated and initialized resources for it, and it may now start doing things. A session's constructors invokes the _start handler before it returns, so it's possible for some sessions' _start states to run before $poe_kernel->run() is called.
Every session must have a _start handler. Its parameters are slightly different from normal ones.
SENDER contains a reference to the new session's parent. Sessions created before $poe_kernel->run() is called will have KERNEL as their parents.
ARG0..$#_ contain the parameters passed into the Session's constructor. See Session's new() and create() methods for more information on passing parameters to new sessions.
_stop is sent to a session when it's about to stop. This usually occurs when a session has run out of events to handle and resources to generate new events.
_stop
The _stop handler is used to perform shutdown tasks, such as releasing custom resources and breaking circular references so that Perl's garbage collection will properly destroy things.
Because a session is destroyed after a _stop handler returns, any POE things done from a _stop handler may not work. For example, posting events from _stop will be ineffective since part of the Session cleanup is removing posted events.
States are always evaluated in a scalar context. States that must return more than one value should therefore return them as a reference to something bigger.
Signal handlers' return values are significant. POE::Kernel's "Signal Watcher Methods" sections covers this is detail.
States may not return references to objects in the "POE" namespace. The Kernel will stringify these references to prevent them from lingering and beraking its own garbage collection.
POE::Kernel tracks resources on behalf of its active sessions. It generates events corresponding to these resources' activity, notifying sessions when it's time to do things.
The conversation goes something like this.
Session: Be a dear, Kernel, and let me know when someone clicks on this widget. Thanks so much! [TIME PASSES] [SFX: MOUSE CLICK] Kernel: Right, then. Someone's clicked on your widget. Here you go.
Furthermore, since the Kernel keeps track of everything sessions do, it knows when a session has run out of tasks to perform. When this happens, the Kernel emits a _stop event at the dead session so it can clean up and shutdown.
Kernel: Please switch off the lights and lock up; it's time to go.
Likewise, if a session stops on its own and there still are opened resource watchers, the Kernel knows about them and cleans them up on the session's behalf. POE excels at long-running services because it so meticulously tracks and cleans up its resources.
While time's passing, however, the Kernel may be telling Session other things are happening. Or it may be telling other Sessions about things they're interested in. Or everything could be quiet... perhaps a little too quiet. Such is the nature of non-blocking, cooperative timeslicing, which makes up the heart of POE's threading.
Some resources must be serviced right away, or they'll faithfully continue reporting their readiness. These reports would appear as a stream of duplicate events, which would be bad. These are "synchronous" events because they're handled right away.
The other kind of event is called "asynchronous" because they're posted and dispatched through a queue. There's no telling just when they'll arrive.
Synchronous event handlers should perform simple tasks limited to handling the resources that invoked them. They are very much like device drivers in this regard.
Synchronous events that need to do more than just service a resource should pass the resource's information to an asynchronous handler. Otherwise synchronous operations will occur out of order in relation to asynchronous events. It's very easy to have race conditions or break causality this way, so try to avoid it unless you're okay with the consequences.
Many external libraries expect plain coderef callbacks, but sometimes programs could use asynchronous events instead. POE::Session's postback() method was created to fill this need.
postback() creates coderefs suitable to be used in traditional callbacks. When invoked as callbacks, these coderefs post their parameters as POE events. This lets POE interact with nearly every callback currently in existing, and most future ones.
Sessions are resources, too. The Kernel watches sessions come and go, maintains parent/child relationships, and notifies sessions when these relationships change. These events, _parent and _child, are useful for job control and managing pools of worker sessions.
Parent/child relationships are maintained automatically. "Child" sessions simply are ones which have been created from an existing session. The existing session which created a child becomes its "parent".
A session with children will not spontaneously stop. In other words, the presence of child sessions will keep a parent alive.
POE::Session contains a two debugging assertions, for now.
ASSERT_DEFAULT is used as the default value for all the other assert constants. Setting it true is a quick and reliably way to ensure all Session assertions are enabled.
Session's ASSERT_DEFAULT inherits Kernel's ASSERT_DEFAULT value unless overridden.
Setting ASSERT_STATES to true causes every Session to warn when they are asked to handle unknown events. Session.pm implements the guts of ASSERT_STATES by defaulting the "default" option to true instead of false. See the option() function earlier in this document for details about the "default" option.
POE::Kernel.
The SEE ALSO section in POE contains a table of contents covering the entire POE distribution.
There is a chance that session IDs may collide after Perl's integer value wraps. This can occur after as few as 4.29 billion sessions.
Please see POE for more information about authors and contributors.
To install POE, copy and paste the appropriate command in to your terminal.
cpanm
cpanm POE
CPAN shell
perl -MCPAN -e shell install POE
For more information on module installation, please visit the detailed CPAN module installation guide.