| 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__ |