Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

POE yield not working

by SoulStyle (Initiate)
on Feb 02, 2013 at 19:20 UTC ( [id://1016739]=perlquestion: print w/replies, xml ) Need Help??

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

Hi Monks,

I'm currently stuck with a piece of POE code. Here is a slightly stripped versions:
#!perl use warnings; use strict; use diagnostics; use IO::Socket; use POE qw(Wheel::SocketFactory Wheel::ReadWrite); my $SERVER_ADDR = '192.168.178.6'; my $SERVER_PORT = '50099'; POE::Session->create( inline_states => { _start => sub { # Start the server. $_[HEAP]{server} = POE::Wheel::SocketFactory->new( RemoteAddress => $SERVER_ADDR, RemotePort => $SERVER_PORT, SocketProtocol => 'udp', SuccessEvent => "on_connect", FailureEvent => "on_server_error", ); }, on_connect => sub { # Begin interacting with the server. my $kernel = $_[KERNEL]; my $client_socket = $_[ARG0]; my $io_wheel = POE::Wheel::ReadWrite->new( Handle => $client_socket, InputEvent => "on_receive_data", ErrorEvent => "on_connect_error", ); $_[HEAP]{client}{ $io_wheel->ID() } = $io_wheel; $io_wheel->put( "login monitor monitor", "log on", ); $kernel->yield('keepalive', $io_wheel, $kernel); }, on_server_error => sub { # Shut down server. my ( $operation, $errnum, $errstr ) = @_[ ARG0, ARG1, ARG2 + ]; warn "Server $operation error $errnum: $errstr\n"; delete $_[HEAP]{server}; }, on_receive_data => sub { # Handle client input. my ( $kernel, $input, $wheel_id ) = @_[ KERNEL, ARG0, ARG1 + ]; print "Received: $input\n"; }, on_connect_error => sub { # Handle client error, including disconnect. my $wheel_id = $_[ARG3]; delete $_[HEAP]{client}{$wheel_id}; }, keepalive => sub { my ( $io_wheel, $kernel ) = @_[ ARG0, ARG1 ]; $io_wheel->put( "keepalive" ); $kernel->delay_add( 'keepalive' => 10 ); }, } ); POE::Kernel->run(); exit;
My intention is to create a timer that calls
keepalive => sub { my ( $io_wheel, $kernel ) = @_[ ARG0, ARG1 ]; $io_wheel->put( "keepalive" ); $kernel->delay_add( 'keepalive' => 10 );
every 10 seconds. The first call to this sub works and the server, to which this client connects, sends the expected answer. After 10 seconds the sub is called again and it always ends up with:
Uncaught exception from user code: Can't call method "put" on an undefined value at fac.pl line 5 +9. at /usr/local/share/perl/5.14.2/POE/Kernel.pm line 1256 POE::Kernel::_rethrow_kr_exception('POE::Kernel=ARRAY(0x88f2e8 +)') called at /usr/local/share/perl/5.14.2/POE/Kernel.pm line 1244 POE::Kernel::run('POE::Kernel') called at fac.pl line 65
Any help would be really appreciated

Replies are listed 'Best First'.
Re: POE yield not working
by rcaputo (Chaplain) on Feb 02, 2013 at 20:27 UTC

    The reason the first call works is that you're passing parameters in your yield() call.

    ... $kernel->yield('keepalive', $io_wheel, $kernel); ....

    The keepalive callback collects those parameters here:

    ... keepalive => sub { my ( $io_wheel, $kernel ) = @_[ ARG0, ARG1 ]; ....

    The problem is that you're not continuing to pass those parameters in the delay. This might work better:

    keepalive => sub { my ( $io_wheel, $kernel ) = @_[ ARG0, KERNEL ]; $io_wheel->put( "keepalive" ); $kernel->delay( 'keepalive' => 10, $io_wheel ); },

    Notes:

    • You don't need to pass a POE::Kernel reference through, since that's given to you for free in every callback. I've removed it from delay(), and you should remove it from yield().
    • The rest of the code tracks wheels by their IDs. It might be more consistent and easier overall to pass the wheel ID instead of the reference. For example, if something destroys the object (deletes the wheel), the ID to object lookup will detect that it's gone away.

      Many thanks for this input, it is now working like intended. Since this little project is my first contact with POE please allow some more noobish questions:
      The rest of the code tracks wheels by their IDs. It might be more consistent and easier overall to pass the wheel ID instead of the reference. For example, if something destroys the object (deletes the wheel), the ID to object lookup will detect that it's gone away.
      Do you mean to rather use
      on_connect => sub { # Begin interacting with the server. my ( $kernel, $client_socket ) = @_[KERNEL, ARG0 ]; $_[HEAP]{client} = POE::Wheel::ReadWrite->new( Handle => $client_socket, InputEvent => "on_receive_data", ErrorEvent => "on_connect_error", ); $_[HEAP]{client}->put( "login monitor monitor", "log on", +); $kernel->yield( 'keepalive', $_[HEAP]{client} ); },
      instead of passing it to the variable $io_wheel?

      And in the same context, would you please be so kind as to elaborate following difference:

      $kernel->yield( 'keepalive', $_[HEAP]{client} );
      is used to address the ReadWrite instance.

      In the sub "on_connect_error" (see first post) following code is used to delete the wheel upon an error:

      delete $_[HEAP]{client}{$wheel_id};
      I don't understand the difference between these two. Still struggling with a lot aspects in POE and I appreciate each input.

        And in the same context, would you please be so kind as to elaborate following difference:

        $kernel->yield( 'keepalive', $_[HEAP]{client} );
        is used to address the ReadWrite instance.

        In the sub "on_connect_error" (see first post) following code is used to delete the wheel upon an error:

        delete $_[HEAP]{client}{$wheel_id};
        I don't understand the difference between these two. Still struggling with a lot aspects in POE and I appreciate each input.

        I think the first one is a typo. It refers to the hash of all clients rather than a particular client. It should probably be

        $kernel->yield('keepalive', $_[HEAP]{client}{$wheel_id});

        Regarding wheel ID vs. reference, I meant it might be better to pass around the client's wheel ID rather than a reference to the wheel itself. As in:

        $kernel->yield('keepalive', $io_wheel->ID, $kernel);
        and
        keepalive => sub { my ( $io_wheel_id, $kernel, $heap ) = @_[ ARG0, KERNEL ]; return unless exists $heap->{clients}{$io_wheel_id}; $heap->{clients}{$io_wheel_id}->put("keepalive"); $kernel->delay( 'keepalive' => 10, $io_wheel_id ); },

        It's more work, for you and for the program, but it stops repeating the timer after the client has disconnected. Of course, another way is to make sure that the timer is canceled wherever you are deleting the client. If you find yourself doing that in multiple places, refactor it into a subroutine like:

        sub delete_client { my ($kernel, $heap, $io_wheel_id) = @_; delete $heap->{clients}{$io_wheel_id}; $kernel->delay(keepalive => undef); }
        which would be called the usual Perlish way
        on_connect_error => sub { # Handle client error, including disconnect. my $wheel_id = $_[ARG3]; delete_client(@_[KERNEL, HEAP], $wheel_id); },
        It really helps when cleanup gets complex, as it often does when programs evolve and grow.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1016739]
Approved by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2024-03-29 16:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found