Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling

Comment on

( #3333=superdoc: print w/replies, xml ) Need Help??
POE is the Perl Object Environment, a cooperative multitasking framework that makes it easy to answer questions like How do I make STDIN time out.

I only started looking at POE this week, because the Documentation for POE? thread piqued my interest. That thread points to several good tutorials on POE,but I thought we should have one here as well. Also, putting this together is helping me figure POE a bit better, and giving me ideas for more fun areas to explore. I think POE and (Tk|Curses) will be next...

POE lets you write programs that handle input from multiple asynchronous sources. Those are big words, but they just mean you'll get the info when it's available, and you don't have to worry about waiting for it. :)

The cooperation is done by creating a set of states, which are invoked by events. Events are generated by input engines (called Wheels), by timers, or by other states.

At the heart of POE lies to POE kernel. It keeps a queue of timed events, and uses the OS's select or poll functionality to watch any file handles or sockets you're interested in for activity. When it's time for an event to fire, or data is available, the associated state handler is invoked. Other event loops are also available, making it possible to have POE programs that have Tk or curses GUIs, for instance.

The sample this tutorial is built around is my answer to the STDIN timeout question asked above. You'll see that it's very easy to write an interactive application in POE with command-line editing and history.

The first step in any POE program is using the POE module. Since POE programs often need to use other modules from the POE:: namespace, you can do

	use POE qw/Wheel::ReadLine Component::Client::HTTP::SSL/;
as a shortcut for
	use POE;
	use POE::Wheel::ReadLine;
	use POE::Component::Client::HTTP::SSL;

In this case, we only need POE::Wheel::ReadLine, which will handle our input requirements.

Each program consists of one or more POE Sessions, which hold a set of states.

#!/usr/bin/perl -w use strict; # Use POE! use POE qw/Wheel::ReadLine/; # Need to do this on Cygwin #$/="\n\r"; # flush STDOUT automatically $|++; POE::Session->create( inline_states => { _start => \&handler_start, gotLine => \&handler_gotLine, prompt => \&handler_prompt, timeout => \&handler_timeout, #_stop => \&handler_stop, } );
In the session's constructor, we specify a hash of state names, and the state handlers associated with them. The subroutines can be named, as they are here, or they can be anonymous sub references.

The _start and _stop states are special; they are invoked by the kernel when the session is created, or just before it's destroyed.

In this case, we don't need to do anything special to handle _stop, so that state is commented out, and the handler isn't implemented.

That means that your _start handler will be invoked before POE::Session->create returns.

The next step is to start the kernel running, and exit the program once it's done.

$poe_kernel->run(); exit;
$poe_kernel is exported by POE automatically.

Of course, we haven't DEFINED any state handlers yet, so our program won't even compile, let alone run.

Every POE state handler is passed a large number of arguments in @_. The most interesting ones are the heap, kernel, and session associated with this state.

The poe heap is just a scalar, which usually is used as a hash reference.

These values can be accessed using an array slice on @_ to initialize scope variables, or explicitly referred to as $_[HEAP], $_[KERNEL], or $_[SESSION]. POE exports HEAP, KERNEL, SESSION, and several other constants automatically.

The first handler we'll see is our start handler:

sub handler_start { my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION]; # POE::Wheel::ReadLine gets you terminal input with command line # history. whenever a line is input, the 'gotLine' event # will run $heap->{wheel} = POE::Wheel::ReadLine->new ( InputEvent => 'gotLine', ); # ask for the prompt event to get run next $kernel->yield('prompt'); }
POE's wheels are modules which handle the hassle of gluing outside event generators, like sockets or file handles, to POE states.

POE::Wheel::ReadLine is a special wheel which invokes states when data is input on the console. It also handles command line editing and history, with a bit of help from us.

Note that we save the wheel in our %{$heap} hash. Otherwise, the wheel would be immediately destroyed, since there would be no outstanding references to it. We'll use that trick later, when it's time to exit. For now, we just associate the wheel's InputEvent with our 'gotLine' state (handled by handler_gotLine). Then, we use "yield" to ask the kernel to schedule the prompt state as soon as possible.

sub handler_prompt { my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION]; print "You have 10 seconds to enter something, or I'll quit!$/"; $heap->{wheel}->get('Type fast: '); # this will make the timeout event fire in 10 seconds $kernel->delay('timeout',10); }
All this handler does is use the get method on our Wheel to prompt the user for input, and then schedule a timeout event in 10 seconds.

Even if this isn't the first time this handler is invoked, calling delay removes the old event, and schedules a new one 10 seconds out.

From here, things are in the hands of the kernel. If the user does nothing, in 10 seconds (or so, timeouts are approximate) the timeout state will be triggered:

sub handler_timeout { my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION]; # taunt (or inform) the user print "You took too long, game over$/"; # setting this to undef will make our Wheel get shutdown # with no wheel, and no pending delay event, the kernel # queue is empty resulting in shutdown $heap->{wheel}=undef; }
When the wheel member is undefined in handler_timeout, the wheel is destroyed, and since there are no pending events and no event sources, the kernel exits.

If the user does enter something, or hits Ctrl-C, the gotLine handler is called.

sub handler_gotLine { my ($kernel, $heap, $session, $arg, $exception) = @_[KERNEL, HEAP, SESSION, ARG0, ARG1]; if(defined $arg) { $heap->{wheel}->addhistory($arg); print "Very good, you entered '$arg'. You get another 10 seconds. +$/"; } else { print "Got input exception '$exception'. Exiting...$/"; # setting this to undef will make our Wheel get shutdown $heap->{wheel}=undef; # setting a delay without a timeout number clears the event $kernel->delay('timeout'); return; } # run the prompt event again to reset timeout and print # new prompt $kernel->yield('prompt'); }
One thing that's interesting here is that we read the ARG0 and ARG1 members of @_. POE passes the arguments last in @_, ranging from ARG0 to $#_. In the case of an InputHandler for this wheel, ARG0 is the text input, and if ARG0 is undef, ARG1 is the exception code.

After "handling" the input, this handler yields back to the prompt handler, which reschedules the timeout and prompts the user again.

I hope this quick walkthru of a simple POE program will help you understand how POE operates. The other tutorials and beginners guides I linked up above are even better, so you should be up and running from state to state in no time. :)

Edit by tye, add READMORE

In reply to An introduction to POE by RMGir

Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":

  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?

    What's my password?
    Create A New User
    [choroba]: Yeah, my wife sent me some pictures of the children yesterday, and there was snow all around. 10 days in Saigon and I totally forgot what weather they have back there.
    [stevieb]: We've had so much snow here this year that the whole area is pretty much under constant avalanche warnings (and avalanches pretty much daily all over the place)

    How do I use this? | Other CB clients
    Other Users?
    Others surveying the Monastery: (15)
    As of 2017-02-23 17:17 GMT
    Find Nodes?
      Voting Booth?
      Before electricity was invented, what was the Electric Eel called?

      Results (350 votes). Check out past polls.