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

I am looking for a perl code that reads/writes to a data file, every time any other program tries to read/write from that file the reading/writing program will block until the first will complete

To make things simpler I am currently trying to have a program which reads a file , sort the file and pop the first line.

For e.g , input file is: (called "a_file")

8 7 6 5 4 3 2 1

If 3 processes are running in parallel or serially, the final file will be:

1 2 3 4 5

I wrote:

#! /usr/local/bin/perl chomp($pid = $$); $LOCK_EXCLUSIVE = 2; $UNLOCK = 8; $fifo = "ENV{HOME}/a_file"; popup($fifo); sub popup { my($fifo) = @_; while (! -e "$fifo.lock") { open (LOCK, "> $fifo.lock") || die "problem opening $f +ifo\n"; flock LOCK , $LOCK_EXCLUSIVE; print LOCK $pid; open (FILE, "< $fifo") || die "problem opening $fifo\n +"; @lines = <FILE>; close(FILE) || warn "update queue file exited $?\n";; open (FILE, "> $fifo") || die "problem opening $fifo\n +"; @sorted = sort @lines; $pop = shift @sorted; print "$hostname is poping $pop\n"; print FILE "@sorted\n"; close(FILE) || warn "update queue file exited $?\n";; close(LOCK) || warn "lock file exited $?\n";; $str=`cat $fifo.lock`; flock LOCK, $UNLOCK; } if ($pid eq $str) { print "PID lock file is going to be removed\n" ; system("rm $fifo.lock"); } }

But it looks that while I running in parallel on 3 different hosts it does not work as I expect , I get:

linux24 is poping PID lock file is going to be removed linux28 is poping PID lock file is going to be removed

Also I found this code over the web but I fail to run it , it get into infinite loop –

chdir; # go home $FIFO = '.signature'; $ENV{PATH} .= ":/etc:/usr/games"; while (1) { unless (-p $FIFO) { unlink $FIFO; system('mknod', $FIFO, 'p') && die "can't mknod $FIFO: $!"; } # next line blocks until there's a reader open (FIFO, "> $FIFO") || die "can't write $FIFO: $!"; print FIFO "John Smith (smith\@host.org\n"; close FIFO; sleep 2; # to avoid dup signals }

Any idea? I will highly appreciate if you can advise.

Janitored by Corion: Added formatting, code tags, as per Writeup Formatting Tips

GrandFather added readmore tags

Replies are listed 'Best First'.
Re: mkfifo/mknode
by jwkrahn (Abbot) on Jun 14, 2006 at 10:35 UTC
    You should use the warnings and strict pragmas so the second and third lines of your program should be:
    use warnings; use strict;
    chomp($pid = $$);
    $$ does not contain a newline so there is no point in using chomp().
    $LOCK_EXCLUSIVE = 2; $UNLOCK = 8;
    You should use the constants from the Fcntl module:
    use Fcntl ':flock';
    $fifo = "ENV{HOME}/a_file";
    You probably meant to use the %ENV hash there:
    $fifo = "$ENV{HOME}/a_file";
    close(FILE) || warn "update queue file exited $?\n";
    The $? variable will only contain useful information if you are running an external program. which you are not. However, you should include the $! variable in any system error messages.
    close(LOCK) || warn "lock file exited $?\n";; $str=`cat $fifo.lock`; flock LOCK, $UNLOCK;
    close(LOCK) will also unlock the file so the subsequent attempt to unlock it is superfluous.
    system("rm $fifo.lock");
    Is there a reason that you couldn't use the built-in unlink function?

    Have you read the entries in perlfaq5 on file locking?

    You are locking the PID file but not the data file. This may work better:

    #!/usr/local/bin/perl use warnings; use strict; use Fcntl ':flock'; use Tie::File; my $fifo = "$ENV{HOME}/a_file"; my $lock = "$fifo.lock"; tie( my @pid, 'Tie::File', $lock )->flock( LOCK_EX ) or die "Cannot open '$lock' $!"; @pid = $$; tie( my @lines, 'Tie::File', $fifo )->flock( LOCK_EX ) or die "Cannot open '$fifo' $!"; ( my $pop, @lines ) = sort @lines; print "$hostname is poping $pop\n"; untie @lines; print "PID lock file is going to be removed\n"; untie @pid; unlink $lock or die "Cannot unlink '$lock' $!"; __END__

      First thanks for your detailed reply. I used the following code but there is a problem with the flock package -

      Usage: Fcntl::constant(name, arg) at Fcntl.pm line 225.

      Can you please help ?

      azaria

      -------------------------------------------------------

      #! /usr/local/bin/perl chomp($hostname=`hostname`); chomp($date = `date \'\+\%y\%m\%d\%H\%M\%S\'`); use warnings; use strict; use Fcntl ':flock'; use Tie::File; my $fifo = "$ENV{HOME}/a_file"; my $lock = "$fifo.lock"; tie( my @pid, 'Tie::File', $lock )->flock( LOCK_EX ) or die "Cannot open '$lock' $!"; @pid = $$; tie( my @lines, 'Tie::File', $fifo )->flock( LOCK_EX ) or die "Cannot open '$fifo' $!"; ( my $pop, @lines ) = sort @lines; print "$hostname is poping $pop\n"; untie @lines; print "PID lock file is going to be removed\n"; untie @pid; unlink $lock or die "Cannot unlink '$lock' $!"; __END__

      Janitored by Corion: Added formatting, code tags, as per Writeup Formatting Tips

        I used the following code but there is a problem with the flock package -
        Usage: Fcntl::constant(name, arg) at Fcntl.pm line 225.
        The Fcntl module that I am using does not have any "usage" messages in it however the Tie::File module does. Which version of the Tie::File module are you using? You can find out like this:
        $ perl -MTie::File -le'print $Tie::File::VERSION' 0.97
        If the Fcntl module does not work you could go back to using:
        my $LOCK_EX = 2;
        And change all occurances of LOCK_EX to $LOCK_EX.

        Also, to get the hostname you could use the Sys::Hostname module:

        use Sys::Hostname; my $hostname = hostname;
        And to get a formatted date you could use the POSIX module:
        use POSIX 'strftime'; my $date = strftime '%y%m%d%H%M%S', localtime;
Re: mkfifo/mknode
by Moron (Curate) on Jun 14, 2006 at 09:51 UTC
    I use flock for this type of locking and it always worked like a dream for me.

    Update: What I meant to say is that I use flock on the file itself to be locked, not on a separate lock file - it doesn't quite work like that.

    I have done what you are doing on occasion, but in that case I opened the lock file '+<', did the flock and then a seek 0 and (over-) wrote the pid for testing, though I left that in there for support purposes.

    -M

    Free your mind

      Hello Moron, I tried to use the flock function but i got the error - Usage: Fcntl::constant(name, arg) at Fcntl.pm line 225. Do you have any simple example ? Thanks
        Here is a simple example of using a dedicated lock file
        my $LOCK_EX = 2; # exclusive lock on file my $LOCK_UN = 8; # unlock file my $lock_file = $ENV{ XYZ_DIR } . "/$0.lock"; open my $lh, ">$lock_file" || die "Can't open lock file $lock_file: $! +"; flock $lh, $LOCK_EX; # will wait for exclusive lock # on lock file # do the exclusive actions flock $lh, $LOCK_UN; close $lh;

        -M

        Free your mind

A reply falls below the community's threshold of quality. You may see it by logging in.