TStanley has asked for the wisdom of the Perl Monks concerning the following question:

After reviewing the presentation that Dominus gave to the Philadelphia PM group, I decided to revisit a module I had written, and implement some better file locking procedures. Here is the module so far:
package Logger::Simple; use strict; use Carp; use FileHandle; use Fcntl qw(:flock); use vars qw /$VERSION $SEM/; $VERSION='1.06'; $SEM = ".LS.lock"; sub new{ my($class,%args)=@_; my $self = bless{LOG => $args{LOG} || croak"No logfile!\n", CARP => $args{CARP} || undef, FILEHANDLE => new FileHandle || croak"Unable to ge +t filehandle\n", SEMAPHORE => new FileHandle || croak"Unable to cr +eate semaphore filehandle\n", ERROR => "", HISTORY => [], },$class; if(! $self->open()){ die"Unable to open $self->{LOG}\n"; } return $self; } sub open{ my $self=shift; if(! open($$self{FILEHANDLE},">>$$self{LOG}")){ $self->set("Unable to open logfile\n"); return 0; } $$self{FILEHANDLE}->autoflush(1); return 1; } sub write{ my($self,$msg)=@_; my $FH=*{$$self{FILEHANDLE}}; my $format="$0 : [".scalar (localtime)."] $msg"; $self->lock(); if(! print $FH "$format\n"){ $self->set("Unable to write to $$self{LOG}: $!\n"); } $self->unlock(); } sub message{ my $self=shift; if(wantarray){ my @messages=@{$$self{HISTORY}}; return @messages; }else{ my $message=$$self{ERROR}; return $message; } } sub clear{ my $self=shift; $$self{ERROR}=undef; } sub set{ my ($self,$error)=@_; $self->clear; $$self{ERROR}=$error; push @{$$self{HISTORY}},$error; carp "$error\n" if $$self{CARP}; $self->write($error); } sub print_object{ require Data::Dumper; my $self=shift; print Data::Dumper->Dumper($self); } sub lock{ my $self=shift; my $FH=*{$$self{SEMAPHORE}}; if(-e $SEM){ flock($FH,LOCK_NB) or carp"Can't obtain file lock: $!\n"; open($FH,">$SEM")||die"Can't create lock file: $!\n"; flock($FH,LOCK_EX) or die"Can't obtain file lock: $!\n"; }else{ open $FH,">$SEM"||die"Can't create lock file: $!\n"; flock($FH,LOCK_EX) or die"Can't obtain file lock: $!\n"; } } sub unlock{ my $self=shift; my $FH=*{$$self{SEMAPHORE}}; if(-e $SEM){ flock(S,LOCK_UN); close S; unlink $SEM; } } 1;
By using cron to run a couple of simple scripts that use the same log file to write to, I notice that the write statement from the second script are intermingled with those from the first inside the log file, which is fine and is doing what I expected. However, if I start the two scripts from a shell script (it runs the first script in the background and starts the second one), I get the following errors:
Ambiguous call resolved as CORE::open(), qualify as such or use & at / +opt/perl5/ lib/site_perl/5.6.0/Logger/Simple.pm line 88. Ambiguous call resolved as CORE::open(), qualify as such or use & at / +opt/perl5/ lib/site_perl/5.6.0/Logger/Simple.pm line 88. Ambiguous call resolved as CORE::open(), qualify as such or use & at / +opt/perl5/ lib/site_perl/5.6.0/Logger/Simple.pm line 91. Ambiguous call resolved as CORE::open(), qualify as such or use & at / +opt/perl5/ lib/site_perl/5.6.0/Logger/Simple.pm line 91. Use of uninitialized value in ref-to-glob cast at /opt/perl5/lib/site_ +perl/5.6.0 /Logger/Simple.pm line 85. Use of uninitialized value in ref-to-glob cast at /opt/perl5/lib/site_ +perl/5.6.0 /Logger/Simple.pm line 85. flock() on closed filehandle main:: at /opt/perl5/lib/site_perl/5.6.0/ +Logger/Sim ple.pm line 87. more lines follow ...
Here are the scripts in question:
#!/opt/perl5/bin/perl # locker.pl use strict; use Logger::Simple; my $L=Logger::Simple->new(LOG=>"file.txt",CARP=>'1'); for(1..10){ $L->write("Writing");} #!/opt/perl5/bin/perl -w # writer.pl use strict; use Logger::Simple; my $L=Logger::Simple->new(LOG=>"file.txt",CARP=>'1') for(1..5){ $L->write("Getting through?"); } #!/bin/sh # runner.sh ./locker.pl & ./writer.pl
To note something however, the behavior of the scripts is the same as far as writing into the log file. How can I get rid of these warning messages (other than run without warnings)?

TStanley
--------
It is God's job to forgive Osama Bin Laden. It is our job to arrange the meeting -- General Norman Schwartzkopf

Replies are listed 'Best First'.
Re: File locking with semaphores
by jasonk (Parson) on Mar 07, 2003 at 19:00 UTC

    Your module defines a subroutine with the same name as a core function, which causes ambiguity. To get rid of the error you need to do what the message tells, you call CORE::open() instead of just open() so that perl doesn't have to guess if you mean CORE::open() or Logger::Simple::open().

Re: File locking with semaphores
by TStanley (Canon) on Mar 07, 2003 at 19:58 UTC
    I managed to spot another error with the above code. The unlock function should be:
    sub unlock{ my $self=shift; my $FH=*{$$self{SEMAPHORE}}; if(-e $SEM){ flock($FH,LOCK_UN); close $FH; unlink $SEM; } }
    That cleared up some errors that I was getting. And what jasonk suggested cleared up the ambiguous opens. I am still getting the following errors:
    Use of uninitialized value in ref-to-glob cast at /opt/perl5/lib/site_ +perl/5.6.0 /Logger/Simple.pm line 85. Use of uninitialized value in ref-to-glob cast at /opt/perl5/lib/site_ +perl/5.6.0 /Logger/Simple.pm line 98.

    TStanley
    --------
    It is God's job to forgive Osama Bin Laden. It is our job to arrange the meeting -- General Norman Schwartzkopf
      I still don't get why you are dereferencing the FileHandle object. It works just fine as is (maybe depends on perl version?). I don't understand what goes on when you dereference the FileHandle object, but this code seems to demonstrate that you get a different GLOB every time, which I would think is not what you want:
      use strict; use warnings; use FileHandle; my $fh = new FileHandle; my $FH1 = *{$$fh}; my $fh1 = \$FH1; my $FH2 = *{$$fh}; my $fh2 = \$FH2; print "$fh1\n"; print "$fh2\n"; ############ output: GLOB(0x20043704) GLOB(0x200436f8)
Re: File locking with semaphores
by runrig (Abbot) on Mar 07, 2003 at 19:20 UTC
    I don't see where the semaphore is set to any actual file. It seems like two different logger objects logging to the same log file would have two different semaphores and so defeat the purpose of locking anything. Wouldn't it be simpler to just lock the log file itself? (Though I can see the need for a common semaphore file if logs are cycled and you want log times to be continuous between cycled log files). Also, I don't get the need for:
    my $FH=*{$$self{FILEHANDLE}};
    And another way to get rid of the open warning is with:
    $self->{FILEHANDLE}->open(">>$logfile") or ...
    Update: Ah, just noticed the package $SEM variable. Nevermind on the first issue...(though you are assuming that all logging processes are in the same current directory opening the same semaphore file, maybe there ought to be a semaphore directory argument, or default to the same directory as the logfile, or ???).