From 9573d8c607b647e1a43f293030f94e5a36e34342 Mon Sep 17 00:00:00 2001 From: Rocco Caputo Date: Mon, 22 May 2000 18:56:35 -0400 Subject: [PATCH] Initial Event support; new Tk test; more manpage revisions --- Changes | 24 ++- MANIFEST | 1 + lib/POE.pm | 2 +- lib/POE/Kernel.pm | 366 +++++++++++++++++++++++++++++++++++++++------ lib/POE/Session.pm | 114 +++++++++++++- lib/POE/Wheel.pm | 109 +++++++------- tests/06_tk.t | 35 +++++ 7 files changed, 535 insertions(+), 116 deletions(-) create mode 100644 tests/06_tk.t diff --git a/Changes b/Changes index 0ab890597..b3fffb34c 100644 --- a/Changes +++ b/Changes @@ -16,18 +16,34 @@ make dist on it! 0.0910 is ``v0_0910''! For example: ,----- To Do ----- | | Create a test program for fork and sigchld. -| Still can't get Event to compile on 5.6.0+OS/2 or 5.005+61+FreeBSD; -| be sure to mark Event experimental in the new documentation. -| Make a test for Tk. +| Turn SocketFactory's sample program into a test case | Make a test for Event, when it can be made to work. | Split the samples out into a separate distribution. | `----------------- + +0.1005 2000.??.?? +----------------- + +I forgot to document POE::Session's constructors. Big oops there. +Now they are, sort of. + +Rewrote the POE::Wheel POD. + +Found the Perl symbols that weren't being exported for OS/2 and AIX +(and maybe Windows). Fixed makedef.pl to export them, and now Event +builds and tests ok. Sweet! Added initial Event support to +POE::Kernel and removed the Event caveats throughout POE's +documentation. This is experimental until a proper test can be made. + +Added t/06_tk.t to test Tk support. + + 0.1004 2000.05.21 ----------------- -Rewrote the POE, POE::Kernel and POE::Session manpages. Documented Tk +Rewrote the POE, POE::Kernel and POE::Session PODs. Documented Tk support. Documented Event support, but it's not in yet. t/04_select.t assumed that two-argument syswrite was legal, but it diff --git a/MANIFEST b/MANIFEST index add0a2e0d..7c7d27458 100644 --- a/MANIFEST +++ b/MANIFEST @@ -62,3 +62,4 @@ t/02_alarms.t t/03_aliases.t t/04_selects.t t/05_macros.t +t/06_tk.t diff --git a/lib/POE.pm b/lib/POE.pm index ab504ec40..0846b3081 100644 --- a/lib/POE.pm +++ b/lib/POE.pm @@ -903,6 +903,7 @@ progress report: README rewritten 2000.05.16 POE::Kernel rewritten 2000.05.19 POE::Session rewritten 2000.05.21 + POE::Wheel rewritten 2000.05.22 POE::Component queued POE::Component::Server::TCP queued @@ -914,7 +915,6 @@ progress report: POE::Filter::Reference queued POE::Filter::Stream queued POE::Preprocessor queued - POE::Wheel queued POE::Wheel::FollowTail queued POE::Wheel::ListenAccept queued POE::Wheel::ReadWrite queued diff --git a/lib/POE/Kernel.pm b/lib/POE/Kernel.pm index d039ffcd4..5136c6282 100644 --- a/lib/POE/Kernel.pm +++ b/lib/POE/Kernel.pm @@ -229,15 +229,19 @@ BEGIN { {% define_assert SESSIONS %} } -# Determine whether Tk is loaded. If it is, set a constant that -# enables Tk behaviors throughout POE::Kernel. If Tk isn't present, -# then the support code won't run, but it still needs to compile. In -# this case, we define a series of dummy constant functions that -# replace the missing Tk calls. +# Determine whether Tk or Event is loaded. If either is, set a +# constant that enables its specific behaviors throughout POE::Kernel. +# Replace the unused ones' methods with dummies; these won't ever be +# called, but they need to be present so that POE::Kernel compiles. BEGIN { + # Can't use Tk and Event at the same time. + if (exists $INC{'Tk.pm'} and exists $INC{'Event.pm'}) { + croak "POE: Tk and Event have incompatible event loops. Can't use both"; + } + + # Check for Tk. if (exists $INC{'Tk.pm'}) { - warn "POE: Tk version $Tk::VERSION is in use! Let's rock!\n"; eval <<' EOE'; sub POE_HAS_TK () { 1 } EOE @@ -249,6 +253,21 @@ BEGIN { sub Tk::MainWindow::new () { undef } EOE } + + # Check for Event. + if (exists $INC{'Event.pm'}) { + eval <<' EOE'; + sub POE_HAS_EVENT () { 1 } + EOE + } + else { + eval <<' EOE'; + sub POE_HAS_EVENT () { 0 } + sub Event::loop () { 0 } + sub Event::idle () { 0 } + sub Event::timer () { 0 } + EOE + } } #------------------------------------------------------------------------------ @@ -266,10 +285,10 @@ enum SH_HANDLE SH_REFCOUNT SH_VECCOUNT # The Kernel object. KR_SIZE goes last (it's the index count). enum KR_SESSIONS KR_VECTORS KR_HANDLES KR_STATES KR_SIGNALS KR_ALIASES enum + KR_ACTIVE_SESSION KR_PROCESSES KR_ALARMS KR_ID KR_SESSION_IDS -enum + KR_ID_INDEX KR_TK_TIMED KR_TK_IDLE KR_SIZE +enum + KR_ID_INDEX KR_WATCHER_TIMER KR_WATCHER_IDLE KR_SIZE # Handle structure. -enum HND_HANDLE HND_REFCOUNT HND_VECCOUNT HND_SESSIONS HND_FILENO +enum HND_HANDLE HND_REFCOUNT HND_VECCOUNT HND_SESSIONS HND_FILENO HND_WATCHERS # Handle session structure. enum HSS_HANDLE HSS_SESSION HSS_STATE @@ -347,7 +366,9 @@ const FIFO_DISPATCH_TIME 0.01 # [ { $session => [ $handle, $session, $state ], .. }, # { $session => [ $handle, $session, $state ], .. }, # { $session => [ $handle, $session, $state ], .. } -# ] +# ], +# fileno(), +# [ $watcher_r, $watcher_w, $watcher_x ], # ] # }; # @@ -409,6 +430,16 @@ sub _signal_handler_generic { } } +# This is Event's generic signal handler. +sub _event_signal_handler_generic { + my $event = shift; + $poe_kernel->_enqueue_state( $poe_kernel, $poe_kernel, + EN_SIGNAL, ET_SIGNAL, + [ $event->watcher->signas ], + time(), __FILE__, __LINE__ + ); +} + # SIGPIPE is handled a little differently. It tends to be # synchronous, so it's posted at the current active session. We can # do this better by generating a pseudo SIGPIPE whenever a driver @@ -429,6 +460,18 @@ sub _signal_handler_pipe { } } +# This is Event's pipe handler. It's probably not valid, since Event +# delays signals even longer than operating systems do. Pipe signals +# should be depreciated in favor of EPIPE anyway. +sub _event_signal_handler_pipe { + my $event = shift; + $poe_kernel->_enqueue_state( $poe_kernel->[KR_ACTIVE_SESSION], $poe_kernel, + EN_SIGNAL, ET_SIGNAL, + [ $event->watcher->signas ], + time(), __FILE__, __LINE__ + ); +} + # SIGCH?LD are normalized to SIGCHLD and include the child process' # PID and return code. @@ -458,6 +501,28 @@ sub _signal_handler_child { } } +# Event's SIGCH?LD handler. +sub _event_signal_handler_child { + my $event = shift; + + # Reap until there are no more children. + for (my $reap=0; $reap < $event->count; $reap++) { + my $pid = wait; + last if $pid < 0; + + # Determine if the child process is really exiting and not just + # stopping for some other reason. This is per Perl Cookbook + # recipe 16.19. + if (WIFEXITED($?)) { + $poe_kernel->_enqueue_state( $poe_kernel, $poe_kernel, + EN_SIGNAL, ET_SIGNAL, + [ 'CHLD', $pid, $? ], + time(), __FILE__, __LINE__ + ); + } + } +} + #------------------------------------------------------------------------------ # Register or remove signals. @@ -535,8 +600,8 @@ sub new { undef, # KR_ID { }, # KR_SESSION_IDS 1, # KR_ID_INDEX - undef, # KR_TK_TIMED - undef, # KR_TK_IDLE + undef, # KR_WATCHER_TIMER + undef, # KR_WATCHER_IDLE ], $type; @@ -575,8 +640,22 @@ sub new { # solution. At some point, POE will include a set of Curses # widgets, and SIGWINCH will be needed... if ($signal eq 'WINCH') { - $SIG{$signal} = 'IGNORE'; - next; + + # Event polls signals in some XS, which means they ought not + # kill Perl. Use an Event->signal watcher if Event is + # available. + + if (POE_HAS_EVENT) { + Event->signal( signal => $signal, + cb => \&_event_signal_handler_generic + ); + } + + # Otherwise ignore WINCH. + else { + $SIG{$signal} = 'IGNORE'; + next; + } } # Windows doesn't have a SIGBUS, but the debugger causes SIGBUS @@ -591,14 +670,48 @@ sub new { # Leave SIGCHLD alone if running under apache. unless (exists $INC{'Apache.pm'}) { - $SIG{$signal} = \&_signal_handler_child; + + # Register an Event signal watcher on it. Rename the signal + # 'CHLD' regardless whether it's CHLD or CLD. + if (POE_HAS_EVENT) { + Event->signal( signal => $signal, + cb => \&_event_signal_handler_child + ); + } + + # Otherwise register a regular Perl signal handler. + else { + $SIG{$signal} = \&_signal_handler_child; + } } } elsif ($signal eq 'PIPE') { - $SIG{$signal} = \&_signal_handler_pipe; + + # Register an Event signal watcher. + if (POE_HAS_EVENT) { + Event->signal( signal => $signal, + cb => \&_event_signal_handler_pipe + ); + } + + # Otherwise register a plain Perl signal handler. + else { + $SIG{$signal} = \&_signal_handler_pipe; + } } else { - $SIG{$signal} = \&_signal_handler_generic; + + # If Event is available, register a signal watcher with it. + if (POE_HAS_EVENT) { + Event->signal( signal => $signal, + cb => \&_event_signal_handler_generic + ); + } + + # Otherwise register a plain signal handler. + else { + $SIG{$signal} = \&_signal_handler_generic; + } } $self->[KR_SIGNALS]->{$signal} = { }; @@ -998,7 +1111,7 @@ sub _dispatch_state { } #------------------------------------------------------------------------------ -# POE's main loop! Now with Tk support! +# POE's main loop! Now with Tk and Event support! sub run { my $self = shift; @@ -1009,6 +1122,12 @@ sub run { eval 'Tk::MainLoop'; } + # Use Event's main loop, if Event is loaded. + + if (POE_HAS_EVENT) { + eval 'Event::loop()'; + } + # Otherwise use POE's main loop. else { @@ -1279,6 +1398,9 @@ sub run { } } +#------------------------------------------------------------------------------ +# Tk support. + # Tk idle callback to dispatch FIFO states. This steals a big chunk # of code from POE::Kernel::run(). Make this function's guts a macro # later, and use it in both places. @@ -1303,9 +1425,9 @@ sub tk_fifo_callback { # Perpetuate the dispatch loop as long as there are states enqueued. - if (defined $self->[KR_TK_IDLE]) { - $self->[KR_TK_IDLE]->cancel(); - $self->[KR_TK_IDLE] = undef; + if (defined $self->[KR_WATCHER_IDLE]) { + $self->[KR_WATCHER_IDLE]->cancel(); + $self->[KR_WATCHER_IDLE] = undef; } # This nasty little hack is required because setting an afterIdle @@ -1318,9 +1440,9 @@ sub tk_fifo_callback { $poe_tk_main_window->after ( 0, sub { - $self->[KR_TK_IDLE] = + $self->[KR_WATCHER_IDLE] = $poe_tk_main_window->afterIdle( \&tk_fifo_callback ) - unless defined $self->[KR_TK_IDLE]; + unless defined $self->[KR_WATCHER_IDLE]; } ); } @@ -1356,15 +1478,15 @@ sub tk_alarm_callback { if (@{$self->[KR_ALARMS]}) { - if (defined $self->[KR_TK_TIMED]) { - $self->[KR_TK_TIMED]->cancel(); - $self->[KR_TK_TIMED] = undef; + if (defined $self->[KR_WATCHER_TIMER]) { + $self->[KR_WATCHER_TIMER]->cancel(); + $self->[KR_WATCHER_TIMER] = undef; } my $next_time = $self->[KR_ALARMS]->[0]->[ST_TIME] - time(); $next_time = 0 if $next_time < 0; - $self->[KR_TK_TIMED] = + $self->[KR_WATCHER_TIMER] = $poe_tk_main_window->after( $next_time * 1000, \&tk_alarm_callback ); @@ -1392,6 +1514,106 @@ sub tk_select_callback { } } +#------------------------------------------------------------------------------ +# Event support. + +# Event idle callback to dispatch FIFO states. This steals a big +# chunk of code from POE::Kernel::run(). Make this functions guts a +# macro later, and use it here, in POE::Kernel::run() and other FIFO +# callbacks. + +sub event_fifo_callback { + my $self = $poe_kernel; + + if ( @{ $self->[KR_STATES] } ) { + + # Pull an event off the queue. + + my $event = shift @{ $self->[KR_STATES] }; + {% ses_refcount_dec2 $event->[ST_SESSION], SS_EVCOUNT %} + + # Dispatch it, and see if that was the last thing the session + # needed to do. + + $self->_dispatch_state(@$event); + {% collect_garbage $event->[ST_SESSION] %} + + } + + # Stop the idle watcher if there are no more state transitions in + # the Kernel's FIFO. + + unless (@{$self->[KR_STATES]}) { + $self->[KR_WATCHER_IDLE]->stop(); + } +} + +# Event timer callback to dispatch alarm states. Same caveats about +# macro-izing this code. + +sub event_alarm_callback { + my $self = $poe_kernel; + + # Dispatch whatever alarms are due. + + my $now = time(); + while ( @{ $self->[KR_ALARMS] } and + ($self->[KR_ALARMS]->[0]->[ST_TIME] <= $now) + ) { + + # Pull an alarm off the queue. + + my $event = shift @{ $self->[KR_ALARMS] }; + {% ses_refcount_dec2 $event->[ST_SESSION], SS_ALCOUNT %} + + # Dispatch it, and see if that was the last thing the session + # needed to do. + + $self->_dispatch_state(@$event); + {% collect_garbage $event->[ST_SESSION] %} + + } + + # Register the next timed callback if there are alarms left. + + if (@{$self->[KR_ALARMS]}) { + $self->[KR_WATCHER_TIMER]->at( $self->[KR_ALARMS]->[0]->[ST_TIME] ); + } +} + +# Event filehandle callback to dispatch selects. + +sub event_select_callback { + my $self = $poe_kernel; + + my $event = shift; + my $watcher = $event->w; + my $handle = $watcher->fd; + my $vector = ( ( $event->got eq 'r' ) + ? VEC_RD + : ( ( $event->got eq 'w' ) + ? VEC_WR + : ( ( $event->got eq 'e' ) + ? VEC_EX + : return + ) + ) + ); + + my @selects = + values %{ $self->[KR_HANDLES]->{$handle}->[HND_SESSIONS]->[$vector] }; + + foreach my $select (@selects) { + $self->_dispatch_state + ( $select->[HSS_SESSION], $select->[HSS_SESSION], + $select->[HSS_STATE], ET_SELECT, + [ $select->[HSS_HANDLE] ], + time(), __FILE__, __LINE__, undef + ); + {% collect_garbage $select->[HSS_SESSION] %} + } +} + #------------------------------------------------------------------------------ sub DESTROY { @@ -1607,10 +1829,17 @@ sub _enqueue_state { # register a Tk idle callback to begin the dispatch loop. if ( POE_HAS_TK ) { - $self->[KR_TK_IDLE] = + $self->[KR_WATCHER_IDLE] = $poe_tk_main_window->afterIdle( \&tk_fifo_callback ); } + # If using Event and the FIFO queue now has only one event, then + # start the Event idle watcher to begin the dispatch loop. + + if ( POE_HAS_TK ) { + $self->[KR_WATCHER_IDLE]->start(); + } + } else { warn ">>>>> ", join('; ', keys(%{$self->[KR_SESSIONS]})), " <<<<<\n"; @@ -1717,16 +1946,23 @@ sub _enqueue_alarm { # register a Tk timed callback to dispatch it when it becomes due. if ( POE_HAS_TK and @{$self->[KR_ALARMS]} == 1 ) { - if (defined $self->[KR_TK_TIMED]) { - $self->[KR_TK_TIMED]->cancel(); - $self->[KR_TK_TIMED] = undef; + if (defined $self->[KR_WATCHER_TIMER]) { + $self->[KR_WATCHER_TIMER]->cancel(); + $self->[KR_WATCHER_TIMER] = undef; } my $next_time = $self->[KR_ALARMS]->[0]->[ST_TIME] - time(); $next_time = 0 if $next_time < 0; - $self->[KR_TK_TIMED] = $poe_tk_main_window->after( $next_time * 1000, - \&tk_alarm_callback - ); + $self->[KR_WATCHER_TIMER] = + $poe_tk_main_window->after( $next_time * 1000, + \&tk_alarm_callback + ); + } + + # If using Event and the alarm queue now has only one event, then + # start the Event timer to dispatch it when it becomes due. + if ( POE_HAS_EVENT and @{$self->[KR_ALARMS]} == 1 ) { + $self->[KR_WATCHER_TIMER]->at( $self->[KR_ALARMS]->[0]->[ST_TIME] ); } # Manage reference counts. @@ -1859,6 +2095,12 @@ sub alarm { # -><- Remove the idle handler. } + # If using Event and the alarm queue is empty, then ensure that the + # timer has stopped. + if (POE_HAS_EVENT and @{$self->[KR_ALARMS]} == 0) { + $self->[KR_WATCHER_TIMER]->stop(); + } + # Add the new alarm if it includes a time. if (defined $time) { $self->_enqueue_alarm( $kr_active_session, $kr_active_session, @@ -2006,6 +2248,25 @@ sub _internal_select { [ \&tk_select_callback, $handle, $select_index ], ); } + + # If we're using Event, then we tell it to watch this + # filehandle for us. This is in lieu of our own select code. + + if (POE_HAS_EVENT) { + + $kr_handle->[HND_WATCHERS]->[$select_index] = + Event->io + ( fd => $handle, + poll => ( ( $select_index == VEC_RD ) + ? 'r' + : ( ( $select_index == VEC_WR ) + ? 'w' + : 'e' + ) + ), + cb => \&event_select_callback, + ); + } } # Increment the handle's overall reference count (which is the @@ -2100,6 +2361,15 @@ sub _internal_select { ); } + # If we're using Event, then we tell it to stop watching + # this filehandle for us. This is in lieu of our own select + # code. + + if (POE_HAS_EVENT) { + $kr_handle->[HND_WATCHERS]->[$select_index]->cancel(); + $kr_handle->[HND_WATCHERS]->[$select_index] = undef; + } + # Shrink the bit vector by chopping zero octets from the # end. Octets because that's the minimum size of a bit # vector chunk that Perl manages. Always keep at least one @@ -2208,6 +2478,10 @@ sub select_pause_write { ); } + if (POE_HAS_EVENT) { + $self->[KR_HANDLES]->{$handle}->[HND_WATCHERS]->[VEC_WR]->stop(); + } + return 0; } @@ -2232,6 +2506,10 @@ sub select_resume_write { ); } + if (POE_HAS_EVENT) { + $self->[KR_HANDLES]->{$handle}->[HND_WATCHERS]->[VEC_WR]->start(); + } + return 1; } @@ -2320,8 +2598,7 @@ sub ID_session_to_id { #============================================================================== # Extra reference counts, to keep sessions alive when things occur. # They take session IDs because they may be called from resources at -# times where the session reference is otherwise unknown. This is -# experimental until the Tk support is definitely working. +# times where the session reference is otherwise unknown. #============================================================================== sub refcount_increment { @@ -2514,8 +2791,7 @@ To have POE encapsulate Tk's event loop: use Tk; use POE; -To have POE encapsulate Event's event loop (not yet implemented, since -Event refuses to compile): +To have POE encapsulate Event's event loop: use Event; use POE; @@ -2661,12 +2937,6 @@ Exported symbols: =head1 DESCRIPTION -The "Event" module documentation is incorrect. I am having trouble -building Event on FreeBSD or OS/2, so the Event support is only in the -planning phase. The rest of the code should quickly fall into place -once Event is working. This time documentation is ahead of -development! :) - POE::Kernel is an event dispatcher and resource watcher. It provides a consistent interface to the most common event loop features whether the underlying architecture is its own, Perl/Tk's, or Event's. Other @@ -2684,7 +2954,8 @@ interact with users through a graphical front end; and Event's loop, which is written in C for maximum performance. POE::Kernel uses its own loop by default, but it will adapt to -whichever external event loop is loaded before it: +whichever external event loop is loaded before it. POE's functions +work the same regardless of the underlying event loop. # Use POE's select loop. use POE::Kernel; @@ -2697,6 +2968,9 @@ whichever external event loop is loaded before it: use Event; use POE::Kernel; +Please read about POE::Session's postback() method if you'd like Tk's +widgets or Event's watchers to fire POE events at your sessions. + It also is possible to enable assertions and debugging traces by defining the constants that enable them before POE::Kernel does. Every definition follows the form: @@ -3372,8 +3646,8 @@ spontaneously stop even if they are hold signal name maps. In other words, signal name maps B keep sessions alive. POE does not make Perl's signal handling safe by itself. The Event -module, however, does implement safe signals, and POE can take -advantage of this. +module, however, does implement safe signals, and POE will take +advantage of them when they're available. Most signals propagate depth first through the sessions' parent/child relationships. That is, they are delivered to grandchildren, then diff --git a/lib/POE/Session.pm b/lib/POE/Session.pm index 64dabe51a..5f03a4a00 100644 --- a/lib/POE/Session.pm +++ b/lib/POE/Session.pm @@ -1256,13 +1256,112 @@ IDs may collide after at 4.29 billion sessions have been created. =item create LOTS_OF_STUFF -POE::Session's create() constructor is preferred over the older new() -constructor. +Bundles some states together into a single machine, then starts it +running. + +LOTS_OF_STUFF looks like a hash of parameter name/value pairs, but +it's really just a list. It's preferred over the older, more DWIMmy +new() constructor because each kind of parameter is explicitly named, +and it can therefore unambiguously figure out what it is a program is +trying to do. + +=over 2 + +=item args => LISTREF + +Defines the arguments to give to the machine's _start state. They're +passed in as @_[ARG0..$#_]. + + args => [ 'arg0', 'arg1', 'etc.' ], + +=item inline_states => HASHREF + +Defines inline coderefs that make up some or all of the session's +states. + + inline_states => + { _start => sub { print "arg0=$_[ARG0], arg1=$_[ARG1], etc.=$_[ARG2]\n"; } + _stop => \&stop_state + }, + +=item object_states => LISTREF + +Defines object methods that make up some or all of the session's +states. + +LISTREF is a list of parameter pairs. The first member of each pair +is an object reference. The second member is either a list reference +or hash reference. When it's a list reference, the referenced list +contains methods from the referenced object. The methods define +states with the same names. When it's a hash reference, the +referenced hash contains state/method pairs which map state names to +methods that may have different names. + +Perhaps some examples are in order! This one defines two states, +state_one and state_two, which are implemented as $object->state_one() +and $object->state_two(). + + object_states => + [ $object => [ 'state_one', 'state_two' ], + ], + +This second example defines two other states, state_five and +state_six, which are implemented as $object->do_five() and +$object->do_six(). + + object_states => + [ $object => { state_five => 'do_five', + state_six => 'do_six', + }, + ], + +It's a lot simpler to do than to describe. + +=item options => HASHREF + +Sets one or more initial session options before starting it. Please +see the POE::Session option() method for a list of available session +options and what they do. + + option => { trace => 1, debug => 1 }, + +=item package_states => LISTREF + +Defines package methods that make up some or all of the session's +states. + +LISTREF is virtually identical to the one for object_states, so I'll +just skip to the examples. Check out object_states' description if +you'd like more details, replacing "object" and "object reference" +with "package" and "package name", respectively. + +So, here's a package_states invocation that defines two states, +state_one and state_two, which are implemented as Package->state_one() +and Package->state_two. + + package_states => + [ Package => [ 'state_one', 'state_two' ], + ], + +And here's an invocation that defines two other states, state_five and +state_six, to Package->do_five() and Package->do_six(). + + package_states => + [ Package => { state_five => 'do_five', + state_six => 'do_six', + }, + ], + +Easy-peasy! + +=back =item new LOTS_OF_STUFF POE::Session's new() constructor is slighly depreciated in favor of -the newer create() constructor. +the newer create() constructor. A detailed description of +POE::Session->new() is not forthcoming, but POE::Session's SYNOPSIS +briefly touches upon its use. =item option OPTION_NAME @@ -1312,9 +1411,12 @@ event at a session whenever it's pressed. -command => $session->postback( 'ev_counters_begin' ) )->pack; -While it was originally designed for Tk compatibility, this also can -be used to post events from Event's watchers. Event currently isn't -supported yet, because it doesn't seem to compile on FreeBSD or OS/2. +It can also be used to post events from Event watchers' callbacks. + + Event->flavor + ( cb => $session->postback( 'ev_flavor' ), + desc => 'post ev_flavor when Event->flavor occurs', + ); =back diff --git a/lib/POE/Wheel.pm b/lib/POE/Wheel.pm index e051b95e3..7ed922a42 100644 --- a/lib/POE/Wheel.pm +++ b/lib/POE/Wheel.pm @@ -18,7 +18,7 @@ __END__ =head1 NAME -POE::Wheel - POE Protocol Logic Abstraction +POE::Wheel - high-level protocol logic =head1 SYNOPSIS @@ -27,90 +27,81 @@ POE::Wheel - POE Protocol Logic Abstraction =head1 DESCRIPTION -Wheels provide standard, reusable protocol logic. They use filters -and drivers to do the actual work. They are designed to manage the -resources and objects they are given, so programs generally should not -bother keeping separate references to them. +Wheels contain reusable chunks of high-level logic. For example, +Wheel::FollowTail contains the algorithm for reading data from the end +of an ever growing file. Their logic is contained in bundles of +reusable states which they insert into and remove from their owners +during creation and destruction. -Wheels mainly work with files. They usually add and remove states to -handle select events in the sessions that create them. Creating a -wheel on behalf of another session will not do what you expect. -Likewise, calling another wheel's methods will do Strange Things, -because a certain level of privacy was assumed while writing them. +Giving a wheel to another session will not transfer related states. +As a result, the original owner will continue receiving a wheel's +events until it's destroyed. -=head1 PUBLIC WHEEL METHODS +=head1 COMMON PUBLIC WHEEL METHODS -=over 4 +These are the methods that are common to every wheel. -=item * +=over 2 -POE::Wheel::new( ... ) +=item new LOTS_OF_STUFF -The new() method creates and initializes a new wheel. Part of a -wheel's initialization involves adding states to its parent session -(the one that is calling the new() method) and registering them with -the kernel (usually through POE::Kernel::select() calls). -Instantiating wheels on behalf of other sessions will not work as -expected, if at all. +Creates a new wheel, returning its reference. The reference holder +should keep the wheel reference around until it's ready for the wheel +to stop. -Because wheels have wildly different purposes, they tend also to have -wildly different constructors. +Every wheel has a different purpose and requires different parameters, +so LOTS_OF_STUFF will vary from one to the next. -=item * +=item DESTROY -POE::Wheel::DESTROY() +Perl calls DESTROY when the wheel's reference is relinquished. This +triggers the wheel's destruction, which releases whatever resources it +was managing. -The DESTROY() method removes the wheel's states from its parent -session and cleans up the wheel's other resources. It's called -implicitly when the parent session lets go of the wheel's reference. +When passing resources from one wheel to another, it's important to +destroy the old wheel before creating the new one. If the hand-off is +not in this order, the old wheel's destruction will release the +resource B the new one has started watching it. The new wheel +will then not be watching the resource, even though it ought to be. -B When passing a filehandle between wheels, you must -ensure that the old wheel is destroyed before creating the new one. -This is necessary because destruction of the old wheel will remove all -the selects for the filehandle. That will undo any selects set by a -new wheel, preventing the new wheel from seeing any file activity. +=item put LIST -=item * +Send a LIST of things through the wheel. The LIST may only contain +one thing, and that's ok. Each thing in the LIST is serialized by the +wheel's Filter, and then bufferend until the wheel's Driver can flush +it to a filehandle. -POE::Wheel::put() +=item event TYPE => STATE_NAME, ... -Wheels hide their resources behind a high-level interface. Part of -that interface is the put() method, which calls Filter and Driver -put() methods as needed. +Changes the states that are called when a wheel notices certain types +of events occurring. -=item * +The event() method's parameters are pairs of event TYPEs and the +STATE_NAMEs to call when they occur. Event TYPEs differ for each +wheel, and their manpages will discuss them in greater detail. +STATE_NAMEs may be undef, in which case the wheel will stop invoking a +state for that TYPE of event. -POE::Wheel::event(...) - -Wheels emit events for different things. The event() method lets a -session change the events its wheels emit at runtime. - -The event() method's parameters are pairs of event types (defined by -wheels' /^.*State$/ constructor parameters) and events to emit. If -the event to emit is undef, then the wheel won't emit an event for the -condition. - -For example: - - $wheel->event( InputState => 'new_input_state', - ErrorState => undef, - FlushedState => 'new_flushed_state', - ); + $_[HEAP]->{wheel}->event( InputState => 'new_input_state', + ErrorState => undef, + FlushedState => 'new_flushed_state', + ); =back =head1 SEE ALSO -POE::Wheel; POE::Wheel::FollowTail; POE::Wheel::ListenAccept; -POE::Wheel::ReadWrite; POE::Wheel::SocketFactory +POE::Wheel::FollowTail; POE::Wheel::ListenAccept; +POE::Wheel::ReadWrite; POE::Wheel::SocketFactory. =head1 BUGS -Wheels are fine for what they do, but they tend to be limiting when -they're used in more interesting ways. +Wheels really ought to be replaced with a proper stream-based I/O +abstraction and POE::Component classes to replace FollowTail and +SocketFactory. =head1 AUTHORS & COPYRIGHTS -Please see the POE manpage. +Please see the POE manpage for authors and licenses. =cut diff --git a/tests/06_tk.t b/tests/06_tk.t new file mode 100644 index 000000000..311e1631b --- /dev/null +++ b/tests/06_tk.t @@ -0,0 +1,35 @@ +#!/usr/bin/perl -w +# $Id$ + +# Tests FIFO, alarm, select and Tk postback events using Tk's event +# loop. + +use strict; +use lib qw(./lib ../lib); +use TestSetup qw(99); + +# Turn on all asserts. +sub POE::Kernel::ASSERT_DEFAULT () { 1 } + +# Skip if Tk isn't here. +BEGIN { + eval 'use Tk'; + unless (exists $INC{'Tk.pm'}) { + for (my $test=1; $test <= 1; $test++) { + print "skip $test # no Tk support\n"; + } + } +} + +use POE; + +# Congratulate ourselves for getting this far. +print "ok 1\n"; + +$poe_kernel->run(); + +# Congratulate ourselves on a job completed, regardless of how well it +# was done. +print "ok N\n"; + +exit;