1

In this program POE::Wheel::FollowTail works well for following the tail of a file, it is also running in a separate thread to simply monitor the progress of a compile job.
Inside the InputEvent handler there's a crude regex to extract compile results, and there everything is working fine, but I cannot get any result values to be accessible outside this sub. Even if I put result variables in the global scope they are not modified.

The program consists of one process running the compile job, another watching the log, and the main loop waiting.

Global scope:

my $Pass = 0;
my $Done = 0;

Then to kick off the monitoring:

threads->create(\&StartWatcher);

Where the watch-log file sub looks like this:

sub StartWatcher
{
my $logfile = "filename.log";

# Create the logfile watcher
POE::Session->create
    (
    inline_states => 
        {
        _start => sub 
            {
            $_[HEAP]{tailor} = POE::Wheel::FollowTail->new( Filename => $logfile, InputEvent => "got_log_line", );
            },
        got_log_line => sub 
            {
            $Pass += () = $_[ARG0] =~ /^\d+.*vcproj \- 0 error\(s\), \d+ warning\(s\)/g;
            $Done += () = $_[ARG0] =~ /^\d+.*vcproj \- \d+ error\(s\), \d+ warning\(s\)/g;
            print "POE InputEvent Pass: $Pass, Done: $Done\n"; # Debug output
            },
        }
    );

POE::Kernel->run();
}

The $logfile is being written by a Visual Studio compile job started using Win32::Process::Create and the main Perl execution is sitting in this loop waiting for the compiler to terminate, and producing a status output every second.

    while('true')
    {
    $ProcessObj->Wait(100);  # milliseconds wait
    $ProcessObj->GetExitCode($exitcode);
    if ( $exitcode == STILL_ACTIVE ) 
        {
        "Compiling... [$Done/$Count]  Pass: $Pass  Failed: $failed" 
            if($RunCounter++ % 10 == 0);
        next;
        }
    last;
    }

The output produced is similar to this:

POE InputEvent Pass: 1, Done: 1
Compiling... [0/91]  Pass: 0  Failed: 0                           

ie. in the InputEvent handler got_log_line the two global variables have been incremented, yet in the Perl main loop they are still at zero. I realise that I could do the print output from the InputEvent handler but why doesn't it modify global variables?

What is going wrong?

0xDEADBEEF
  • 808
  • 1
  • 11
  • 19
  • Of course as soon as you ask the world the answer becomes more apparent: Perhaps variable scoping in POE has something to do with this: http://stackoverflow.com/questions/1064273/how-does-variable-scoping-work-in-a-poe-session – 0xDEADBEEF Jun 22 '11 at 09:40

2 Answers2

5

Threading in perl doesn't work in same way as in other languages, the program space is not shared. In thread creation, current thread is copied into new one, which separated from the parent one (each thread has it's own instrance of perl interpret). If you want to communicate between the threads, look at threads::shared, Thread::Queue and Thread::Semaphore.

anydot
  • 1,499
  • 9
  • 14
0

Following from anydot's suggestion above here's the answer:

Before starting the thread, create a shared queue

use threads;
use Thread::Queue;
use threads::shared;
my $queue:shared = Thread::Queue->new();

In the input event, create some shared data to enqueue

        my %data:shared = ();

        $data{PASS_VCPRJ_COUNT} = () = $_[ARG0] =~ /^\d+.*vcproj.*0 error.*\d+ warning/g;
        $data{DONE_VCPRJ_COUNT} = () = $_[ARG0] =~ /^\d+.*vcproj.*d+ error.*\d+ warning/g;
        $queue->enqueue(\%data) if($data{DONE_VCPRJ_COUNT} ne 0 || $data{PASS_VCPRJ_COUNT} ne 0);

Then in the screen update code, dequeue it, here non-blocking

if (defined(my $item = $queue->dequeue_nb()))
    {
    foreach my $key(%$item)
    {print "$key       $item->{$key}\n";}
    }

There are other ways, I'm sure but this works for me. Many thanks.

0xDEADBEEF
  • 808
  • 1
  • 11
  • 19