jasonk has asked for the wisdom of the Perl Monks concerning the following question:

POE's garbage collection is pretty good, it does a heck of a job of keeping track of which sessions are in use and destroying them when they are no longer doing anything. One of the things it uses to determine if a session is doing anything is whether there is anything running that could post events to that session. In my case, my application is being hung by a TCP client with a connection to a messaging server.

My situation is that all the POE processes in this application have a session that handles a connection to a STOMP-like messaging server. At startup time, all the other sessions post events to the messaging session to inform it which message queues on the server they would like to subscribe to. These sessions will sometimes change their subscriptions as the application runs. I'm using refcount_increment and refcount_decrement from POE::Kernel to indicate to POE that those sessions are still in use as long as they have at least one subscription to a messaging channel.

What I haven't been able to figure out though, is how I can tell POE that those sessions that are not subscribed to any messaging queues should not include the fact that there are events being generated by the messaging session in the calculation of which sessions are still active.

Basically, right after startup my application looks something like this....

Messaging Session
Connected to STOMP server
Worker Session 1
Subscribed to /queue/foo/bar
Worker Session 2
Subscribed to /queue/something/else
Worker Session 3
Worker Session 4

The problem is that when POE is determining which sessions are still active, it thinks that all four worker sessions are still in use, because they may get sent events from some of the other sessions, so it doesn't shut them down. Because of the design of the application, I know that this isn't the case, the worker sessions don't communicate with each other directly, if they do have information to send to one another (and in the vast majority of cases, they won't) then they will always send it through the STOMP server. I also know that the worker sessions that aren't subscribed to any queues and don't have any events of their own queued up, will never get sent another event, they will always idle until the application ends. So what I'm looking for is a way to tell POE this is the case, so I don't have these extra sessions hanging around all the time.

If it isn't possible to do this at the session level, I'd settle for a way to tell POE that the application can end if the only thing keeping it alive is that socket connection. At least that way my tests wouldn't hang. :) I've looked at POE::Kernel::_idle_queue_{grow,shrink}, but they seem to only apply to the event queue, not to filehandles being watched.

What I'm doing currently is not connecting to the server until a subscription is active, and then shutting down the connection when the last subscription is unsubscribed, which works OK, but is less than ideal, and has some issues (like if a session unsubscribes from the last subscription, and then subscribes to something else, causing the connection to drop and reconnect).

Any suggestions would be appreciated!


We're not surrounded, we're in a target-rich environment!
  • Comment on Telling POE to ignore a filehandle for garbage collection purposes?

Replies are listed 'Best First'.
Re: Telling POE to ignore a filehandle for garbage collection purposes?
by shmem (Chancellor) on Mar 17, 2008 at 23:33 UTC
    If you don't need those "sessions hanging around" to do something useful at some later time, you have to figure out what the appropriate state is to send them a _stop event to pull them actively - just guessing...

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

      The problem is that I might need them later, only the garbage collector knows for sure. As an example, if I make the messaging session go away when the last subscription does, then code like this will fail:

      sub handle_something { my ( $kernel, $heap ) = @_[ KERNEL, HEAP ]; $kernel->post( $heap->{ 'messaging_session_id' }, 'unsubscribe_all', ); $kernel->post( $heap->{ 'messaging_session_id' }, 'subscribe' => '/some/message/queue', ); }

      POE's garbage collection includes a bunch of different things in determining if there is any work left to do (pending events, child sessions, handles in use, aliases in use, extra refs, external processes spawned) and basically what I want to do is tell it not to include this one handle in it's consideration, so that the messaging session alone can't keep the process alive, but I don't want to shut the messaging session down completely, because if any of those other things are generating events, then it may still be needed.


      We're not surrounded, we're in a target-rich environment!

      You don't send a _stop event. POE generates a _stop event to indicate that a session is terminating.

      Not seeing any code, my best guess would be to implement an idle timer on worker socket. I've done this in the past, by storing a time stamp using time() in the heap in a POE::Wheel::ReadWrite's InputEvent handler and FlushedEvent handler. Then have a delayed event that periodically wakes up and checks the time stamp against the current time and takes appropriate action.

      sub _start_wheel { # we've started our wheel and stashed it in the heap. # let's start a delayed event. $kernel->delay( '_wheel_alarm', 60 ); return; } sub _input_handler { # deal with input $heap->{timestamp} = time(); return; } sub _flushed_handler { # Looks like we sent some stuff $heap->{timestamp} = time(); return; } sub _wheel_alarm { my ($kernel,$heap) = @_[KERNEL,HEAP]; # ding dong if ( time() - $heap->{timestamp} > 300 ) { delete $heap->{socket_wheel}; # remove the socket } else { $kernel->delay( '_wheel_alarm', 60 ); } return; }

        After some more digging, I've pretty much come to the conclusion that what I'm trying to do can't be done without mucking around with POE internals....

        Here is the code I finally came up with, though I would prefer there were a way to do it without the monkey-patch. On the other hand though, monkey-patching seems to be part of the core for POE. :)

        # NOTE: This hasn't really had any testing yet... package POE::Resource::Sessions::GCIgnore; use strict; use warnings; our $VERSION = '0.01'; # monkey-patch into POE::Kernel package POE::Kernel; sub _data_ses_collect_garbage { my ( $self, $session ) = @_; # snipping out the ASSERT_DATA/TRACE_REFCOUNT stuff # that was just copied from POE::Resource::Sessions... my $count = $kr_sessions{ $session }->[ SS_REFCOUNT ]; if ( $session->can( 'gc_ignore' ) ) { $count -= $session->gc_ignore; } return if $count > 0; $self->_data_ses_stop( $session ); }

        We're not surrounded, we're in a target-rich environment!