File Coverage

blib/lib/AnyEvent/FileLock.pm
Criterion Covered Total %
statement 57 63 90.4
branch 15 24 62.5
condition 11 26 42.3
subroutine 9 9 100.0
pod 0 1 0.0
total 92 123 74.8


line stmt bran cond sub pod time code
1             package AnyEvent::FileLock;
2              
3             our $VERSION = '0.06';
4              
5 1     1   46348 use strict;
  1         4  
  1         36  
6 1     1   7 use warnings;
  1         4  
  1         23  
7 1     1   20 use 5.010;
  1         3  
8 1     1   4 use Carp;
  1         6  
  1         48  
9 1     1   260 use AE;
  1         4669  
  1         37  
10              
11 1     1   9 use Fcntl ();
  1         4  
  1         21  
12 1     1   251 use Method::WeakCallback qw(weak_method_callback);
  1         1572  
  1         556  
13              
14             sub flock {
15 3     3 0 11043 my ($class, %opts) = @_;
16              
17 3   50     21 my $type = delete $opts{type} // 'flock';
18 3 50       20 $type =~ /^(?:flock|fcntl)$/ or croak "invalid lock type '$type'";
19              
20 3   50     14 my $mode = delete $opts{mode} // '+<';
21 3   33     11 my $lock_mode = delete $opts{lock_mode} // $mode;
22 3   50     13 my $delay = delete $opts{delay} || 0.1;
23              
24 3   33     10 my $user_cb = delete $opts{cb} // croak "cb argument is missing";
25              
26 3         6 my $max_time;
27 3 100       10 if (defined(my $timeout = delete $opts{timeout})) {
28 1         4 $max_time = AE::now() + $timeout;
29             }
30              
31 3         5 my $fh;
32 3         5 my $file = delete $opts{file};
33 3 100       10 if (defined $file) {
34 2   33     10 my $open_mode = delete $opts{open_mode} // $mode;
35 2 50       11 $open_mode =~ /^\+?(?:<|>>?)/ or croak "bad mode specification";
36 2 50       43 open $fh, $mode, $file or return
37             }
38             else {
39 1   33     3 $fh = delete $opts{fh} // croak "file or fh argument is required";
40             }
41              
42 3         14 my $self = { file => $file,
43             fh => $fh,
44             type => $type,
45             max_time => $max_time,
46             user_cb => $user_cb,
47             delay => $delay };
48              
49 3         7 bless $self, $class;
50              
51 3 50       8 if ($type eq 'fcntl') {
52             $self->{$_} = delete($opts{lock_start}) // 0
53 0   0     0 for qw(lock_start lock_whence lock_len);
54 0 0       0 $self->{operation} = ($lock_mode eq '<' ? Fcntl::F_RDLCK() : Fcntl::F_WRLCK());
55             }
56             else {
57 3 50       11 $self->{operation} = ($lock_mode eq '<' ? Fcntl::LOCK_SH() : Fcntl::LOCK_EX());
58             }
59              
60              
61 3 50       8 %opts and croak "unkwnown arguments found (".join(', ', sort keys %opts).")";
62              
63 3         11 my $alcb = $self->{acquire_lock_cb} = weak_method_callback($self, '_acquire_lock');
64 3         45 &AE::postpone($alcb);
65              
66 3         35 $self;
67             }
68              
69             sub _acquire_lock {
70 17     17   1430603 my $self = shift;
71 17         71 my $operation = $self->{opertation};
72 17         85 my $now = AE::now;
73              
74 17         47 my $ok;
75 17 50       82 if ($self->{type} eq 'flock') {
76 17         291 $ok = CORE::flock($self->{fh}, $self->{operation}|Fcntl::LOCK_NB());
77             }
78             else {
79 0         0 require Fcntl::Packer;
80 0         0 my %flock = (type => $self->{operation});
81 0         0 $flock{$_} = $self->{"lock_$_"} for qw(whence start len);
82 0         0 $ok = fcntl($self->{fh}, Fcntl::F_SETLK, Fcntl::Packer::pack_fcntl_flock(\%flock));
83             }
84 17 100 100     429 if ($ok) {
    100 33        
85 2         9 $self->{user_cb}->($self->{fh});
86             }
87             elsif ($! == Errno::EAGAIN() and
88             (!defined($self->{max_time}) or $self->{max_time} >= $now)) {
89             # we add some randomness into the delay to avoid the case
90             # where all the contenders follow exactly the same pattern so
91             # that they end looking for the pattern all at once every time
92             # (and obviosly all but one failing).
93 14         162 $self->{timer} = &AE::timer($self->{delay} * (0.8 + rand 0.40), 0, $self->{acquire_lock_cb});
94 14         182 return;
95             }
96             else {
97 1         11 $self->{user_cb}->();
98             }
99             # release all the references, the object is useless from this
100             # point on time.
101 3         222 %$self = ();
102             }
103              
104             1;
105             __END__