4

Thanks to some help, I've started attempting to get thread-safe logging working for my script - however, I don't seem to have it working correctly:

use Fcntl ':flock';

no warnings 'redefine';
sub IO::Tee::PRINT
{
    my $self = shift;
    my $ret = 1;
    foreach my $fh (@$self) {
        flock($fh, LOCK_EX);
        print "\n\t\ttestA\n";  #<-- added for testing
        undef $ret unless print $fh @_;
        flock($fh, LOCK_UN);
        print "\t\ttestB\n";    #<-- added for testing
    }
    return $ret;
}

my $Info_tee = IO::Tee->new(\*STDOUT, ">$ENV{DOM}\\build.log");

When I get to the threaded section of my script:

print $Info_tee "\n------------------------------------------------------\n";
print $Info_tee "\n\t\t*** Performing Action: \'$cmd\' on $comp ***";

My output to STDOUT (4 threads) is:

                testA
                testA

------------------------------------------------------
                testB

                testA

------------------------------------------------------
                testB

------------------------------------------------------
                testB

                testA

                testA

------------------------------------------------------
                testB

.. and then the script locks up. What am I doing wrong?

Edit: I've created a simple example of my issue here -- I noticed that if you remove the queue from the script, everything seems to work as designed.

Community
  • 1
  • 1
MrDuk
  • 16,578
  • 18
  • 74
  • 133

3 Answers3

1

The easy way

Just use an explicit mutex around your shared resource. That's clear, concise, and easy:

use threads;
use threads::shared;

my $mutex : shared; # will synchronize access to our shared IO::Tee resource

async {
  ...
  { lock $mutex; print $shared_resource "I did something"; }
  ...
}

Note, your synchronization will need to be a mutex or some other threading construct. flock and friends were designed for interprocess locking, and generally don't have desirable multithreaded semantics.

The harder way

The alternative approach you seem to want, viz., to redefine or to extend IO::Tee to support an implicit mutex, is inobvious, verbose, and more challenging.

Why? IO::Tee objects carry state and were not designed for multithreaded use. (That is, simply synchronizing PRINT/PRINTF calls may not be enough.) Each object is really two objects tie()d together, one a GLOB and one ARRAY slotted in that glob. Globs can't be shared under perl threads, and arrayrefs are cleared when shared. It's not intractable, of course, but the amount of work seems disproportionately large to simply using an explicit mutex to control thread access to a shared resource.

Community
  • 1
  • 1
pilcrow
  • 56,591
  • 13
  • 94
  • 135
0

You're unclear as usual.


If the problem you're having is that the script locks up, it's not from anything you posted.


If the problem you posted is that stuff is being printed after the lock has been released, it's because you need to flush the buffers before releasing the lock.

STDOUT->flush();
$fh->flush();

If the problem is that you're getting the following, it's because you don't hold the lock long enough (as you've previously been told).

------------------------------------------------------
------------------------------------------------------
*** Performing Action: 'action' on comp1 ***
*** Performing Action: 'action' on comp2 ***
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • So, `flock($fh, LOCK_EX); --- undef $ret unless print $fh @_; --- STDOUT->flush(); --- $fh->flush(); --- flock($fh, LOCK_UN);` ? I only ask because this appears to do the same thing. – MrDuk Apr 24 '14 at 17:39
  • Yes. TestB is particularly useless where it is. It should be before the flush before the unlock. – ikegami Apr 24 '14 at 17:42
  • I'm ok with jumbled output (I can sort that out at the end I think) - but the script is still stopping abruptly as before (i.e., all I get is the '-------' line, and not the '*** Performing Action: ... ' line – MrDuk Apr 24 '14 at 17:45
  • As a test, I put `print "\n\t\t*** Performing Action: \'$cmd\' on $comp ***" ;` after the `$Info_tee` line - still no output after the `-----` portion though – MrDuk Apr 24 '14 at 17:47
  • 2
    As I ask you every time you post a question: please provide a demonstration of your problem. – ikegami Apr 24 '14 at 17:51
  • I noticed in the demo I posted that [if you remove the que](https://gist.github.com/anonymous/11269063) and just use threads directly, the output seems fine.. – MrDuk Apr 24 '14 at 20:51
  • 1
    Can't look at this before Tuesday. – ikegami Apr 25 '14 at 04:38
0

You could avoid reinveting the wheel. There are many logging modules that are supports this out-of-the-box.

One example is Log4perl: perl: Will Log::Log4perl work in a multi-threaded environment?

http://search.cpan.org/dist/Log-Log4perl/lib/Log/Log4perl/FAQ.pm#How_can_I_run_Log%3a%3aLog4perl_under_mod_perl?

Regards

Community
  • 1
  • 1
user1126070
  • 5,059
  • 1
  • 16
  • 15
  • This approach is very good, but I think has problems with Win32 platforms – Kostia Shiian May 01 '14 at 05:22
  • Log$perl works under windows but I do not know if this appender is works under windows or not. I will try it later to confirm. – user1126070 May 05 '14 at 08:33
  • I cannot get `Log4perl-Appender-Synchronized` to install on Windows. I've tried `ppm install Log-Log4perl-Appender-Synchronized`, but it says "Can't find any package that provides.." – MrDuk May 05 '14 at 16:48