Search code examples
multithreadingperlpoe

Perl POE::Wheel::FollowTail running in a thread not modifying global variables


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?


Solution

  • 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.