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

Hello Monks-

I have a perl script with a pTk text window being fed input from a single POE session (POE::Wheel::FollowTail) and want to destroy it while keeping the GUI up and running.

I can't seem to make this happen (see code below). When I hit the Stop button, the perl script exits. Also, if the input file is big enough, and you hit the stop button before its all been read, the stop button seems to have no effect. I suspect I'm doing something stupid here, but just can't see it.

Any help appreciated!

Thanks

-Craig

use strict; use warnings; use Tk; # This MUST come before use POE... use POE; use POE::Wheel::FollowTail; # Use any nearby file for input... my $ifile = shift || die "Missing input file"; _displayData(); print STDERR "STARTING to process data...\n"; _processData($ifile); POE::Kernel->run; exit; sub _processData{ my $ifile=shift || die "Missing input file"; POE::Session->create( inline_states=>{ _start=>sub { print "Session ", $_[SESSION]->ID, " has started.\n"; $_[KERNEL]->alias_set("myClient"); my ( $kernel, $heap ) = @_[ KERNEL, HEAP ]; $_[HEAP]->{wheel}=POE::Wheel::FollowTail->new( Seek=>0, Filename=>$ifile, InputEvent=>'display', ); }, display => sub{ InputEvent=>$_[KERNEL]->post(Display=>data=>$_[ARG0]), }, sendStop => sub{ print "Stopping file input\n"; my $heap = $_[HEAP]; delete $heap->{wheel}; }, }, ), } sub _displayData{ my $text; POE::Session->create( inline_states => { _start => sub { $_[KERNEL]->alias_set("Display"); my $kernel=$_[KERNEL]; my $top = $poe_main_window; $top->Button(-text=>"Stop", -command=>sub{ stop($kernel)})-> pack(-side=>'bottom'); $text = $top->Scrolled('Text', -scrollbars=>'osow', -wrap=>'none')-> pack(-fill=>'both',-side=>'left', -expand=>1); }, data=>sub{ $text->insert(end=>$_[ARG0] . "\n"); $text->update; }, }); } sub stop{ my $ker = shift; if ($ker){ print STDERR "Sending sendStop to myClient\n"; $ker->call("myClient","sendStop"); } }

Replies are listed 'Best First'.
Re: Confusion with POE & pTk
by rcaputo (Chaplain) on Oct 10, 2008 at 22:29 UTC

    The problem is that your POE::Session instance is only doing one POEy thing: tailing that file. When it stops, POE recognizes that nothing POEy is happening and stops the seesion for inactivity.

    The program needs a way to say: I'm getting events from Tk, so I'll stick around as long as the user interface is present.

    POE has this, through POE::Session's postback() method. Postbacks are anonymous subroutines that, when called, post events. They also have a side effect: as long as they exist, the target session will remain alive.

    Try this:

    my $client_session = $_[KERNEL]->alias_resolve("myClient"); $top->Button( -text =>"Stop", -command => $client_session->postback("sendStop"), )->pack(-side=>'bottom');
    When that button is pressed, Tk will send a "sendStop" to the "myClient" session. If it works, you can discard the stop() function.

    The unresponsiveness happens because POE::Wheel::FollowTail has already read the entire file and generated events for each line. Your button press is enqueued at the end of all that.

    You'll either have to wait it out, or not seek so far from the end of the file.

      Rocco-

      rcaputo++ for the response, and for all the work you've done on POE in general.

      To rephrase what you said (see if I understood correctly):

      The problem is that I only have one POE thing happening (the FollowTail), and when that session goes away, POE looks around and doesn't see anything else going on, so the whole POE kernel is exited. If I could find a way to keep the FollowTail session alive (even though it's not FollowTail-ing anymore) the kernel won't exit.

      To see if I understood this concept correctly, I simply added the following lines to the original script, right after the call to _processData():

      # This should simply create a dummy postback on the FollowTail session my $subref = $sess->postback('DontDie');

      This works fine, so I believe you are correct, but don't understand why the second POE::Session->create() in _displayData() isn't considered my second POEy thing? I assumed that this was my second session that it would force POE to hang around until the Tk stuff died (even after my FollowTail session exited).

      Obviously not, but I don't understand why...

      Finally, one side issue I encountered was that I wasn't able to use postback() to fire the sendStop event to the myClient session. As far as I could tell (in my environment) the command  $session->postback("sendStop") does nothing.

      Any help with these issues would be great!

      Thanks

      -Craig

        That's a good analysis, Craig.

        The second session, "Display" if I'm not lost, doesn't have any POEy things going on. The only reason it sticks around at all is because its alias implies that another session might send it events.

        Likewise, you might think the "myClient" alias should keep the FollowTail session alive even when there's nothing POEy going on in that session.

        In most cases that's true, but there's an exception: POE sessions will be terminated if every active session is waiting for an event from some other session. In POE terms, when all remaining sessions are kept alive only by an alias, then none of them will wake up to send an event. POE::Kernel recognizes this deadlock and signals the remaining sessions that they're idle. It literally sends a fake SIGIDLE signal to every session.

        If no session wakes up after SIGIDLE is broadcast, then POE::Kernel broadcasts one more fake signal, SIGZOMBIE. This kills off the sessions, and POE::Kernel->run() exits normally.

        If you export POE_TRACE_EVENTS=1 or export POE_TRACE_SIGNALS=1, you should see this happen.