Multitasking

The counter we wrote can multitask. Each instance holds its own accumulator in its own HEAP. Each session's events pass through POE::Kernel's queue, and they are dispatched in first-in/first-out order. This forces each session to take turns.

To illustrate this happening, we'll change the previous program to run two sessions at once. The rest of the program will remain the same.

for ( 1 .. 2 ) {
    POE::Session->create(
        inline_states => {
            _start => \&session_start,
            _stop  => \&session_stop,
            count  => \&session_count,
          }
    );
}

And here's the modified program's output.

  Session 2 has started.
  Session 3 has started.
  Starting POE::Kernel.
  Session 2 has counted to 1.
  Session 3 has counted to 1.
  Session 2 has counted to 2.
  Session 3 has counted to 2.
  Session 2 has counted to 3.
  Session 3 has counted to 3.
  Session 2 has counted to 4.
  Session 3 has counted to 4.
  Session 2 has counted to 5.
  Session 3 has counted to 5.
  Session 2 has counted to 6.
  Session 3 has counted to 6.
  Session 2 has counted to 7.
  Session 3 has counted to 7.
  Session 2 has counted to 8.
  Session 3 has counted to 8.
  Session 2 has counted to 9.
  Session 3 has counted to 9.
  Session 2 has counted to 10.
  Session 2 has stopped.
  Session 3 has counted to 10.
  Session 3 has stopped.
  POE::Kernel's run() method returned.

So, then, what's going on here?

Each session is maintaining its own count in its own $_[HEAP]. This happens no matter how many instances are created.

POE runs each event handler in turn. Only one handler may run at a time, so most locking and synchronization issues are implicitly taken care of. POE::Kernel itself is suspended while event handlers run, and not even signals may be dispatched until an event handler returns.

Events for every session are passed through a master queue. Events are dispatched from the head of this queue, and new events are placed at its tail. This ensures that sessions take turns.

POE::Kernel's run() method returns after the last session has stopped.

This is the full listing:

#!/usr/bin/perl
use warnings;
use strict;
use POE;
for (1 .. 2) {
  POE::Session->create(
    inline_states => {
      _start => \&session_start,
      _stop  => \&session_stop,
      count  => \&session_count,
    }
  );
}
print "Starting POE::Kernel.\n";
POE::Kernel->run();
print "POE::Kernel's run() method returned.\n";
exit;

sub session_start {
  print "Session ", $_[SESSION]->ID, " has started.\n";
  $_[HEAP]->{count} = 0;
  $_[KERNEL]->yield("count");
}

sub session_stop {
  print "Session ", $_[SESSION]->ID, " has stopped.\n";
}

sub session_count {
  my ($kernel, $heap) = @_[KERNEL, HEAP];
  my $session_id = $_[SESSION]->ID;
  my $count      = ++$heap->{count};
  print "Session $session_id has counted to $count.\n";
  $kernel->yield("count") if $count < 10;
}

Casiano Rodríguez León
2012-02-29