File Coverage

blib/lib/Tie/DB_FileLock.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             #!/usr/local/bin/perl -w
2             # Tie::DB_FileLock - an implementation of a tied, locking DB_File Hash.
3             # Tie::FileLock.pm 2000-01-10
4             # John M Vinopal jmv@cpan.org
5             #
6             # Copyright (C) 1998-2000, John M Vinopal, All Rights Reserved.
7             # This program is free software. Permission is granted to copy
8             # and modify this program so long as this copyright notice is
9             # preserved. This software is distributed without warranty.
10             # You can redistribute it and/or modify it under the same terms
11             # as Perl itself.
12             #
13             package Tie::DB_FileLock;
14 3     3   7557 use strict;
  3         5  
  3         153  
15              
16             require 5.004;
17             require Tie::Hash;
18 3     3   17 use Carp;
  3         4  
  3         341  
19 3     3   4811 use DB_File;
  0            
  0            
20             use FileHandle;
21             use Fcntl qw(:flock O_RDONLY O_RDWR O_CREAT);
22              
23             use vars qw(@ISA @EXPORT $VERSION $DEBUG);
24             @ISA = qw(Tie::Hash DB_File);
25             @EXPORT = @DB_File::EXPORT;
26             $VERSION = '0.11';
27             $DEBUG = 0;
28              
29             sub TIEHASH {
30             my $class = shift;
31             my ($dbname, $openmode, $perms, $type) = @_;
32              
33             # Typecheck the type, only HASH and BTREE.
34             if ($type and ref($type) eq "DB_File::RECNOINFO") {
35             croak "Tie::DB_FileLock can only tie an array to a DB_RECNO database\n";
36             }
37              
38             # Create the new hash object.
39             my $self = bless {}, $class;
40              
41             # Open and Initialize the dbm.
42             $self->_openDB(@_);
43             # Lock the dbm for the duration.
44             # XXX - Postpone lock until the first access?
45             $self->lockDB if ($dbname);
46              
47             return $self;
48             }
49              
50             # XXX - to support RECNO type
51             sub TIEARRAY {
52             my $class = shift;
53             my ($dbname, $openmode, $perms, $type) = @_;
54              
55             # Typecheck the type, only HASH and BTREE.
56             if ($type and ref($type) ne "DB_File::RECNOINFO") {
57             my $t = ref($type);
58             $t =~ s/DB_File::(\w+)INFO/$1/;
59             croak "Tie::DB_FileLock can only tie an associative array to a DB_$t database\n";
60             }
61              
62             croak "DB_RECNO not implemented";
63             }
64              
65             sub _openDB {
66             my $self = shift;
67             my $dbname = shift;
68             my ($openmode, $perms, $type) = @_;
69             my @params = @_;
70             my %db;
71              
72             # Default settings.
73             $openmode = O_CREAT | O_RDWR unless defined $openmode;
74              
75             # Obtain a tie to the DB Hash.
76             my $dbobj = tie(%db, 'DB_File', $dbname, @params);
77             croak "tie($dbname): $!" unless $dbobj;
78              
79             # Required on some OSes, else new files are not created and
80             # the subsequent locking calls fail. [Linux,Solaris,?]
81             $dbobj->sync();
82              
83             # Dup a filehandle to the hash object if not in-core db.
84             if ($dbname) {
85             my $lockmode;
86             my $fd = $dbobj->fd;
87             my $fh = FileHandle->new("<&=$fd") or croak("dup: $!");
88             $self->{LOCKFH} = $fh;
89              
90             # Determine type of locking.
91             if ($openmode == O_RDONLY) {
92             $lockmode = LOCK_SH;
93             } else {
94             $lockmode = LOCK_EX;
95             }
96             $self->{LOCKMODE} = $lockmode;
97             }
98              
99             # Store object parts.
100             $self->{DBNAME} = $dbname;
101             $self->{TIEPARAMS} = \@params;
102             $self->{OPENMODE} = $openmode;
103             $self->{DBOBJ} = $dbobj;
104             $self->{ORIG_DB} = \%db;
105             }
106              
107             # Close a file. Undef the object, untie it and undef the
108             # locking file handle.
109             sub _closeDB {
110             undef $_[0]->{DBOBJ};
111             untie($_[0]->{ORIG_DB}) or croak("untie: $!");
112             undef($_[0]->{LOCKFH});
113             }
114              
115             # Lock the DB, blocking until we have a lock.
116             sub lockDB {
117             my ($self) = @_;
118             my %db;
119              
120             # Block on locking the filehandle.
121             flock($self->{LOCKFH}, $self->{LOCKMODE}) or croak("flock: $!");
122              
123             # Reopen the dbm to obtain the current state.
124             my $dbobj = tie(%db, 'DB_File', $self->{DBNAME}, @{$self->{TIEPARAMS}});
125             croak "tie($self->{DBNAME}): $!" unless $dbobj;
126              
127             # Store object parts.
128             $self->{DB} = \%db;
129             $self->{DBOBJ} = $dbobj;
130             }
131              
132             # Unlock the locked DB, first sync()ing changes to disk.
133             sub unlockDB {
134             my ($self) = @_;
135             return unless $self->{LOCKMODE};
136             # Sync, and release the database.
137             if ($self->{LOCKMODE} == LOCK_EX) {
138             $self->{DBOBJ}->sync() and croak("sync(): $!");
139             }
140             undef($self->{DBOBJ});
141             untie($self->{DB}) or croak("untie: $!");
142             undef($self->{DB});
143             flock($self->{LOCKFH}, LOCK_UN) or croak("unlock: $!");
144             }
145              
146             # Toggle debug setting and return state.
147             sub debug { $DEBUG = $_[1] if (@_ > 1); return $DEBUG };
148              
149             # Everything unlocked and closed automatically.
150             sub DESTROY { $_[0]->unlockDB(); $_[0]->_closeDB(); }
151              
152             sub STORE {
153             print STDERR "STORE: @_\n" if $DEBUG;
154             croak("RO hash") if $_[0]->{OPENMODE} == O_RDONLY;
155             $_[0]->{DBOBJ}->put($_[1], $_[2]);
156             }
157             sub FETCH {
158             print STDERR "FETCH: @_\n" if $DEBUG;
159             my $v;
160             $_[0]->{DBOBJ}->get($_[1], $v);
161             return $v;
162             }
163             sub FIRSTKEY {
164             print STDERR "FIRSTKEY: @_\n" if $DEBUG;
165             $_[0]->{DBOBJ}->FIRSTKEY();
166             }
167             sub NEXTKEY {
168             print STDERR "NEXTKEY: @_\n" if $DEBUG;
169             $_[0]->{DBOBJ}->NEXTKEY($_[1]);
170             }
171             sub EXISTS {
172             print STDERR "EXISTS: @_\n" if $DEBUG;
173             exists $_[0]->{DB}->{$_[1]};
174             }
175             sub DELETE {
176             print STDERR "DELETE: @_\n" if $DEBUG;
177             croak("RO hash") if $_[0]->{OPENMODE} == O_RDONLY;
178             delete $_[0]->{DB}->{$_[1]};
179             }
180             sub CLEAR {
181             print STDERR "CLEAR: @_\n" if $DEBUG;
182             croak("RO hash") if $_[0]->{OPENMODE} == O_RDONLY;
183             %{$_[0]->{DB}} = ();
184             }
185              
186             # Use AUTOLOADER here?
187             sub put { my $r = shift; $r->{DBOBJ}->put(@_); }
188             sub get { my $r = shift; $r->{DBOBJ}->get(@_); }
189             sub del { my $r = shift; $r->{DBOBJ}->del(@_); }
190             sub seq { my $r = shift; $r->{DBOBJ}->seq(@_); }
191             sub sync { my $r = shift; $r->{DBOBJ}->sync(@_); }
192             sub fd { my $r = shift; $r->{DBOBJ}->fd(@_); }
193              
194             # BTREE only calls.
195             sub get_dup { my $r = shift; $r->{DBOBJ}->get_dup(@_); }
196             sub find_dup { my $r = shift; $r->{DBOBJ}->find_dup(@_); }
197             sub del_dup { my $r = shift; $r->{DBOBJ}->del_dup(@_); }
198              
199             # DBM Filters
200             sub filter_store_key { my $r = shift; $r->{DBOBJ}->filter_store_key(@_); }
201             sub filter_store_value { my $r = shift; $r->{DBOBJ}->filter_store_value(@_); }
202             sub filter_fetch_key { my $r = shift; $r->{DBOBJ}->filter_fetch_key(@_); }
203             sub filter_fetch_value { my $r = shift; $r->{DBOBJ}->filter_fetch_value(@_); }
204              
205             package Tie::DB_FileLock::HASHINFO;
206             use strict;
207             @Tie::DB_FileLock::HASHINFO::ISA = qw(DB_File::HASHINFO);
208             sub new { shift; DB_File::HASHINFO::new('DB_File::HASHINFO', @_); }
209              
210             package Tie::DB_FileLock::BTREEINFO;
211             use strict;
212             @Tie::DB_FileLock::BTREEINFO::ISA = qw(DB_File::BTREEINFO);
213             sub new { shift; DB_File::HASHINFO::new('DB_File::BTREEINFO', @_); }
214              
215             package Tie::DB_FileLock::RECNOINFO;
216             use strict;
217             @Tie::DB_FileLock::RECNOINFO::ISA = qw(DB_File::RECNOINFO);
218             sub new { shift; DB_File::HASHINFO::new('DB_File::RECNOINFO', @_); }
219              
220             1;
221             __END__