File Coverage

blib/lib/DDLock/Client.pm
Criterion Covered Total %
statement 58 87 66.6
branch 7 24 29.1
condition 5 14 35.7
subroutine 14 19 73.6
pod 0 10 0.0
total 84 154 54.5


line stmt bran cond sub pod time code
1             package DDLock::Client;
2              
3 3     3   47214 use vars qw($VERSION);
  3         6  
  3         219  
4             $VERSION = '0.50';
5              
6             =head1 NAME
7              
8             DDLock::Client - Client library for distributed lock daemon
9              
10             =head1 SYNOPSIS
11              
12             use DDLock::Client ();
13              
14             my $cl = DDLock::Client->new(
15             servers => ['locks.localnet:7004', 'locks2.localnet:7002', 'localhost']
16             );
17              
18             # Do something that requires locking
19             if ( my $lock = $cl->trylock("foo") ) {
20             ...do some 'foo'-synchronized stuff...
21             } else {
22             die "Failed to lock 'foo': $!";
23             }
24              
25             # You can either just let $lock go out of scope or explicitly release it:
26             $lock->release;
27              
28             =head1 DESCRIPTION
29              
30             This is a client library for ddlockd, a distributed lock daemon not entirely
31             unlike a very simplified version of the CPAN module IPC::Locker.
32              
33             This can be used as a drop in replacment for the unreleased DDLockClient class
34             that some of us in the world may be using. Simply replace the class name.
35              
36             =head1 EXPORTS
37              
38             Nothing.
39              
40             =head1 MAINTAINER
41              
42             Jonathan Steinert
43              
44             =head1 AUTHOR
45              
46             Brad Fitzpatrick
47              
48             Copyright (c) 2004 Danga Interactive, Inc.
49              
50             =cut
51              
52 3     3   18 use strict;
  3         6  
  3         111  
53 3     3   2970 use Socket;
  3         14018  
  3         1931  
54              
55 3     3   1794 use DDLock::Client::Daemon;
  3         11  
  3         99  
56 3     3   1668 use DDLock::Client::File;
  3         12  
  3         157  
57              
58 3     3   2375 BEGIN {
59 3     3   19 use fields qw( servers lockdir sockcache hooks );
  3         7  
  3         13  
60 3     3   189 use vars qw{$Error};
  3         6  
  3         101  
61             }
62              
63             $Error = undef;
64              
65             our $Debug = 0;
66              
67             sub get_sock_onlycache {
68 0     0 0 0 my ($self, $addr) = @_;
69 0         0 return $self->{sockcache}{$addr};
70             }
71              
72             sub get_sock {
73 3     3 0 5 my ($self, $addr) = @_;
74 3         6 my $sock = $self->{sockcache}{$addr};
75 3 50 33     12 return $sock if $sock && getpeername($sock);
76             # TODO: cache unavailability for 'n' seconds?
77 3         23 return $self->{sockcache}{$addr} =
78             IO::Socket::INET->new(
79             PeerAddr => $addr,
80             Proto => "tcp",
81             Type => SOCK_STREAM,
82             ReuseAddr => 1,
83             Blocking => 1,
84             );
85             }
86              
87             ### (CLASS) METHOD: DebugLevel( $level )
88             sub DebugLevel {
89 0     0 0 0 my $class = shift;
90              
91 0 0       0 if ( @_ ) {
92 0         0 $Debug = shift;
93 0 0       0 if ( $Debug ) {
94 0         0 *DebugMsg = *RealDebugMsg;
95             } else {
96 0     0   0 *DebugMsg = sub {};
  0         0  
97             }
98             }
99              
100 0         0 return $Debug;
101             }
102              
103              
104 3     3 0 4 sub DebugMsg {}
105              
106              
107             ### (CLASS) METHOD: DebugMsg( $level, $format, @args )
108             ### Output a debugging messages formed sprintf-style with I and I
109             ### if I is greater than or equal to the current debugging level.
110             sub RealDebugMsg {
111 0     0 0 0 my ( $class, $level, $fmt, @args ) = @_;
112 0 0       0 return unless $Debug >= $level;
113              
114 0         0 chomp $fmt;
115 0         0 printf STDERR ">>> $fmt\n", @args;
116             }
117              
118              
119             ### (CONSTRUCTOR) METHOD: new( %args )
120             ### Create a new DDLock::Client
121             sub new {
122 1     1 0 16 my DDLock::Client $self = shift;
123 1         4 my %args = @_;
124              
125 1 50       11 $self = fields::new( $self ) unless ref $self;
126 1 50 33     4126 die "Servers argument must be an arrayref if specified"
127             unless !exists $args{servers} || ref $args{servers} eq 'ARRAY';
128 1   50     6 $self->{servers} = $args{servers} || [];
129 1   50     7 $self->{lockdir} = $args{lockdir} || '';
130 1         2 $self->{sockcache} = {}; # "host:port" -> IO::Socket::INET
131 1         3 $self->{hooks} = {}; # hookname -> coderef
132              
133 1         4 return $self;
134             }
135              
136              
137             sub set_hook {
138 0     0 0 0 my DDLock::Client $self = shift;
139 0   0     0 my $hookname = shift || return;
140              
141 0 0       0 if (@_) {
142 0         0 $self->{hooks}->{$hookname} = shift;
143             } else {
144 0         0 delete $self->{hooks}->{$hookname};
145             }
146             }
147              
148             sub run_hook {
149 6     6 0 9 my DDLock::Client $self = shift;
150 6   50     15 my $hookname = shift || return;
151              
152 6 50       22 if (my $hook = $self->{hooks}->{$hookname}) {
153 0         0 local $@;
154 0         0 eval { $hook->($self) };
  0         0  
155 0 0       0 warn "DDLock::Client hook '$hookname' threw error: $@" if $@;
156             }
157             }
158              
159             ### METHOD: trylock( $name )
160             ### Try to get a lock from the lock daemons with the specified I. Returns
161             ### a DDLock object on success, and undef on failure.
162             sub trylock {
163 3     3 0 1659 my DDLock::Client $self = shift;
164 3         5 my $lockname = shift;
165              
166 3         9 $self->run_hook('trylock', $lockname);
167              
168 3         4 my $lock;
169 3         5 local $@;
170              
171             # If there are servers to connect to, use a network lock
172 3 50       4 if ( @{$self->{servers}} ) {
  3         12  
173 3         12 $self->DebugMsg( 2, "Creating a new DDLock object." );
174 3         6 $lock = eval { DDLock::Client::Daemon->new($self, $lockname, @{$self->{servers}}) };
  3         5  
  3         24  
175             }
176              
177             # Otherwise use a file lock
178             else {
179 0         0 $self->DebugMsg( 2, "No servers configured: Creating a new DDFileLock object." );
180 0         0 $lock = eval { DDLock::Client::File->new($lockname, $self->{lockdir}) };
  0         0  
181             }
182              
183             # If no lock was acquired, fail and put the reason in $Error.
184 3 50       11 unless ( $lock ) {
185 3         5 my $eval_error = $@;
186 3         9 $self->run_hook('trylock_failure');
187 3 50       12 return $self->lock_fail( $eval_error ) if $eval_error;
188 0         0 return $self->lock_fail( "Unknown failure." );
189             }
190              
191 0         0 $self->run_hook('trylock_success', $lockname, $lock);
192              
193 0         0 return $lock;
194             }
195              
196              
197             ### (PROTECTED) METHOD: lock_fail( $msg )
198             ### Set C<$!> to the specified message and return undef.
199             sub lock_fail {
200 3     3 0 5 my DDLock::Client $self = shift;
201 3         4 my $msg = shift;
202              
203 3         5 $Error = $msg;
204 3         14 return undef;
205             }
206              
207              
208             1;
209              
210              
211             # Local Variables:
212             # mode: perl
213             # c-basic-indent: 4
214             # indent-tabs-mode: nil
215             # End: