File Coverage

blib/lib/POE/Component/SNMP/Dispatcher.pm
Criterion Covered Total %
statement 51 201 25.3
branch 1 50 2.0
condition 2 5 40.0
subroutine 20 46 43.4
pod 0 9 0.0
total 74 311 23.7


line stmt bran cond sub pod time code
1             package POE::Component::SNMP::Dispatcher;
2              
3 16     16   97 use strict;
  16         34  
  16         762  
4              
5 16     16   84 use base qw/Net::SNMP::Dispatcher/;
  16         94  
  16         1467  
6              
7 16     16   95 use POE::Kernel; # imports $poe_kernel
  16         39  
  16         177  
8 16     16   940 use POE::Session;
  16         43  
  16         128  
9              
10 16     16   1881 use Time::HiRes qw/time/;
  16         27  
  16         305  
11 16     16   2079 use Scalar::Util qw/weaken/;
  16         30  
  16         2727  
12              
13             our $VERSION = '1.32';
14              
15             our $INSTANCE; # reference to our Singleton object
16              
17             our $MESSAGE_PROCESSING; # reference to single MP object
18              
19             BEGIN {
20 16 50   16   627 if ( ! defined &VERBOSE ) {
21 0         0 eval { sub VERBOSE () { 0 } };
22             }
23             }
24              
25             # *DEBUG_INFO = sub {};
26             *DEBUG_INFO = \&Net::SNMP::Dispatcher::DEBUG_INFO;
27              
28 16     16   87 use constant _ACTIVE => 0; # State of the event ( not used )
  16         27  
  16         1092  
29 16     16   72 use constant _TIME => 1; # Execution time
  16         25  
  16         757  
30 16     16   75 use constant _CALLBACK => 2; # Callback reference
  16         20  
  16         615  
31 16     16   67 use constant _DELAY => 3; # Delay, in seconds
  16         28  
  16         667  
32              
33 16     16   76 use constant _PAUSE_FD => 0;
  16         27  
  16         4076  
34              
35             # {{{ SUBCLASSED METHODS
36              
37             # all subclassed methods return the same values as their base
38             # versions.
39              
40             # {{{ instance methods and constructor
41              
42 19   66 19 0 170 sub instance { $INSTANCE ||= POE::Component::SNMP::Dispatcher->_new }
43              
44             # In Net::SNMP::Dispatcher, this function invokes the event
45             # dispatch loop. Here, we let POE handle things for us instead,
46             # and overload with a no-op.
47 0     0 0 0 sub activate { }
48              
49 16     16   194 sub _new { shift->SUPER::_new(@_)->_new_session() }
50              
51             sub _new_session {
52 16     16   295 my $this = shift;
53              
54             # $this->{_active} = Net::SNMP::Message::TRUE;
55 16         125 $this->{_active} = 1;
56              
57 16         35 $MESSAGE_PROCESSING = $Net::SNMP::Dispatcher::MESSAGE_PROCESSING;
58 16         174 POE::Session->create( object_states =>
59             [ $this => [
60             qw/
61             _start
62             _stop
63             __schedule_event
64             __invoke_callback
65             __socket_callback
66             __listen
67             __dispatch_pdu
68             __clear_pending
69             /
70             ],
71             ]);
72              
73 16         31647 $this;
74             }
75              
76             # }}} instance methods and constructor
77             # {{{ send_pdu and _send_pdu
78              
79             # Net::SNMP::Dispatcher::send_pdu() takes a reference to &_send_pdu in
80             # its own package, which bypasses inheritance. Here we temporarily
81             # replace that reference to point to our own local copy before
82             # continuing.
83             #
84             # This is the first method in the chain of calls to
85             # Net::SNMP::Dispatcher that gets the action going.
86             sub send_pdu {
87 0     0 0 0 my ($this, $pdu, $delay) = @_;
88              
89 0         0 DEBUG_INFO('%s', dump_args( [ $pdu, $delay ] ));
90              
91 16     16   97 no warnings; # the line below warns "redefined"
  16         34  
  16         42251  
92 0         0 local *Net::SNMP::Dispatcher::_send_pdu = \&_send_pdu;
93              
94 0         0 weaken($pdu);
95              
96 0         0 VERBOSE and DEBUG_INFO('{-------- SUPER::send_pdu()');
97 0         0 my $retval = $this->SUPER::send_pdu($pdu, $delay);
98 0         0 VERBOSE and DEBUG_INFO(' --------} SUPER::send_pdu()');
99              
100 0         0 $retval;
101             }
102              
103             # _send_pdu() tosses requests into POE space at the __dispatch_pdu
104             # state, which invokes SUPER::_send_pdu() or queues requests for
105             # later, as appropriate.
106             sub _send_pdu {
107 0     0   0 my ($this, $pdu, $timeout, $retries) = @_;
108              
109 0         0 DEBUG_INFO('dispatching request [%d] %s', $pdu->transport->fileno,
110             VERBOSE ? dump_args( [ $pdu, $timeout, $retries ] ) : '');
111              
112             # using yield() or call() instead of post() here breaks things. So don't do that.
113 0         0 $poe_kernel->post(_alias() => __dispatch_pdu =>
114             $pdu, $timeout, $retries);
115              
116 0         0 1;
117             }
118              
119             # }}} send_pdu and _send_pdu
120             # {{{ schedule and cancel
121              
122             # Net::SNMP v5.x
123              
124             # In Net::SNMP::Dispatcher, the critical methods to intercept are:
125             # - register() : listen for data on a socket
126             # - schedule() : schedule a timeout action if no response is received
127             # - deregister(): stop listening on a socket
128             # - cancel() : cancel a pending event
129             # Our versions hand the appropriate actions to POE.
130             #
131              
132             sub schedule {
133 0     0 0 0 my ($this, $when, $callback) = @_;
134 0         0 my $time = time;
135              
136             # cook the args like Net::SNMP::schedule() does for _event_insert()
137 0         0 my $event = [ $this->{_active}, $time + $when, $this->_callback_create($callback), $when ];
138              
139 0         0 my $fileno = $this->_get_fileno($event);
140 0 0       0 if ($event->[_TIME] <= $time) {
141             # run the callback NOW, instead of invoking __invoke_callback. saves a POE call().
142              
143 0         0 DEBUG_INFO('{-------- invoking callback [%d] %s', $fileno,
144             VERBOSE ? dump_args( $event->[_CALLBACK] ) : '');
145              
146 0         0 $this->_callback_execute($event->[_CALLBACK]); # no parameter cooking needed!
147              
148 0         0 DEBUG_INFO(' --------} callback complete [%d]', $fileno);
149             } else {
150 0         0 DEBUG_INFO("%0.1f seconds [%d] %s", $event->[_DELAY], $fileno,
151             VERBOSE ? dump_args( $event->[_CALLBACK] ) : '');
152              
153             # This call breaks down to $kernel->alarm_set($event)
154 0         0 $poe_kernel->call(_alias() => __schedule_event => $event);
155              
156             # $poe_kernel->post(_alias() => __schedule_event => $event);
157             # breaks
158             # $poe_kernel->yield(__schedule_event => $event);
159             }
160              
161 0         0 $event;
162             }
163              
164             sub cancel {
165 0     0 0 0 my ($this, $event) = @_;
166              
167             # this catches a stray shutdown case where __schedule has been
168             # queued but not yet dispatched. In this case, $event->[_TIME]
169             # will be an epoch time in the future, meaning that we never
170             # replaced it with a POE delay id, which means there is no POE
171             # event to cancel.
172 0 0       0 return if $event->[_TIME] > time;
173              
174             # $event->[_TIME] is the POE alarm id, which was stashed in __schedule_event
175 0         0 DEBUG_INFO('remove alarm id %d', $event->[_TIME]);
176 0         0 $poe_kernel->alarm_remove($event->[_TIME]);
177              
178 0         0 return ! ! $this->_pending_pdu_count($this->_get_fileno($event)); # boolean: are there entries are left?
179             }
180              
181             # }}} schedule and cancel
182             # {{{ register and deregister
183              
184             ## version support
185             # see the notes on Net::SNMP v4.x support
186              
187             our $SUPER_register = 'SUPER::register';
188             our $SUPER_deregister = 'SUPER::deregister';
189              
190             ## coding notes
191             #
192             # Here we say $poe_kernel->call(dispatcher => '__listen' ), which does
193             # select_read() *within a POE::Session* and returns, instead of simply
194             # invoking select_read() here, so that select_read() is guaranteed to
195             # occur from within the 'dispatcher' session (instead of possibly the
196             # parent 'snmp' session). Otherwise, when we reach _unlisten(), we
197             # could get a (silent) failure because the "session doesn't own
198             # handle".
199              
200             # This was a *GIGANTIC* hassle to debug, and I don't care who
201             # knows about it. During the course of tracing this down, Rocco even
202             # added a diagnostic message to indicate this problem (see the Changes
203             # file for POE 0.29 ), so at least I can have the satisfaction of
204             # having been responsible for somebody else down the line not having
205             # to spend the hours debugging this same problem that I did.
206              
207             sub register {
208 0     0 0 0 my ($this, $transport, $callback) = @_;
209              
210 0         0 DEBUG_INFO('register on [%d] %s', $transport->fileno, VERBOSE ? dump_args([ $callback ]) : '');
211              
212 0 0       0 if (ref ($transport = $this->SUPER::register($transport, $callback))) {
213              
214             # $poe_kernel->post(_alias() => __listen => $transport);
215 0         0 $poe_kernel->call(_alias() => __listen => $transport);
216              
217             # we would use this version if we were sending the callback to
218             # return with the "got data" event, but in fact we retrieve it
219             # directly from the SNMP object. I can't make up my mind
220             # which is cleaner in terms of encapsulation:
221              
222             # $poe_kernel->post(_alias() => __listen => $transport,
223             # [ $this->_callback_create($callback), $transport ]);
224             }
225              
226 0         0 $transport;
227             }
228              
229             # there is an optimization here in not having a __unlisten state
230             # corresponding to __listen (avoiding call() overhead), and just
231             # telling the kernel directly to stop watching the handle. __listen
232             # only needs to exist because when we watch a socket, we have to be in
233             # the right session... deregister() is always called in the same
234             # session as __listen.
235              
236             sub deregister {
237 0     0 0 0 my ($this, $transport) = @_;
238 0         0 my $fileno = $transport->fileno;
239              
240 0         0 DEBUG_INFO('deregister on [%d] %s', $transport->fileno,
241             VERBOSE ? dump_args([ $transport ]) : '');
242              
243 0 0       0 if (ref ($transport = $this->SUPER::deregister($transport))) {
244 0         0 $this->_unwatch_socket($transport->socket);
245             }
246              
247             # no more current.
248 0         0 $this->_clear_current_pdu($fileno);
249              
250 0 0       0 if ($this->_pending_pdu_count($fileno)) {
251             # run next pending
252 0         0 DEBUG_INFO('dispatching (queued) request on [%d] %d remaining',
253             $fileno, $this->_pending_pdu_count($fileno) - 1);
254              
255             # $poe_kernel->yield(__dispatch_pending_pdu => $fileno);
256 0         0 $poe_kernel->yield(__dispatch_pdu => $this->_get_next_pending_pdu_args($fileno));
257             }
258              
259 0         0 $transport;
260             }
261              
262             # }}} register and deregister
263              
264             # }}} SUBCLASSED METHODS
265             # {{{ PRIVATE METHODS
266              
267             ##### socket methods
268             #
269             ## These two methods are the only place in this module where the
270             ## socket refcounting is done, so it's all self-contained.
271             #
272             # {{{ _watch_socket
273              
274             # socket listen with refcount. If socket refcount, increment it. Else
275             # set refcount and listen on the socket.
276             #
277             # accesses global kernel.
278             sub _watch_socket {
279 0     0   0 my ($this, $socket) = @_;
280 0         0 my $fileno = $socket->fileno;
281              
282 0 0       0 if (not $this->{_refcount}{$fileno}) {
283             # reference counting starts at 1 for the controlling
284             # *session*, and 1 for this *request*.
285             #
286             # refcount will fluctuate between 1 and 2 until the owning
287             # snmp session is stopped, then it will drop to 0 and we'll
288             # stop watching that handle.
289 0         0 $this->{_refcount}{$fileno} = 1 + 1;
290              
291 0         0 DEBUG_INFO('[%d] refcount %d, select', $fileno, $this->{_refcount}{$fileno});
292              
293 0         0 $poe_kernel->select_read($socket, '__socket_callback');
294             } else {
295 0         0 $this->{_refcount}{$fileno}++;
296 0         0 DEBUG_INFO('[%d] refcount %d, resume', $fileno, $this->{_refcount}{$fileno});
297              
298 0         0 _PAUSE_FD and $poe_kernel->select_resume_read($socket);
299             }
300 0         0 $this->{_refcount}{$fileno};
301             }
302              
303             # }}} _watch_socket
304             # {{{ _unwatch_socket
305              
306             # decrement the socket refcount. unlisten if refcount == 0.
307             # accesses global kernel.
308             sub _unwatch_socket {
309 0     0   0 my ($this, $socket) = @_;
310 0         0 my $fileno = $socket->fileno;
311              
312 0 0       0 if (--$this->{_refcount}{$fileno} <= 0) {
313 0         0 DEBUG_INFO('[%d] refcount %d, unselect', $fileno, $this->{_refcount}{$fileno});
314              
315             # stop listening on this socket
316 0         0 $poe_kernel->select_read($socket, undef);
317             } else {
318 0         0 DEBUG_INFO('[%d] refcount %d, pause %s',
319             $fileno, $this->{_refcount}{$fileno}, ('(deferred)') x defined $this->_current_pdu($fileno) );
320              
321 0 0       0 _PAUSE_FD and $poe_kernel->select_pause_read($socket) unless $this->_current_pdu($fileno);
322              
323             }
324 0         0 $this->{_refcount}{$fileno}
325             }
326              
327             # }}} _unwatch_socket
328             #####
329              
330             ##### current and pending PDU pethods
331             # {{{ _current_pdu
332              
333             # if called with one argument, a fileno, returns the current pdu.
334             #
335             # if called with two arguments, a fileno and a pdu, makes that pdu the
336             # current pdu.
337             sub _current_pdu {
338 0     0   0 my ($this, $fileno, $pdu) = @_;
339              
340 0 0       0 if (@_ == 3) {
341 0         0 $this->{_current_pdu}{$fileno} = $pdu;
342             }
343              
344 0         0 $this->{_current_pdu}{$fileno};
345             }
346              
347             # remove the current pdu. return it.
348             sub _clear_current_pdu {
349 0     0   0 my ($this, $fileno) = @_;
350              
351 0         0 delete $this->{_current_pdu}{$fileno};
352             }
353              
354             # }}} _current_pdu
355             # {{{ (_enqueue_pending|_get_next_pending|_clear_pending)_pdu
356              
357             # enqueues an array reference
358             sub _enqueue_pending_pdu {
359 0     0   0 my ($this, $fileno, $arg) = @_;
360              
361 0         0 push @{$this->{_pending_pdu}{$fileno}}, $arg;
  0         0  
362             }
363              
364             # dequeues an array reference and dereferences it, returning an array
365             sub _get_next_pending_pdu_args {
366 0     0   0 my ($this, $fileno) = @_;
367              
368 0         0 @{ shift @{$this->{_pending_pdu}{$fileno}} }
  0         0  
  0         0  
369             }
370              
371             # deletes the pending queue
372             sub _clear_pending_pdu {
373 0     0   0 my ($this, $fileno) = @_;
374              
375 0         0 delete $this->{_pending_pdu}{$fileno};
376             }
377              
378             # }}} (_enqueue_pending|_get_next_pending|_clear_pending)_pdu
379             # {{{ _pending_pdu_count
380              
381             sub _pending_pdu_count {
382 0     0   0 my ($this, $fileno) = @_;
383              
384 0         0 ref $this->{_pending_pdu}{$fileno} eq 'ARRAY' ?
385 0 0       0 scalar @{$this->{_pending_pdu}{$fileno}} :
386             0
387             }
388              
389             # }}} _pending_pdu_count
390             #####
391              
392             # {{{ _current_callback
393              
394             # fetch the "current" callback for the fileno corresponding to the
395             # socket we just saw a response on out of Net::SNMP::Dispatcher.
396             sub _current_callback {
397 0     0   0 my ($this, $fileno) = @_;
398              
399 0         0 $this->{_descriptors}{$fileno}
400             }
401              
402             # }}} _current_callback
403             # {{{ _get_fileno
404              
405             # the calls to schedule($when, $callback) looks like this:
406             # $this->schedule($delay, [\&_send_pdu, $pdu, $pdu->timeout, $pdu->retries]);
407             # $this->schedule($timeout, [\&_transport_timeout, $pdu, $timeout, $retries])
408              
409             # so _CALLBACK is: [ CODE, PDU, TIMEOUT, RETRIES ];
410              
411             sub _get_fileno {
412 0     0   0 my ($this, $event) = @_;
413              
414 0         0 return $this->_fileno_from_callback($event->[_CALLBACK]);
415             }
416              
417             sub _fileno_from_callback {
418 0     0   0 my ($self, $callback) = @_;
419             # $callback->[1] is a $pdu object
420 0         0 return $callback->[1]->transport->fileno;
421             }
422              
423             # }}} _get_fileno
424              
425             # {{{ _alias
426              
427             # this session runs as a singleton, here is its session alias:
428 30     30   215 sub _alias { '_poe_component_snmp_dispatcher' }
429              
430             # }}} _alias
431              
432             # }}} PRIVATE METHODS
433             # {{{ POE EVENTS
434              
435             # By convention, all POE states, except _start and _stop, have
436             # two leading underscores.
437              
438             # {{{ _start and _stop
439              
440             sub _start {
441 16     16   6281 $_[KERNEL]->alias_set(_alias())
442             }
443              
444             sub _stop {
445 14     14   10483 $_[KERNEL]->alias_remove(_alias());
446 14         672 undef $INSTANCE;
447             }
448              
449             # }}} _start and _stop
450             # {{{ __dispatch_pdu
451              
452             # We want to prevent conflicts between listening sockets and pending
453             # requests, because POE can't listen to two at a time on the same
454             # handle. If that socket is currently listening for a reply to a
455             # different request (eg $this->_current_pdu() is TRUE), the request is
456             # queued, otherwise it is dispatched immediately.
457             #
458             # (which again additionally POE-izes Net::SNMP)
459             #
460             # this event is invoked by _send_pdu()
461             sub __dispatch_pdu {
462 0     0     my ($this, @pdu_args) = @_[OBJECT, ARG0..$#_];
463              
464             # these are the args this state was invoked with:
465             # @pdu_args = ( $pdu, $timeout, $retries );
466              
467 0           my $pdu = $pdu_args[0];
468 0           my $fileno = $pdu->transport->fileno;
469              
470             # enqueue or execute
471 0 0         if ($this->_current_pdu($fileno)) {
472             # this socket is busy. enqueue.
473              
474 0           $this->_enqueue_pending_pdu($fileno => \@pdu_args);
475 0           DEBUG_INFO('queued request for [%d] %d requests pending',
476             $fileno, $this->_pending_pdu_count($fileno));
477              
478             } else {
479             # this socket is free. execute.
480              
481 0           DEBUG_INFO('sending request for [%d]', $fileno);
482              
483 0           $this->_current_pdu($fileno => $pdu);
484              
485 0           VERBOSE and DEBUG_INFO('{-------- SUPER::_send_pdu() for [%d]', $fileno);
486 0           $this->SUPER::_send_pdu(@pdu_args);
487 0           VERBOSE and DEBUG_INFO(' --------} SUPER::_send_pdu() for [%d]', $fileno );
488             }
489             }
490              
491             # }}} __dispatch_pdu
492             # {{{ __schedule_event
493              
494             # this event is invoked by schedule() / _event_insert()
495             sub __schedule_event {
496 0     0     my ($this, $event) = @_[ OBJECT, ARG0 ];
497              
498             # $event->[_ACTIVE] is always true for us, and we ignore it.
499             #
500             # $event->[_TIME] is the epoch time this event should fire. We
501             # use that value for scheduling the POE event, then replace it
502             # with POE's alarm id.
503             #
504             # $event->[_CALLBACK] is an opaque callback reference.
505             #
506             # $event->[_DELAY] is how long from the time of scheduling to
507             # fire the event, in seconds
508             #
509             # We get this same $event back in cancel(), where we reference
510             # $event->[_TIME] as alarm id to deactivate.
511              
512 0 0         if ($event->[_TIME] <= time) {
513 0           $this->_callback_execute($event->[_CALLBACK]); # no parameter cooking needed!
514 0           return;
515             }
516              
517 0           my $timeout_id = $poe_kernel->alarm_set(__invoke_callback => $event->[_TIME], $event->[_CALLBACK]);
518              
519             # stash the alarm id. since $event is a reference, this
520             # assignment is "global".
521 0           $event->[_TIME] = $timeout_id;
522              
523             # I only use $event->[_DELAY] for debugging.
524 0           DEBUG_INFO("alarm id %d, %0.1f seconds [%d] %s",
525             $timeout_id, $event->[_DELAY],
526             $this->_get_fileno($event),
527             VERBOSE ? dump_args([ $event->[_CALLBACK] ]) : ''
528             );
529             }
530              
531             # }}} __schedule_event
532             # {{{ __invoke_callback
533              
534             # Invokes a callback immediately.
535             #
536             # this event is invoked when an delay has fired.
537             sub __invoke_callback {
538 0     0     my ($this, $callback) = @_[OBJECT, ARG0];
539              
540 0           my $fileno = $this->_fileno_from_callback($callback);
541 0           DEBUG_INFO('{-------- invoking scheduled callback for [%d] %s',
542             $fileno, VERBOSE ? dump_args([ $callback ]) : '');
543              
544 0           $this->_callback_execute($callback);
545              
546 0           DEBUG_INFO(' --------} callback complete for [%d]', $fileno );
547             }
548              
549             # }}} __invoke_callback
550             # {{{ __listen
551              
552             # stash the supplied $callback based on the fileno of the $transport
553             # object. tell POE to watch the $transport's socket.
554             #
555             # this event is invoked by register()
556             sub __listen {
557 0     0     my ($this, $transport, $callback) = @_[OBJECT, ARG0, ARG1];
558             # we'll fetch the callback directly from $this in
559             # __socket_callback. later versions of POE allow for sending the
560             # callback with the request, but we only strive for a "relatively
561             # recent" version. Actually, we've tested all the way back to
562             # 0.22, released 03-Jul-2002.
563              
564 0           DEBUG_INFO('listening on [%d]', $transport->fileno);
565 0           $this->_watch_socket($transport->socket);
566             }
567              
568             # }}} __listen
569             # {{{ __socket_callback
570              
571             # fetch the stashed callback and execute it.
572             #
573             # this event is invoked when a watched socket becomes ready to read
574             # data.
575             sub __socket_callback {
576 0     0     my ($this, $socket) = @_[OBJECT, ARG0];
577 0           my $fileno = $socket->fileno;
578              
579 0 0         return unless $this->_current_callback($fileno);
580              
581 0           DEBUG_INFO('{-------- invoking callback for [%d] %s',
582             $fileno, dump_args($this->_current_callback($fileno)));
583              
584 0           $this->_callback_execute( @{ $this->_current_callback($fileno) } );
  0            
585             # the extra argument contained in the callback is harmless
586              
587 0           DEBUG_INFO(' --------} callback complete for [%d]', $fileno);
588             }
589              
590             # }}} __socket_callback
591             # {{{ __clear_pending
592              
593             # account for a 'finish' request to a parent snmp session. Cancels
594             # any *pending* requests for the specified session. However, if
595             # 'finish' is called on a session while the Dispatcher is currently
596             # listening for a reply to that session, that reply *will* be
597             # delivered when it arrives.
598             #
599             # this event is invoked from P::C::S::close_snmp_session(), to help us
600             # keep in sync.
601             sub __clear_pending {
602 0     0     my ($this, $session) = @_[OBJECT, ARG0];
603              
604 0           DEBUG_INFO('start');
605              
606 0 0         my $socket =
    0          
607             $session->transport ?
608             $session->transport->socket :
609             $session->{_pdu}{_transport} ?
610             $session->{_pdu}{_transport}->socket :
611             undef;
612              
613 0 0         my $fileno = $socket ? $socket->fileno : undef;
614              
615 0           DEBUG_INFO('clearing %d pending requests', $this->_pending_pdu_count($fileno));
616 0           $this->_clear_pending_pdu($fileno);
617              
618             # we purposely do NOT delete $this->_current_pdu($fileno) until
619             # *AFTER* the select() stuff, so that it doesn't bother doing
620             # socket ops, because next we will stop listening all the way.
621              
622             # drop reference count
623             # $this->_unwatch_socket($session->transport->socket);
624 0           $this->_unwatch_socket($socket);
625              
626 0 0         if (defined (my $pdu = $this->_clear_current_pdu($fileno))) {
627              
628 0           DEBUG_INFO('cancelling current request');
629              
630             # stop listening
631 0           $this->deregister($pdu->transport);
632              
633             # cancel pending timeout:
634              
635             # Fetch the last cached reference held to our request (and its
636             # postback) held outside our own codespace...
637 0 0         if (defined (my $request = $MESSAGE_PROCESSING->msg_handle_delete($pdu->request_id))) {
638             # ... which returns enough information to cancel anything
639             # we had pending:
640 0           $this->cancel($request->timeout_id);
641             }
642              
643             }
644              
645 0           DEBUG_INFO('done');
646             }
647              
648             # }}} __clear_pending
649              
650             # }}} POE EVENTS
651              
652             # {{{ method call tracing
653              
654             # this code generates overload stubs for EVERY method in class
655             # SUPER, that warn their name and args before calling SUPER:: whatever.
656             if (0) {
657             my $code_for_method_tracing = q!
658              
659             no strict; # 'refs';
660             my $package = __PACKAGE__ . "::";
661             my $super = "$ISA[0]::";
662              
663             for (grep defined *{"$super$_"}{CODE}, keys %{$super}) {
664             next if /_*[A-Z]+$/; # ignore constants
665             next if defined *{ "$package$_" }{CODE};
666             print "assigning trace for $_\n";
667              
668             *{ "$package::$_" } =
669             eval qq[ sub {
670             my (\$package, \$filename, \$line, \$subroutine, \$sub) = caller (1);
671             print "$super$_ from \$subroutine:\$line ", (dump_args(\\\@_)), "\n";
672             goto &{"$super$_"};
673             }
674             ];
675              
676             warn "$@" if $@; # in case we screwed something up
677             }
678             !
679              
680             }
681              
682             # {{{ dump_args
683              
684             # get sub_fullname from Sub::Identify if it's present and we're being
685             # VERBOSE. Otherwise, generate our own, simple version.
686             eval { require Sub::Identify };
687              
688             if ($@ or not VERBOSE) {
689              
690 16     16   202 no warnings 'redefine';
  16         43  
  16         4882  
691 0     0 0   eval { sub sub_fullname($) { ref shift } };
692              
693             } else {
694             Sub::Identify->import('sub_fullname') unless *sub_fullname;
695             }
696              
697             sub dump_args {
698 0     0 0   return '' unless VERBOSE;
699 0           my @out;
700 0           my $first = 0;
701 0           for (@{$_[0]}) {
  0            
702 0 0         next if ref eq __PACKAGE__;
703             # next if $first++;
704 0           my $out;
705 0 0         if (ref eq 'ARRAY') {
    0          
706 0           $out .= '[';
707 0 0 0       $out .= join ' ', map {ref $_ ? (ref $_ eq 'CODE' ? sub_fullname($_) : ref $_ ) : $_ || 'undef'} @$_;
  0 0          
708 0           $out .= ']';
709             } elsif (defined $_) {
710 0 0         $out .= ref $_ ? ref $_ : $_;
711             } else {
712 0           $out .= 'undef';
713             }
714 0           push @out, $out;
715             }
716              
717 0           return '{' . join (" ", @out) . '}';
718             }
719              
720             # }}} dump_args
721              
722             # }}} method call tracing
723              
724             1;
725              
726             __END__