POE Logging.

Matts on 2002-01-15T10:56:05

I have to give a 30 minute talk on POE tomorrow here at work. I asked to do it, because I want to get people here thinking about cool new technologies (yes I know POE isn't exactly new) and ways we can make use of them. For example I've written some code to download a live email feed off one of our servers (just uses FTP), and then process those emails. Everything happens on separate "threads" (sessions in POE parlance), so it should all run quite smoothly.

One thing that bothers me a little about this is that I'm using PoCo::Logger (that I wrote), which alledgedly logs in the background, but instead I was surprised when I saw all the logging appear after all the emails had been processed. Then I realised this was just an error in my expectations... You see POE uses a queue for its scheduling, so what I'm doing in my code is queuing the files up for processing, by queuing one state per email, then in the processing code I do the logging, which also queue's up a state. However the logging states all go to the end of the queue.

So I need to think about this a little more.

I think what I can do is queue up a new state to download each new file. Then in that state queue up a new state to process the file. Then the logging, in that state, will appear linearly.

Either that or I need to fixup PoCo::Logger to not use POE states. But I don't really want to do that as it would lose some of its concept of threadedness.

Or, one other thing I could do is make logging a priority state. You can do that in POE by setting an alarm for the state to be executed at now - 1 second. I think that should make it happen next in the queue, but a) I need to check that will really happen, and b) should logging really be a high priority event?


Solved it

Matts on 2002-01-15T14:27:57

POE is neat, but you need to be careful you don't dispatch too many of the same events too often. I guess this is what you'd call a POE design pattern to watch out for...

So instead of:

  • sub process_files {
        my ($kernel, $files) = @_[KERNEL, ARG0];
        $kernel->yield('process_file', $_) for @$files;
    }

I now have:

  • sub process_files {
        my ($kernel, $files) = @_[KERNEL, ARG0];
        if (@$files) {
            my $file = shift @$files;
            $kernel->yield('process_file', $file);
            $kernel->yield('process_files', $files);
        }
    }

Which interleaves quite nicely thank you ;-)

Re:Solved it

jmm on 2002-01-15T14:39:54

I'm curious. In the original you had:

    $kernel->yield('process_file', $_) for @$files;

and in the revised version:

    $kernel->yield('process_files', $files);

Does yield take a list ref (and the for wrapper was not needed in the original) or is this a bug in the revised version?

Re:Solved it

Matts on 2002-01-15T16:10:13

First of all, you're looking at 'process_file' vs 'process_files'. So what the second version does is shift one file off the list, and call process_file on it, and then call process_files on the remaining files.

$files is an array ref, and $file is a scalar.

In POE, yield only cares about the first argument, which says which state to call. The rest of the arguments are passed in @_ and accessed via the ARGN aliases.