package DB_File::Lock2; require 5.004; use strict; BEGIN { # RCS/CVS compliant: must be all one line, for MakeMaker $DB_File::Lock2::VERSION = do { my @r = (q$Revision: 1.1 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; } use DB_File (); use Fcntl qw(:flock O_RDWR O_CREAT); use Carp qw(croak carp verbose); use Symbol (); @DB_File::Lock2::ISA = qw( DB_File ); %DB_File::Lock2::lockfhs = (); use constant DEBUG => 0; # file creation permissions mode use constant PERM_MODE => 0660; # file locking modes %DB_File::Lock2::locks = ( read => LOCK_SH, write => LOCK_EX, ); # SYNOPSIS: # tie my %mydb, 'DB_File::Lock2', $filepath, # ['read' || 'write', 'HASH' || 'BTREE'] # while (my($k,$v) = each %mydb) { # print "$k => $v\n"; # } # untie %mydb; ######### sub TIEHASH { my $class = shift; my $file = shift; my $lock_mode = lc shift || 'read'; my $db_type = shift || 'HASH'; die "Dunno about lock mode: [$lock_mode].\n Valid modes are 'read' or 'write'.\n" unless $lock_mode eq 'read' or $lock_mode eq 'write'; # Critical section starts here if in write mode! # create an external lock my $lockfh = Symbol::gensym(); open $lockfh, ">$file.lock" or die "Cannot open $file.lock for writing: $!\n"; unless (flock $lockfh, $DB_File::Lock2::locks{$lock_mode}) { croak "cannot flock: $lock_mode => $DB_File::Lock2::locks{$lock_mode}: $!\n"; } my $self = $class->SUPER::TIEHASH ($file, O_RDWR|O_CREAT, PERM_MODE, ($db_type eq 'BTREE' ? $DB_File::DB_BTREE : $DB_File::DB_HASH ) ); # remove the package name in case re-blessing occurs (my $id = "$self") =~ s/^[^=]+=//; # cache the lock fh $DB_File::Lock2::lockfhs{$id} = $lockfh; return $self; } # end of sub new # DESTROY is automatically called when a tied variable # goes out of scope, on explicit untie() or when the program is # interrupted, e.g. with a die() call. # # It unties the db by forwarding it to the parent class, # unlocks the file and removes it from the cache of locks. ########### sub DESTROY{ my $self = shift; $self->SUPER::DESTROY(@_); # now it safe to unlock the file, (close() unlocks as well). Since # the object has gone we remove its lock filehandler entry # from the cache. (my $id = "$self") =~ s/^[^=]+=//; # see 'sub TIEHASH' close delete $DB_File::Lock2::lockfhs{$id}; # Critical section ends here if in write mode! print "Destroying ".__PACKAGE__."\n" if DEBUG; } #### END { print "Calling the END from ".__PACKAGE__."\n" if DEBUG; } 1;