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.05';
4              
5 1     1   78018 use strict;
  1         3  
  1         30  
6 1     1   4 use warnings;
  1         2  
  1         24  
7 1     1   17 use 5.010;
  1         4  
8 1     1   4 use Carp;
  1         6  
  1         51  
9 1     1   309 use AE;
  1         5241  
  1         31  
10              
11 1     1   14 use Fcntl ();
  1         3  
  1         33  
12 1     1   363 use Method::WeakCallback qw(weak_method_callback);
  1         1783  
  1         620  
13              
14             sub flock {
15 3     3 0 17661 my ($class, %opts) = @_;
16              
17 3   50     36 my $type = delete $opts{type} // 'flock';
18 3 50       34 $type =~ /^(?:flock|fcntl)$/ or croak "invalid lock type '$type'";
19              
20 3   50     25 my $mode = delete $opts{mode} // '+<';
21 3   33     20 my $lock_mode = delete $opts{lock_mode} // $mode;
22 3   50     19 my $delay = delete $opts{delay} || 0.1;
23              
24 3   33     16 my $user_cb = delete $opts{cb} // croak "cb argument is missing";
25              
26 3         7 my $max_time;
27 3 100       15 if (defined(my $timeout = delete $opts{timeout})) {
28 1         7 $max_time = AE::now() + $timeout;
29             }
30              
31 3         9 my $fh;
32 3         11 my $file = delete $opts{file};
33 3 100       14 if (defined $file) {
34 2   33     14 my $open_mode = delete $opts{open_mode} // $mode;
35 2 50       16 $open_mode =~ /^\+?(?:<|>>?)/ or croak "bad mode specification";
36 2 50       63 open $fh, $mode, $file or return
37             }
38             else {
39 1   33     7 $fh = delete $opts{fh} // croak "file or fh argument is required";
40             }
41              
42 3         27 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         14 bless $self, $class;
50              
51 3 50       16 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       22 $self->{operation} = ($lock_mode eq '<' ? Fcntl::LOCK_SH() : Fcntl::LOCK_EX());
58             }
59              
60              
61 3 50       13 %opts and croak "unkwnown arguments found (".join(', ', sort keys %opts).")";
62              
63 3         21 my $alcb = $self->{acquire_lock_cb} = weak_method_callback($self, '_acquire_lock');
64 3         88 &AE::postpone($alcb);
65              
66 3         62 $self;
67             }
68              
69             sub _acquire_lock {
70 16     16   1302827 my $self = shift;
71 16         88 my $operation = $self->{opertation};
72 16         108 my $now = AE::now;
73              
74 16         54 my $ok;
75 16 50       96 if ($self->{type} eq 'flock') {
76 16         286 $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 16 100 100     408 if ($ok) {
    100 33        
85 2         13 $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 13         175 $self->{timer} = &AE::timer($self->{delay} * (0.8 + rand 0.40), 0, $self->{acquire_lock_cb});
94 13         169 return;
95             }
96             else {
97 1         7 $self->{user_cb}->();
98             }
99             # release all the references, the object is useless from this
100             # point on time.
101 3         207 %$self = ();
102             }
103              
104             1;
105             __END__