File Coverage

blib/lib/AnyEvent/FileLock.pm
Criterion Covered Total %
statement 21 64 32.8
branch 0 24 0.0
condition 0 26 0.0
subroutine 7 9 77.7
pod 0 1 0.0
total 28 124 22.5


line stmt bran cond sub pod time code
1             package AnyEvent::FileLock;
2              
3             our $VERSION = '0.03';
4              
5 1     1   20048 use strict;
  1         3  
  1         34  
6 1     1   5 use warnings;
  1         2  
  1         29  
7 1     1   23 use 5.010;
  1         9  
  1         44  
8 1     1   6 use Carp;
  1         1  
  1         98  
9 1     1   830 use AE;
  1         7892  
  1         31  
10              
11 1     1   8 use Fcntl ();
  1         2  
  1         19  
12 1     1   782 use Method::WeakCallback qw(weak_method_callback);
  1         3362  
  1         726  
13              
14             sub flock {
15 0     0 0   my ($class, %opts) = @_;
16              
17 0   0       my $type = delete $opts{type} // 'flock';
18 0 0         $type =~ /^(?:flock|fcntl)$/ or croak "invalid lock type '$type'";
19              
20 0   0       my $mode = delete $opts{mode} // '+<';
21 0   0       my $lock_mode = delete $opts{lock_mode} // $mode;
22 0   0       my $delay = delete $opts{delay} || 0.1;
23              
24 0   0       my $user_cb = delete $opts{cb} // croak "cb argument is missing";
25              
26 0           my $max_time;
27 0 0         if (defined(my $timeout = delete $opts{timeout})) {
28 0           $max_time = AE::now() + $timeout;
29             }
30              
31 0           my $fh;
32 0           my $file = delete $opts{file};
33 0 0         if (defined $file) {
34 0   0       my $open_mode = delete $opts{open_mode} // $mode;
35 0 0         $open_mode =~ /^\+?(?:<|>>?)/ or croak "bad mode specification";
36 0 0         open $fh, $mode, $file or return
37             }
38             else {
39 0   0       $fh = delete $opts{file} // croak "file or fh argument is required";
40             }
41              
42 0           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 0           bless $self, $class;
50              
51 0 0         if ($type eq 'fcntl') {
52             $self->{$_} = delete($opts{lock_start}) // 0
53 0   0       for qw(lock_start lock_whence lock_len);
54 0 0         $self->{operation} = ($lock_mode eq '<' ? Fcntl::F_RDLCK() : Fcntl::F_WRLCK());
55             }
56             else {
57 0 0         $self->{operation} = ($lock_mode eq '<' ? Fcntl::LOCK_SH() : Fcntl::LOCK_EX());
58             }
59              
60              
61 0 0         %opts and croak "unkwnown arguments found (".join(', ', sort keys %opts).")";
62              
63 0           my $alcb = $self->{acquire_lock_cb} = weak_method_callback($self, '_acquire_lock');
64 0           &AE::postpone($alcb);
65              
66 0           $self;
67             }
68              
69             sub _acquire_lock {
70 0     0     my $self = shift;
71 0           my $operation = $self->{opertation};
72 0           my $now = AE::now;
73              
74 0           my $ok;
75 0 0         if ($self->{type} eq 'flock') {
76 0           $ok = CORE::flock($self->{fh}, $self->{operation}|Fcntl::LOCK_NB());
77             }
78             else {
79 0           require Fcntl::Packer;
80 0           my %flock = (type => $self->{operation});
81 0           $flock{$_} = $self->{"lock_$_"} for qw(whence start len);
82 0           $ok = fcntl($self->{fh}, Fcntl::F_SETLK, Fcntl::Packer::pack_fcntl_flock(\%flock));
83             }
84 0 0 0       if ($ok) {
    0 0        
85 0           $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 everytime
92             # (and obviosly all but one failing).
93 0           &AE::timer($self->{delay} * (0.8 + rand 0.40), 0, $self->{acquire_lock_cb});
94 0           return;
95             }
96             else {
97 0           $self->{user_cb}->();
98             }
99             # release all the references, the object is useless from this
100             # point on time.
101 0           %$self = ();
102             }
103              
104             1;
105             __END__