Revision: 2258
http://poe.svn.sourceforge.net/poe/?rev=2258&view=rev
Author: gwyn17
Date: 2007-12-08 03:50:48 -0800 (Sat, 08 Dec 2007)
Log Message:
-----------
Middle-sized documentation review
Modified Paths:
--------------
trunk/poe/lib/POE/Kernel.pm
Modified: trunk/poe/lib/POE/Kernel.pm
===================================================================
--- trunk/poe/lib/POE/Kernel.pm 2007-12-08 02:27:54 UTC (rev 2257)
+++ trunk/poe/lib/POE/Kernel.pm 2007-12-08 11:50:48 UTC (rev 2258)
@@ -2599,9 +2599,15 @@
POE::Kernel->run();
exit;
+=head2 POE::Kernel singleton
+
+The POE::Kernel is a singleton object; there can be only one POE::Kernel
+instance within a process. This allows many object methods to also be
+package methods.
+
=head2 Sessions
-POE implements isolated compartments called "sessions". Sessions play
+POE implements isolated compartments called I<sessions>. Sessions play
the role of tasks or threads within POE. POE::Kernel acts as POE's
task scheduler, doling out timeslices to each session.
@@ -2612,14 +2618,16 @@
take exclusive control of a program's time, if necessary.
Every POE-based application needs at least one session. Code cannot
-run "within POE" without being a part of some session.
+run I<within POE> without being a part of some session.
Sessions in POE::Kernel should not be confused with
L<POE::Session|POE::Session> even though the two are inextricably
associated. POE::Session adapts POE::Kernel's dispatcher to a
particular calling convention. Other POE::Session classes exist on
-CPAN. Some radically alter the way event handlers are called.
+the CPAN. Some radically alter the way event handlers are called.
+L<http://search.cpan.org/search?query=poe+session>.
+
=head2 Session Lifespans
"Session" as a term is somewhat overloaded. There are two related
@@ -2645,18 +2653,17 @@
case, POE::Kernel forces the session to stop, and all resources
associated with the session are pre-emptively released.
-The resources that keep sessions active. Each is explained elsewhere
-in more detail.
+Resources keep sessions active. Each is explained elsewhere in more detail.
=over 2
-=item
+=item Events.
-Events. Posting an event keeps both the sender and the receiver alive
-until after the event has been dispatched. This is only guaranteed if
-both the sender and receiver are in the same process. Inter-Kernel
-message passing add-ons may have other guarantees. Please see their
-documentation for details.
+An event is a message to a sessions. Posting an event keeps both the sender
+and the receiver alive until after the event has been dispatched. This is
+only guaranteed if both the sender and receiver are in the same process.
+Inter-Kernel message passing add-ons may have other guarantees. Please see
+their documentation for details.
The rationale is that the event is in play, so the receiver must
remain active for it to be dispatched. The sender remains alive in
@@ -2665,56 +2672,81 @@
Posted events cannot be pre-emptively canceled. They tend to be
short-lived in practice, so this generally isn't an issue.
-=item
+=item Timers.
-Timers. Once set, a timer will keep its session active until it goes
-off and the resulting event is dispatched. The session setting the
-timer is kept active so that it will eventually receive the timer's
-event.
+Timers allow an application to send a message to the future. Once set, a
+timer will keep the destination session active until it goes off and the
+resulting event is dispatched.
-=item
+=item Aliases.
-Aliases. Aliases act as passive event watchers. As long as a session
-has an alias, some other session may send events to that session by
-that name. Aliases keep sessions alive as long as a process has
-active sessions.
+Session aliases are an application-controled way of addressing a session.
+Aliases act as passive event watchers. As long as a session has an alias,
+some other session may send events to that session by that name. Aliases
+keep sessions alive as long as a process has active sessions.
-If the only sessions remaining are being kept alive solely by their
-aliases, POE::Kernel will send them a terminal IDLE signal. In most
-cases this will terminate the remaining sessions and allow the program
-to exit. If the sessions remain in memory without waking up on the
-IDLE signal, POE::Kernel sends them a nonmaskable ZOMBIE signal. They
-are then forcibly removed, and the program will finally exit.
+If the only sessions remaining are being kept alive solely by their aliases,
+POE::Kernel will send them a terminal L</IDLE> signal. In most cases
+this will terminate the remaining sessions and allow the program to exit.
+If the sessions remain in memory without waking up on the C<IDLE> signal,
+POE::Kernel sends them a nonmaskable L</ZOMBIE> signal. They are then
+forcibly removed, and the program will finally exit.
-=item
+=item I/O watchers.
-I/O watchers. A session will remain active as long as a session is
-paying attention to some external data source or sink.
+A session will remain active as long as a session is
+paying attention to some external data source or sink. See
+L<select_read|"select_read FILE_HANDLE [, EVENT_NAME [, ADDITIONAL_PARAMETERS] ]">
+and
+L<select_write|"select_write FILE_HANDLE [, EVENT_NAME [, ADDITIONAL_PARAMETERS] ]">.
-=item
+=item Child sessions.
-Child sessions. A session acting as a parent of one or more other
+A session acting as a parent of one or more other
sessions will remain active until all the child sessions stop. This
may be bypassed by detaching the children from the parent.
-=item
+=item Child processes.
-Child processes watched by sig_child(). The sig_child() watcher will
+Child process are watched by sig_child(). The sig_child() watcher will
keep the watching session active until the child process has been
reaped by POE::Kernel and the resulting event has been dispatched.
-All other signal watchers, including using sig() to watch for "CHLD",
-do not keep their sessions active. If you need a session to remain
-active when it's only watching for signals, have it set an alias or
-one of its own reference counters.
+All other signal watchers, including using L</sig> to watch for C<CHLD>, do
+not keep their sessions active. If you need a session to remain active when
+it's only watching for signals, have it set an alias or one of its own
+public reference counters.
-=item
+=item Public reference counters.
-Public reference counters. A session will remain active as long as it
-has one or more nonzero public reference counters.
+A session will remain active as long as it
+has one or more nonzero public (or external) reference counter.
=back
+
+=head2 Events
+
+An event is a message that is sent from one part of the POE application to
+another. An event consists of the event's name, optional event-specific
+parameters and OOB information. An event may be sent from the kernel, from
+a wheel or from a session.
+
+An application creates an event with L</post>, L</yield>, L</call> or even
+L</signal>. POE::Kernel creates events in response external stimulous
+(signals, select, etc).
+
+An event is handled by a function called an I<event handler>, generaly attached
+to a session. See L</Event Handler Management> and L<POE::Session>.
+
+The term I<state> is often used in place of I<event handler>, especially
+when treating sessions as event driven state machines.
+
+TODO - discuss the POE::Kernel queue
+
+
+
+
=head2 Using POE with Other Event Loops
POE::Kernel supports any number of event loops. Four are included in
@@ -2729,7 +2761,7 @@
Cooperation with other event loops also lets you embed POE code into
other software. For example, one can embed networking code into Vim,
so non-blocking HTTP clients into irssi because they all cooperatively
-share Glib.
+share L<Glib>.
Because this is Perl, there are multiple ways to load an alternate
event loop. The simplest way is to load the event loop before loading
@@ -2740,14 +2772,14 @@
Remember that POE loads POE::Kernel internally.
-POE::Kernel examines the modules loaded before it and detects that Gtk
-has been loaded. If POE::Loop::Gtk is available, POE loads and hooks
+POE::Kernel examines the modules loaded before it and detects that L<Gtk>
+has been loaded. If L<POE::Loop::Gtk|POE::Loop::Gtk> is available, POE loads and hooks
it into POE::Kernel automatically.
-It's less mysterious to load the appropriate POE::Loop class directly.
-Their names follow the format "POE::Loop::$loop_module_name", where
-$loop_module_name is the name of the event loop module after each "::"
-has been substituted with an underscore.
+It's less mysterious to load the appropriate L<POE::Loop|POE::Loop> class
+directly. Their names follow the format C<POE::Loop::$loop_module_name>,
+where C<$loop_module_name> is the name of the event loop module after each
+C<::> has been substituted with an underscore.
use POE::Loop::Event_Lib;
use POE;
@@ -2762,10 +2794,11 @@
use POE::Kernel { loop => "Glib" };
Many external event loops support their own callback mechanisms.
-POE::Session's postbaic() and callback() methods return plain Perl
-code references that will generate POE events when called.
-Applications can pass these code references to event loops for use as
-callbacks.
+L<POE::Session|POE::Session>'s L<"postback()"|POE::Session/postback> and
+L<"callback()"|POE::Session/callback>
+methods return plain Perl code references that will generate POE events when
+called. Applications can pass these code references to event loops for use
+as callbacks.
These are the four loops included in POE's distribution:
@@ -2778,35 +2811,33 @@
=head3 POE::Loop::Event
This event loop provides interoperability with other modules that use
-Event. It may also provide a performance boost because Event is
-written in a compiled language. Event is however less portable than
-Perl's built-in select().
+L<Event>. It may also provide a performance boost because L<Event> is
+written in a compiled language. Unfortunately, this makes L<Event> less
+portable than Perl's built-in select().
=head3 POE::Loop::Gtk
-This event loop allows programs to work under the Gtk graphical
+This event loop allows programs to work under the L<Gtk> graphical
toolkit.
=head3 POE::Loop::Tk
-This event loop allows programs to work under the Tk graphical
+This event loop allows programs to work under the L<Tk> graphical
toolkit. Tk has some restrictions that require POE to behave oddly.
-Tk's event loop will not run unless one or more widgets are created.
-POE must therefore create such a widget before it can run.
-POE::Kernel exports $poe_main_window so that the application developer
-may use the widget (which is a Tk MainWindow), since POE doesn't need
-it other than for dispatching events.
+Tk's event loop will not run unless one or more widgets are created. POE
+must therefore create such a widget before it can run. POE::Kernel exports
+$poe_main_window so that the application developer may use the widget (which
+is a L<MainWindow|Tk/MainWindow>), since POE doesn't need it other than for
+dispatching events.
Creating and using a different MainWindow often has an undesired
outcome.
-MainWindow->new() often has an undesired outcome.
-
=head3 POE::Loop::IO_Poll
-The IO::Poll event loop provides an alternative that theoretically
-scales better than select().
+The L<IO::Poll|IO::Poll> event loop provides an alternative that
+theoretically scales better than select().
=head1 PUBLIC METHODS
@@ -2818,11 +2849,15 @@
=head3 ID
ID() returns the kernel's unique identifier. Every POE::Kernel
-instance is assigned a (hopefully) unique ID at birth.
+instance is assigned a (hopefully) globaly unique ID at birth.
% perl -wl -MPOE -e 'print $poe_kernel->ID'
poerbook.local-46c89ad800000e21
+While the IDs are made globaly unique by including hostname, time and PID,
+they should be considered an opaque but printable string. That is, your
+code should not depend on the current format.
+
=head3 run
run() runs POE::Kernel's event dispatcher. It will not return until
@@ -2890,8 +2925,8 @@
Sessions are not notified about their destruction. If anything relies
on _stop being delivered, it will break and/or leak memory.
-stop() is still considered experimental. It was added to improve
-fork() support for POE::Wheel::Run. If it proves unfixably
+stop() is still considered experimental. It was added to improve fork()
+support for L<POE::Wheel::Run|POE::Wheel::Run>. If it proves unfixably
problematic, it will be removed without much notice.
stop() is advanced magic. Programmers who think they need it are
@@ -2988,7 +3023,7 @@
}
);
-The POE::Wheel classes uses call() to synchronously deliver I/O
+The L<POE::Wheel|POE::Wheel> classes uses call() to synchronously deliver I/O
notifications. This avoids a host of race conditions.
call() may fail in the same way and for the same reasons as post().
@@ -3006,19 +3041,18 @@
until either an absolute time ("alarms") or until a certain duration
of time has elapsed ("delays").
-Timer interfaces are further divided into two groups. One group
-identifies timers by the names of their associated events. Another
-group's timer constructors return identifiers that can be used to
-refer to specific timers regardless of name. Technically, the two
-are both name-based, but the "identifier-based" timers provide a
-second, more specific handle to identify individual timers.
+Timer interfaces are further divided into two groups. One group identifies
+timers by the names of their associated events. Another group identifies
+timers by a unique identifyer returned by the timer constructors.
+Technically, the two are both name-based, but the "identifier-based" timers
+provide a second, more specific handle to identify individual timers.
Timers may only be set up for the current session. This design was
modeled after alarm() and SIGALRM, which only affect the current UNIX
process. Each session has a separate namespace for timer names.
Timer methods called in one session cannot affect the timers in
-another. (As you may have noticed, quite a lot of POE's API is
-designed to prevent sessions from interfering with each other.)
+another. As you may have noticed, quite a lot of POE's API is
+designed to prevent sessions from interfering with each other.
The best way to simulate deferred inter-session messages is to send an
immediate message that causes the destination to set a timer. The
@@ -3035,7 +3069,7 @@
Subsecond accuracy is supported through the use of select() timeouts
and other event-loop features. For increased accuracy, POE::Kernel
-uses Time::HiRes's time() internally, if it's available.
+uses L<Time::HiRes|Time::HiRes>'s time() internally, if it's available.
You can disable POE's use of Time::HiRes by defining a constant in the
POE::Kernel namespace. This must be done before POE::Kernel is
@@ -3048,7 +3082,7 @@
use POE;
Or the old-fashioned (and more concise) "constant subroutine" method.
-This doesn't need the BEGIN{} block since subroutine definitions are
+This doesn't need the C<BEGIN{}> block since subroutine definitions are
done at compile time.
sub POE::Kernel::USE_TIME_HIRES () { 0 }
@@ -3079,7 +3113,7 @@
EPOCH_TIME is the UNIX epoch time. You know, seconds since midnight,
1970-01-01. "Now" is whatever time() returns, either the built-in or
-Time::HiRes version.
+L<Time::HiRes|Time::HiRes> version.
POE supports fractional seconds, but accuracy falls off steeply after
1/100 second. Mileage will vary depending on your CPU speed and your
@@ -3271,7 +3305,7 @@
},
postpone => sub {
my $new_time = $_[KERNEL]->alarm_adjust(
- $_[HEAP]{alarm_id}, 10
+ $_[HEAP]{alarm_id}, -10
);
print(
"Now we're gonna party like it's ",
@@ -3471,10 +3505,10 @@
}
);
-Kernels also maintain a global session namespace from which sessions
-may reserve symbolic aliases. Once an alias is reserved, that alias
-may be used to refer to the session wherever a session may be
-specified.
+Kernels also maintain a global session namespace or dictionary from which
+may be used to map a symblic aliases to a session. Once an alias is mapping
+has been created, that alias may be used to refer to the session wherever a
+session may be specified.
In the previous examples, each echoer service has set an "echoer"
alias. Another session can post a ping request to the echoer session
@@ -3487,19 +3521,18 @@
}
);
-A session with an alias will not stop until all other activity has
-stopped. Aliases are treated as a kind of event watcher. The events
-come from active sessions. Aliases therefore become useless when
-there are no active sessions left. Rather than leaving the program
-running in a "zombie" state, POE detects this deadlock condition and
-triggers a cleanup.
+A session with an alias will not stop until all other activity has stopped.
+Aliases are treated as a kind of event watcher. Events come from active
+sessions. Aliases therefore become useless when there are no active
+sessions left. Rather than leaving the program running in a "zombie" state,
+POE detects this deadlock condition and triggers a cleanup.
-TODO Discuss SIGIDLE and SIGZOMBIE somewhere.
+TODO Link to SIGIDLE and SIGZOMBIE documentation.
=head3 alias_set ALIAS
-alias_set() enters an ALIAS for the current session into POE::Kernel's
-dictionary. The ALIAS may then be used nearly everywhere a session
+alias_set() maps an ALIAS in POE::Kernel's dictonary to the
+current session. The ALIAS may then be used nearly everywhere a session
reference, stringified reference, or ID is expected.
Sessions may have more than one alias. Each alias must be defined in
@@ -3553,7 +3586,7 @@
As previously mentioned, alias_resolve() returns a session reference
or undef on failure. Failure also sets $! to ESRCH ("No such
-process") when the ALIAS is not currently in POE::Kernel's dictionary.
+process") when the ALIAS is not currently in POE::Kernel's.
=head3 alias_list [SESSION_REFERENCE]
@@ -3767,7 +3800,7 @@
This statement:
- $_[KERNEL]->select( $file_handle, undef, "write_event", @stuff );
+ $_[KERNEL]->select( $file_handle, undef, "write_event", undef, @stuff );
is equivalent to:
@@ -3783,10 +3816,10 @@
=head2 Session Management
-Sessions are dynamic. They may be created and destroyed during a
-program's lifespan. When a session is created, it becomes the "child"
-of the current session. The creator---the current session---becomes
-its "parent" session. This is loosely modeled after UNIX processes.
+Sessions are dynamic. They may be created and destroyed during a program's
+lifespan. When a session is created, it becomes the "child" of the current
+session. The creator -- the current session -- becomes its
+"parent" session. This is loosely modeled after UNIX processes.
The most common session management is done by creating new sessions
and allowing them to eventually stop.
@@ -3799,7 +3832,7 @@
Lifespans> for more about why sessions stay alive.
The parent/child relationship tree also governs the way many signals
-are dispatched. See L<Signal Watchers> for more information on that.
+are dispatched. See L</Signal Watchers> for more information on that.
=head3 Session Management Events (_start, _stop, _parent, _child)
@@ -3809,23 +3842,28 @@
=over 2
-=item
+=item _start
_start should be familiar by now. POE calls it to initialize a newly
created session. What is not readily apparent, however, is that it is
-invoked before the POE::Session constructor returns.
+invoked before the L<POE::Session|POE::Session> constructor returns.
The _start event's "sender" is the new session's creator and current
parent.
The _start handler's return value is passed to the parent session in a
-_child event, along with the notification that the parent's new child
-was created successfully.
+_child event, along with the notification that the parent's new child was
+created successfully. Like _start, _child is invoked before the
+POE::Session constructor returns.
-_start and _child are invoked before the POE::Session constructor
-returns.
+ POE::Session->create( inline_states => { _start=>\&_start },
+ args => [ $some, $args ] );
+ sub _start {
+ my( $some, $args ) = @_[ ARG0, ARG1 ];
+ # ....
+ }
-=item
+=item _stop
_stop is a little more mysterious. POE calls a _stop handler when a
session is irrevocably about to be destroyed. Part of session
@@ -3842,14 +3880,20 @@
corresponding _child handler is invoked synchronously along with
_stop.
-=item
+=item _parent
_parent is used to notify a child session when its parent has changed.
This usually happens when a session is first created. It can also
-happen when a child session is detached from its parent.
+happen when a child session is detached from its parent. See
+L<detach_child|/"detach_child CHILD_SESSION"> and L</detach_myself>.
-=item
+ sub _parent {
+ my( $session, $new_parent ) = @_[ SESSION, ARG0 ];
+ # ....
+ }
+=item _child
+
_child notifies one session when a child session has been created,
destroyed, or reassigned to or from another parent. It's usually
dispatched when sessions are created or destroyed. It can also happen
@@ -3870,37 +3914,110 @@
session's _start handler. Likewise, ARG2 holds the _stop handler's
return value for the 'lose' case.
+ sub _child {
+ my( $reason, $child ) = @_[ ARG0, ARG1 ];
+ if( $reason eq 'create' ) {
+ my $retval = $_[ ARG2 ];
+ }
+ # ...
+ }
+
=back
-The events are delivered in specific orders:
+The events are delivered in specific orders.
-When a new session is created. (1) The session's constructor is
-called. (2) The session is put into play. That is, POE::Kernel
-enters the session into its bookkeeping. (3) The new session receives
-_start. (4) The parent session receves _child with 'create', the new
-session reference, and the new session's _start's return value. (5)
+=head4 When a new session is created:
+
+=over 4
+
+=item 1
+
+The session's constructor is called.
+
+=item 2
+
+The session is put into play. That is, POE::Kernel
+enters the session into its bookkeeping.
+
+=item 3
+
+The new session receives _start.
+
+=item 4
+
+The parent session receves _child ('create'), the new
+session reference, and the new session's _start's return value.
+
+=item 5
+
The session's constructor returns.
-When an old session stops. (1) If the session has children of its
+=back
+
+
+=head4 When an old session stops:
+
+=over 4
+
+=item 1
+
+If the session has children of its
own, they are given to the session's parent. This triggers one or
more _child ('gain') events in the parent, and a _parent in each
-child. (2) Once divested of its children, the stopping session
-receives a _stop event. (3) The stopped session's parent receives a
+child.
+
+=item 2
+
+Once divested of its children, the stopping session
+receives a _stop event.
+
+=item 3
+
+The stopped session's parent receives a
_child ('lose') event with the departing child's reference and _stop
-handler's return value. (4) The stopped session is removed from play,
-as are all its remaining resources. (5) The parent session is checked
+handler's return value.
+
+=item 4
+
+The stopped session is removed from play,
+as are all its remaining resources.
+
+=item 5
+
+The parent session is checked
for idleness. If so, garbage collection will commence on it, and it
too will be stopped
-When a session is detached from its parent. (1) The parent session of
+=back
+
+=head4 When a session is detached from its parent:
+
+=over 4
+
+=item 1
+
+The parent session of
the session being detached is notified with a _child ('lose') event.
The _stop handler's return value is undef since the child is not
-actually stopping. (2) The detached session is notified that its new
-parent is POE::Kernel itself. (3) POE::Kernel's bookkeeping data is
-adjusted to reflect the change of parentage. (4) The old parent
-session is checked for idleness. If so, garbage collection will
-commence on it, and it too will be stopped
+actually stopping.
+=item 2
+
+The detached session is notified with a _parent event that its new parent is
+POE::Kernel itself.
+
+=item 3
+
+POE::Kernel's bookkeeping data is adjusted to reflect the change of
+parentage.
+
+=item 4
+
+The old parent session is checked for idleness. If so, garbage collection
+will commence on it, and it too will be stopped
+
+=back
+
=head3 Session Management Methods
These methods allow sessions to be detached from their parents in the
@@ -3914,80 +4031,107 @@
however, detach_child() returns false and sets $! to explain the
nature of the failure:
-ESRCH ("No such process"). The CHILD_SESSION is not a valid session.
+=over 4
-EPERM ("Operation not permitted"). The CHILD_SESSION exists, but it
-is not a child of the current session.
+=item ESRCH ("No such process").
-detach_child() will generate _parent and/or _child events to the
-appropriate sessions. See L<Session Management Events> for a detailed
-explanation of these events.
+The CHILD_SESSION is not a valid session.
-TODO - Chart the events generated, and the order in which they are
-dispatched.
+=item EPERM ("Operation not permitted").
+The CHILD_SESSION exists, but it is not a child of the current session.
+
+=back
+
+detach_child() will generate L</_parent> and/or L</_child> events to the
+appropriate sessions. See L</"Session Management Events"> for a detailed
+explanation of these events. See
+L<above|/"When a session is detached from its parent:">
+for the order the events are generated.
+
=head4 detach_myself
detach_myself() detaches the current session from its current parent.
The new parent will be the running POE::Kernel instance. It returns
-true on success. On failure it returns false and sets $! to
+true on success. On failure it returns false and sets C<$!> to
explain the nature of the failure:
-EPERM ("Operation not permitted"). The current session is alreay a
+=over 4
+
+=item EPERM ("Operation not permitted").
+
+The current session is alreay a
child of POE::Kernel, so it may not be detached.
-detach_child() will generate _parent and/or _child events to the
-appropriate sessions. See L<Session Manaement Events> for a detailed
-explanation of these events.
+=back
-TODO - Chart the events generated, and the order in which they are
-dispatched.
+detach_child() will generate L</_parent> and/or L</_child> events to the
+appropriate sessions. See L</"Session Manaement Events"> for a detailed
+explanation of these events. See
+L<above|/"When a session is detached from its parent:">
+for the order the events are generated.
-=head3 Signals
+
+=head2 Signals
+
POE::Kernel provides methods through which a program can register
interest in signals that come along, can deliver its own signals
without resorting to system calls, and can indicate that signals have
-been handled so that defauld behaviors are not necessary.
+been handled so that default behaviors are not necessary.
-Signals are action at a distance by nature, and their implementation
+Signals are I<action at a distance> by nature, and their implementation
requires widespread synchronization between sessions (and re-entrancy
in the dispatcher, but that's an implementation detail). Perfecting
-the semantics has proven difficult, but POE tries to do the right
-thing whenever possible.
+the semantics has proven difficult, but POE tries to do the Right
+Thing whenever possible.
POE does not register %SIG handlers for signals until sig() is called
to watch for them. Therefore a signal's default behavior occurs for
unhandled signals. That is, SIGINT will gracelessly stop a program,
SIGWINCH will do nothing, SIGTSTP will pause a program, and so on.
-=head4 Signal Classes (benign, terminal and nonmaskable)
+=head3 Signal Classes
There are three signal classes. Each class defines a default behavior
for the signal and whether the default can be overridden. They are:
-=over 2
+=head4 Benign, advisory, or informative signals
-=item
+These are three names for the same signal class. Signals in this class
+notify a session of an event but do not terminate the session if they are
+not handled.
-Benign, advisory, or informative signals. These are three names for
-the same signal class. Signals in this class notify a session of an
-event but do not terminate the session if they are not handled.
+It is possible for an application to create its own benign signals. See
+L</signal> below.
-=item
+
+=head4 Terminal signals
+
Terminal signals will kill sessions if they are not handled by a
-sig_handled() call. The OS signals that usually kill or dump a
-process are considered terminal in POE, but they never trigger a
-coredump. These are: HUP, INT, QUIT and TERM.
+L</sig_handled>() call. The OS signals that usually kill or dump a process
+are considered terminal in POE, but they never trigger a coredump. These
+are: HUP, INT, QUIT and TERM.
-There are two terminal signals created by and used within POE: IDLE
-and DIE. The IDLE signal is used to notify leftover sessions that a
-program has run out of things to do. DIE notifies sessions that a
-Perl exception has occurred. See L<Exception Handling> for details.
+There are two terminal signals created by and used within POE:
-=item
+=over
+=item DIE
+
+C<DIE> notifies sessions that a Perl exception has occurred. See
+L</"Exception Handling"> for details.
+
+=item IDLE
+
+The C<IDLE> signal is used to notify leftover sessions that a
+program has run out of things to do.
+
+=back
+
+=head4 Nonmaskable signals
+
Nonmaskable signals are terminal regardless whether sig_handled() is
called. The term comes from "NMI", the nonmaskable CPU interrupt
usually generated by an unrecoverable hardware exception.
@@ -3995,31 +4139,38 @@
Sessions that receive a nonmaskable signal will unavoidably stop. POE
implements two nonmaskable signals:
-ZOMBIE. This nonmaskable signal is fired if a program has received an
-IDLE signal but neither restarted nor exited. The program has become
-a zombie (that is, it's neither dead nor alive, and only exists to
-consume memory). The ZOMBIE signal acts livke a cricket bat to the
-head, bringing the zombie down, for good.
+=over
-UIDESTROY. This nonmaskable signal indicates that a program's user
+=item ZOMBIE
+
+This nonmaskable signal is fired if a program has received an C<IDLE> signal
+but neither restarted nor exited. The program has become a zombie (that is,
+it's neither dead nor alive, and only exists to consume braaaains ...er...
+memory). The C<ZOMBIE> signal acts like a cricket bat to the head,
+bringing the zombie down, for good.
+
+=item UIDESTROY
+
+This nonmaskable signal indicates that a program's user
interface has been closed, and the program should take the user's hint
and buzz off as well. It's usually generated when a particular GUI
widget is closed.
=back
+
=head3 Common Signal Dispatching
Most signals are not dispatched to a single session. POE's session
lineage (parents and children) form a sort of family tree. When a
signal is sent to a session, it first passes through any children (and
-grandchildren, and so on) that are also interested in the signale
+grandchildren, and so on) that are also interested in the signal.
-In the case of terminal signals, if any of the sessions a signal
-passes through calls sig_handled(), then the signal is considered
-taken care of. However if none of them do, then the entire session
-tree rooted at the destination session is terminated. For example,
-consider this tree of sessions:
+In the case of terminal signals, if any of the sessions a signal passes
+through calls L</sig_handled>(), then the signal is considered taken care
+of. However if none of them do, then the entire session tree rooted at the
+destination session is terminated. For example, consider this tree of
+sessions:
POE::Kernel
Session 2
@@ -4034,7 +4185,9 @@
A signal sent to Session 2 may also be dispatched to sessionl 4 and 5
because they are 2's children. Sessions 4 and 5 will only receive the
-signal if they have registered the appropriate watcher.
+signal if they have registered the appropriate watcher. If the signal is
+terminal, and none of the signal watchers in sessions 2, 4 and 5 called
+C<sig_handled()>, all 3 sessions will be terminated.
The program's POE::Kernel instance is considered to be a session for
the purpose of signal dispatch. So any signal sent to POE::Kernel
@@ -4046,24 +4199,48 @@
Certain signals have special semantics.
-=head4 SIGCHLD (also known as SIGCLD)
+=head4 SIGCHLD
-Both SIGCHLD and SIGCLD indicate that a child process has exited or
+=head4 SIGCLD
+
+Both C<SIGCHLD> and C<SIGCLD> indicate that a child process has exited or
been terminated by some signal. The actual signal name varies between
-operating systems, but POE uses "CHLD" regardless.
+operating systems, but POE uses C<CHLD> regardless.
-Interest in SIGCHLD is registered using the sig_child() method. The
-sig() method also works, but it's not as nice.
+Interest in C<SIGCHLD> is registered using the L</sig_child> method. The
+L</sig>() method also works, but it's not as nice.
-The SIGCHLD event includes three parameters: C<ARG0> contains the
-string 'CHLD' (even if the OS calls it SIGCLD, SIGMONKEY, or something
-else). C<ARG1> contains the process ID of the finished child process.
+The C<SIGCHLD> event includes three parameters:
+
+=over
+
+=item ARG0
+
+C<ARG0> contains the string 'CHLD' (even if the OS calls it SIGCLD, SIGMONKEY, or something
+else).
+
+=item ARG1
+
+C<ARG1> contains the process ID of the finished child process.
+
+=item ARG2
+
And C<ARG2> holds the value of C<$?> for the finished process.
-SIGCHLD is not handled ny registering a %SIG handler, although it may
+=back
+
+Example:
+
+ sub sig_CHLD {
+ my( $name, $PID, $exit_val ) = @_[ ARG0, ARG1, ARG2 ];
+ # ...
+ }
+
+
+SIGCHLD is not handled by registering a C<%SIG> handler, although it may
be in the future. For now, POE polls for child processes using a
-non-blocking waitpid() call. This is much more portable and reliable
-than setting $SIG{CHLD}, although it's somewhat less responsive.
+non-blocking C<waitpid()> call. This is much more portable and reliable
+than setting C<$SIG{CHLD}>, although it's somewhat less responsive.
=head4 SIGPIPE
@@ -4075,17 +4252,99 @@
The SIGPIPE signal will still propagate to child sessions.
+TODO - what are ARG0, ARG1 ... in SIGPIPE
+
=head4 SIGWINCH
Window resizes can generate a large number of signals very quickly.
This may not be a problem when using perl 5.8.0 or later, but earlier
versions may not take kindly to such abuse. You have been warned.
+TODO - what are ARG0, ARG1 ... in SIGWINCH
+
+
=head3 Exception Handling
-TODO - Document exception handling.
+POE::Kernel provides only one form of exception handling: the
+C<DIE> signal.
-By the way, POE::Kernel's built-in exception handling can be disabled
+When exception handling is enabled (the default), POE::Kernel wraps state
+invocation in C<eval{}>. If the event handler raises an exception, generaly
+with C<die>, POE::Kernel will dispatch a C<DIE> signal to the event's
+destination session.
+
+C<ARG0> is the signal name, C<DIE>.
+
+C<ARG1> is a hashref describing the exception:
+
+=over
+
+=item error_str
+
+The text of the exception. In other words, C<$@>.
+
+=item dest_session
+
+Session object of the state that the raised the exception. In other words,
+C<$_[SESSION]> in the function that died.
+
+=item event
+
+Name of the event that died.
+
+=item source_session
+
+Session object that sent the original event.
+That is, C<$_[SENDER]> in the function that died.
+
+=item from_state
+
+State from which the orignal event was sent.
+That is, C<$_[CALLER_STATE]> in the function that died.
+
+=item file
+
+Name of the file the event was sent from.
+That is, C<$_[CALLER_FILE]> in the function that died.
+
+=item line
+
+Line number the event was sent from.
+That is, C<$_[CALLER_LINE]> in the function that died.
+
+=back
+
+I<Note that the preceeding discussion assumes you are using
+L<POE::Session|POE::Session>'s call semantics.>
+
+Note that the C<DIE> signal is sent to the session that raised the
+exception, not the session that sent the event that caused the exception to
+be raised.
+
+ sub _start {
+ $poe_kernel->sig( DIE => 'sig_DIE' );
+ $poe_kernel->yield( 'some_event' );
+ }
+
+ sub some_event {
+ die "I didn't like that!";
+ }
+
+ sub sig_DIE {
+ my( $sig, $ex ) = @_[ ARG0, ARG1 ];
+ # $sig is 'DIE'
+ # $ex is the exception hash
+ warn "$$: error in $event: $ex->{error_str}";
+ $poe_kernel->sig_handled();
+
+ # Send the signal to session that sent the original event.
+ if( $ex->{source_session} ne $_[SESSION] ) {
+ $poe_kernel->signal( $ex->{source_session}, 'DIE', $sig, $ex );
+
+ }
+ }
+
+POE::Kernel's built-in exception handling can be disabled
by setting the C<POE::Kernel::CATCH_EXCEPTIONS> constant to zero. As
with other compile-time configuration constants, it must be set before
POE::Kernel is compiled:
@@ -4108,15 +4367,15 @@
=head3 sig SIGNAL_NAME [, EVENT_NAME]
sig() registers or unregisters an EVENT_NAME event for a particular
-SIGNAL_NAME. The event is registered if EVENT_NAME is defined,
-otherwise the SIGNAL_NAME handler is unregistered. This does indded
-imply that a session can register only one handler per SIGNAL_NAME.
-Subsequent registration attempts will replace the old handler.
+SIGNAL_NAME. The event is registered if EVENT_NAME is defined, otherwise
+the SIGNAL_NAME handler is unregistered. This means that a session can
+register only one handler per SIGNAL_NAME; subsequent registration attempts
+will replace the old handler.
-SIGNAL_NAMEs are generally the same as members of %SIG, with two
-exceptions. First, "CLD" is an alias for "CHLD" (although see
-sig_child()). And second, it's possible to send and handle signals
-that have no basis in the operating system.
+SIGNAL_NAMEs are generally the same as members of C<%SIG>, with two
+exceptions. First, C<CLD> is an alias for C<CHLD> (although see
+L</sig_child>). And second, it's possible to send and handle signals
+created by the application and have no basis in the operating system.
sub handle_start {
$_[KERNEL]->sig( INT => "event_ui_shutdown" );
@@ -4125,7 +4384,8 @@
}
The operating system may never be able to generate the last two
-signals, but a POE session can by using POE::Kernel's signal() method.
+signals, but a POE session can by using POE::Kernel's
+L</signal>() method.
Later on the session may decide not to handle the signals:
@@ -4141,7 +4401,7 @@
sig() does not return a meaningful value.
-=head3 sig_child PROCESS_ID [, EVENT_NAME [, ARGS_LIST] ]
+=head3 sig_child PROCESS_ID [, EVENT_NAME]
sig_child() is a convenient way to deliver an EVENT_NAME event with an
optional ARGS_LIST when a particular PROCESS_ID has exited. The
@@ -4159,23 +4419,42 @@
sig_chid() does not return a meaningful value.
-TODO - Example
+ sub forked_parent {
+ my( $heap, $pid, $details ) = @_[ HEAP, ARG0, ARG1 ];
+ $heap->{$pid} = $details;
+ $poe_kernel->sig_child( $pid, 'sig_child' );
+ }
+
+ sub sig_child {
+ my( $heap, $sig, $pid, $exit_val ) = @_[ HEAP, ARG0, ARG1, ARG2 ];
+ my $details = delete $heap->{ $pid };
+ warn "$$: Child $pid exited"
+ # ....
+ }
+
=head3 sig_handled
-sig_handled() informs the POE::Kernel instance that the currently
-dispatched signal has been handled by the currently active session.
-If the signal is terminal, the sig_handled() call prevents POE::Kernel
-from stopping the sessions that received the signal.
+sig_handled() informs POE::Kernel that the currently dispatched signal has
+been handled by the currently active session. If the signal is terminal, the
+sig_handled() call prevents POE::Kernel from stopping the sessions that
+received the signal.
A single signal may be dispatched to several sessions. Only one needs
to call sig_handled() to prevent the entire group from being stopped.
If none of them call it, however, then they are all stopped together.
-TODO - Example
-
sig_handled() does not return a meaningful value.
+ sub _start {
+ $_[KERNEL]->sig( INT => 'sig_INT' );
+ }
+
+ sub sig_INT {
+ warn "$$ SIGINT";
+ $_[KERNEL]->sig_handled();
+ }
+
=head3 signal SESSION, SIGNAL_NAME [, ARGS_LIST]
signal() posts a SIGNAL_NAME signal to a specific SESSION with an
@@ -4184,7 +4463,7 @@
children, grandchildren, and so on. And if SESSION is the POE::Kernel
itself, then all interested sessions will receive the signal.
-It is possible to send a signal() in POE that doesn't exist in the
+It is possible to send a signal in POE that doesn't exist in the
operating system. signal() places the signal directly into POE's
event queue as if they came from the operating system, but they are
not limited to signals recognized by kill(). POE uses a few of these
@@ -4200,8 +4479,22 @@
signal() returns true on success. On failure, it returns false after
setting $! to explain the nature of the failure:
-ESRCH ("No such process"). The SESSION does not exist.
+=over
+=item ESRCH ("No such process")
+
+The SESSION does not exist.
+
+=back
+
+
+Because all sessions are a child of POE::Kernel, sending a signal to
+the kernel will propagate the signal to all sessions. This is a cheap
+form of I<mutlicast>.
+
+ $_[KERNEL]->signal( $_[KERNEL], 'shutdown' );
+
+
=head3 signal_ui_destroy WIDGET_OBJECT
signal_ui_destroy() associates the destruction of a particular
@@ -4217,27 +4510,25 @@
$_[KERNEL]->signal_ui_destroy( $_[HEAP]{main_widget} );
}
+Detecting widget destruction is specific to each toolkit.
+
=head3 TODO
TODO - See if there is anything to migrate over from POE::Session?
-=head2 Event Handler (State) Management
+=head2 Event Handler Management
-The term "state" is often used in place of "event handler", especially
-when treating sessions as event driven state machines.
+Event handler management methods let sessions hot swap their event handlers
+at runtime. For example, the L<POE::Wheel|POE::Wheel> objects use state() to
+dynamically mix their own event handlers into the sessions that create them.
-State management methods let sessions hot swap their event handlers at
-runtime.
+These methods only affect the current session; it would be rude to change
+another session's handlers.
-It would be rude to change another session's handlers, so these
-methods only affect the current one.
-
There is only one method in this group. Since it may be called in
several different ways, it may be easier to understand if each is
documented separately.
-The POE::Wheel objects use state() to dynamically mix their own event
-handlers into the sessions that create them.
=head3 state EVENT_NAME [, CODE_REFERNCE]
@@ -4271,7 +4562,8 @@
Subsequent attempts to set an EVENT_NAME handler will replace earlier
handlers with the same name.
-TODO - Example.
+ $_[KERNEL]->state( 'some_event', $self );
+ $_[KERNEL]->state( 'other_event', $self, 'other_method' );
=head3 state EVENT_NAME [, CLASS_NAME [, CLASS_METHOD_NAME] ]
@@ -4288,7 +4580,8 @@
Subsequent attempts to set an EVENT_NAME handler will replace earlier
handlers with the same name.
-TODO - Example.
+ $_[KERNEL]->state( 'some_event', __PACKAGE__ );
+ $_[KERNEL]->state( 'other_event', __PACKAGE__, 'other_method' );
=head2 Public Reference Counters
@@ -4305,15 +4598,17 @@
Reference counting is a big part of POE's magic. Various objects
(mainly event watchers and components) hold references to the sessions
-that own them. L<Session Lifespans> explains the concept in more
+that own them. L</Session Lifespans> explains the concept in more
detail.
-The ability to keep a session alive is sometimes useful in an
-application or library. For example, a component may hold a reference
-to another session while it processes a request from that session. In
-doing so, the component guarantees that the requester is still around
-when a response is eventually ready.
+The ability to keep a session alive is sometimes useful in an application or
+library. For example, a component may hold a public reference to another
+session while it processes a request from that session. In doing so, the
+component guarantees that the requester is still around when a response is
+eventually ready. Keeping a reference to the session's object is not
+enough. POE::Kernel has its own internal reference counting mechanism.
+
=head3 refcount_increment SESSION_ID, COUNTER_NAME
refcount_increment() increases the value of the COUNTER_NAME reference
@@ -4336,7 +4631,7 @@
For this to work, the session needs a way to remember the
$_[SENDER]->ID for a given request. Customarily the session generates
-a request ID and uses that to track the request until it is fulfilled
+a request ID and uses that to track the request until it is fulfilled.
refcount_increment() returns true on success or false on failure.
Furthermore, $! is set on failure to one of:
@@ -4366,6 +4661,9 @@
ESRCH: The SESSION_ID does not refer to a currently active session.
+It is not possible to discover currently active public references. See
+L<POE::API::Peek>.
+
=head2 Kernel State Accessors
POE::Kernel provides a few accessors into its massive brain so that
@@ -4380,43 +4678,52 @@
get_active_session() returns a reference to the session that is
currently running, or a reference to the program's POE::Kernel
instance if no session is running at that moment. The value is
-equivalent to $_[SESSION].
+equivalent to L<POE::Session|POE::Session>'s C<$_[SESSION]>.
-This method was added for libraries that need $_[SESSION] but don't
+This method was added for libraries that need C<$_[SESSION]> but don't
want to include it as a parameter in their APIs.
-TODO - Example.
+ sub some_housekeeping {
+ my( $self ) = @_;
+ my $session = $poe_kernel->get_active_session;
+ # do some housekeeping on $session
+ }
=head3 get_active_event
get_active_event() returns the name of the event currently being
dispatched. It returns an empty string when called outside event
-dispatch. The value is equivalent to $_[STATE].
+dispatch. The value is equivalent to L<POE::Session|POE::Session>'s C<$_[STATE]>.
-TODO - Example.
+ sub waypoint {
+ my( $message ) = @_;
+ my $event = $poe_kernel->get_active_event;
+ print STDERR "$$:$event:$mesage\n";
+ }
+
=head3 get_event_count
get_event_count() returns the number of events pending in POE's event
-queue. It is exposed for POE::Loop class authors. It may be
+queue. It is exposed for L<POE::Loop|POE::Loop> class authors. It may be
deprecated in the future.
=head3 get_next_event_time
get_next_event_time() returns the time the next event is due, in a
form compatible with the UNIX time() function. It is exposed for
-POE::Loop class authors. It may be deprecated in the future.
+L<POE::Loop|POE::Loop> class authors. It may be deprecated in the future.
=head2 Session Helper Methods
-The methods in this group expose features for POE::Session class
-authors.
+The methods in this group expose features for L<POE::Session|POE::Session>
+class authors.
=head3 session_alloc SESSION_OBJECT [, START_ARGS]
session_alloc() allocates a session context within POE::Kernel for a
newly created SESSION_OBJECT. A list of optional START_ARGS will be
-passed to the session as part of the _start event.
+passed to the session as part of the L</_start> event.
The SESSION_OBJECT is expected to follow a subset of POE::Session's
interface.
@@ -4445,9 +4752,9 @@
=head2 $poe_kernel
-$poe_kernel contains a reference to the process' POE::Kernel instance.
-It's mainly used for accessing POE::Kernel methods from places where
-$_[KERNEL] is not available. It's most commonly used in helper
+C<$poe_kernel> contains a reference to the process' POE::Kernel singleton
+instance. It's mainly used for accessing POE::Kernel methods from places
+where C<$_[KERNEL]> is not available. It's most commonly used in helper
libraries.
=head2 $poe_main_window
@@ -4456,15 +4763,15 @@
one widget to be created before their event loops are usable. This is
currently only Tk.
-POE::Loop::Tk creates a main window to satisfy Tk's event loop. The
+L<POE::Loop::Tk|POE::Loop::Tk> creates a main window to satisfy Tk's event loop. The
window is given to the application since POE has no other use for it.
-$poe_main_window is undefined in toolkits that don't require a widget
+C<$poe_main_window> is undefined in toolkits that don't require a widget
to dispatch events.
On a related note, POE will shut down if the widget in
-$poe_main_window is destroyed. This can be changed with POE::Kernel's
-signal_ui_destroy() methode
+C<$poe_main_window> is destroyed. This can be changed with POE::Kernel's
+C</signal_ui_destroy>() methode
=head1 DEBUGGING POE AND PROGRAMS USING IT
@@ -4478,7 +4785,7 @@
prefixed with a four-character field describing the POE subsustem that
generated it.
-Assertions (asserts) are silent but deadly, both in performance (they
+Assertions (asserts) are quiet but deadly, both in performance (they
cause a significant runtime performance hit) and because they cause
fatal errors when triggered.
@@ -4487,7 +4794,7 @@
Each assertion and tracing group is enabled by setting a constant in
the POE::Kernel namespace to a true value. This is the same mechanism
-documented under L<Using Time::HiRes>, namely:
+documented under L</"Using Time::HiRes">, namely:
BEGIN {
package POE::Kernel;
@@ -4500,7 +4807,7 @@
sub POE::Kernel::ASSERT_DEFAULT () { 1 }
use POE;
-As mentioned in L<Using Time::HiRes>, the switches must be defined as
+As mentioned in L</"Using Time::HiRes">, the switches must be defined as
constants before POE::Kernel is first loaded. Otherwise Perl's
compiler will not see the constants when first compiling POE::Kernel,
and the features will not be properly enabled.
@@ -4587,7 +4894,7 @@
=head2 TRACE_DEFAULT
TRACE_DEFAULT specifies the default value for traces that are not
-explicitly enabled or disabled. This is a quick and reliable wat to
+explicitly enabled or disabled. This is a quick and reliable way to
ensure your program generates copious output on the file named in
TRACE_FILENAME or STDERR by default.
@@ -4604,7 +4911,7 @@
=head2 TRACE_DESTROY
TRACE_DESTROY causes every POE::Session object to dump the contents of
-its $_[HEAP] when Perl destroys it. This trace was added to help
+its C<$_[HEAP]> when Perl destroys it. This trace was added to help
developers find memory leaks in their programs.
Prefix: A line that reads "----- Session $self Leak Check -----".
@@ -4614,7 +4921,7 @@
=head2 TRACE_EVENTS
TRACE_EVENTS enables messages pertaining to POE's event queue's
-activities: when events are enquered, dispatched or discarded, and
+activities: when events are enqueued, dispatched or discarded, and
more. It's great for determining where events go and when.
Understandably this is one of POE's more verbose traces.
@@ -4626,7 +4933,7 @@
TRACE_FILENAME specifies the name of a file where POE's tracing and
assertion messages should go. It's useful if you want the messages
-but have other plans for STDERR, which is where the messages go ny
+but have other plans for STDERR, which is where the messages go by
default.
POE's tests use this so the trace and assertion code can be
@@ -4638,8 +4945,8 @@
=head2 TRACE_FILES
-TRACE_FILES enables or disables traces in POE's filehanle watchers and
-the POE::Loop class that implements the lowest-level filehandle
+TRACE_FILES enables or disables traces in POE's filehandle watchers and
+the L<POE::Loop|POE::Loop> class that implements the lowest-level filehandle
multiplexing. This may be useful when tracking down strange behavior
related to filehandles.
@@ -4655,7 +4962,7 @@
its dispatch count.
When TRACE_PROFILE is enabled, a program may call
-$_[KERNEL]->stat_show_profile() to display a current dispatch profile
+C<< $_[KERNEL]->stat_show_profile() >> to display a current dispatch profile
snapshot.
See TRACE_STATISTICS for more profiling.
@@ -4667,7 +4974,7 @@
=head2 TRACE_REFCNT
TRACE_REFCNT governs whether POE::Kernel will trace sessions'
-reference counts. As discussed in L<Session Lifespans>, POE does a
+reference counts. As discussed in L</"Session Lifespans">, POE does a
lot of reference counting, and the current state of a session's
reference counts determines whether the session lives or dies. It's
common for developers to wonder why a session stops too early or
@@ -4680,7 +4987,7 @@
=head2 TRACE_RETVALS
TRACE_RETVALS can enable carping whenever a POE::Kernel method is
-about to fail. It's a kinder, gentler, and noisier form of
+about to fail. It's a non-fatal but noisier form of
ASSERT_RETVALS.
Prefix: <rv>
@@ -4714,7 +5021,7 @@
TRACE_STATISTICS enables runtime gathering and reporting of various
performance metrics within a POE program. Some statistics include how
-much time is spent processing event callbacks, time spent in POE's
+much time is spent processing event handlers, time spent in POE's
dispatcher, and the time spent waiting for an event. A report is
displayed just before run() returns, and the data can be retrieved at
any time using stat_getdata().
@@ -4730,11 +5037,23 @@
=head1 BUGS
+=over
+
+=item *
+
There is no mechanism in place to prevent external reference count
names from clashing.
+=item *
+
+There is no mechanism to catch exceptions generated in another session.
+
+=item *
+
Probably lots more.
+=back
+
=head1 AUTHORS & COPYRIGHTS
Please see L<POE> for more information about authors and contributors.
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|