File Coverage

blib/lib/ExclusiveLock/Guard.pm
Criterion Covered Total %
statement 48 70 68.5
branch 20 38 52.6
condition 3 11 27.2
subroutine 8 9 88.8
pod 0 3 0.0
total 79 131 60.3


line stmt bran cond sub pod time code
1             package ExclusiveLock::Guard;
2 6     6   188445 use strict;
  6         11  
  6         157  
3 6     6   23 use warnings;
  6         8  
  6         245  
4             our $VERSION = '0.06';
5              
6 6     6   462 use Errno qw(EWOULDBLOCK);
  6         945  
  6         568  
7 6     6   25 use Fcntl qw(LOCK_EX LOCK_NB LOCK_UN);
  6         9  
  6         210  
8 6     6   2469 use File::stat;
  6         30978  
  6         22  
9              
10             my $ERRSTR;
11              
12 0     0 0 0 sub errstr { $ERRSTR }
13              
14             sub new {
15 6     6 0 3006547 my($class, $filename, %args) = @_;
16 6   50     112 my $retry_count = $args{retry_count} || 5;
17              
18 6         9 my $fh;
19 6         16 my $count = 0;
20 6         11 my $is_locked = 1;
21 6         18 while (1) {
22 6         12 $ERRSTR = undef;
23 6         14 $is_locked = 1;
24 6 50       601 unless (open $fh, '>', $filename) {
25 0         0 $ERRSTR = "failed to open file:$filename:$!";
26 0         0 return;
27             }
28 6 100       29 if ($args{nonblocking}) {
29 2 100       29 unless (flock $fh, LOCK_EX | LOCK_NB) {
30 1 50       19 if ($! != EWOULDBLOCK) {
31 0         0 $ERRSTR = "failed to flock file:$filename:$!";
32 0         0 return;
33             }
34 1         5 $is_locked = 0;
35             }
36             } else {
37 4 50       83 unless (flock $fh, LOCK_EX) {
38 0         0 $ERRSTR = "failed to flock file:$filename:$!";
39 0         0 return;
40             }
41             }
42 6 50 33     174 unless (-f $filename && stat($fh)->ino == do { my $s = stat($filename); $s ? $s->ino : -1 }) {
  6 50       1555  
  6         555  
43 0 0       0 unless (flock $fh, LOCK_UN) {
44 0         0 $ERRSTR = "failed to unlock flock file:$filename:$!";
45 0         0 return;
46             }
47 0 0       0 unless (close $fh) {
48 0         0 $ERRSTR = "failed to close file:$filename:$!";
49 0         0 return;
50             }
51 0 0 0     0 if ($retry_count && ++$count > $retry_count) {
52 0         0 $ERRSTR = "give up! $retry_count times retry to lock.";
53 0         0 return;
54             }
55 0         0 next;
56             }
57 6         149 last;
58             }
59              
60             bless {
61 6         72 filename => $filename,
62             fh => $fh,
63             is_locked => $is_locked,
64             }, $class;
65             }
66              
67 3     3 0 31 sub is_locked { $_[0]->{is_locked} }
68              
69             sub DESTROY {
70 6     6   2123807 my $self = shift;
71 6 100       164 return unless $self->{is_locked};
72              
73 5         59 my $fh = delete $self->{fh};
74 5         38 my $filename = delete $self->{filename};
75 5 50       150 unless (close $fh) {
76 0         0 warn "failed to close file:$filename:$!";
77 0         0 return;
78             }
79              
80             # try unlink lock file
81 5 50       353 if (open my $unlink_fh, '<', $filename) { # else is unlinked lock file by another process?
82             # A
83 5 100       228 if (flock $unlink_fh, LOCK_EX | LOCK_NB) { # else is locked the file by another process
84 4 50 33     118 if (-f $filename && stat($unlink_fh)->ino == do { my $s = stat($filename); $s ? $s->ino : -1 }) { # else is unlink and create file by another process in the A timing
  4 50       613  
  4         437  
85 4 50       291 unless (unlink $filename) {
86 0         0 warn "failed to unlink file:$filename:$!";
87             }
88 4 50       36 unless (flock $unlink_fh, LOCK_UN) {
89 0         0 warn "failed to unlock flock file:$filename:$!";
90             }
91 4 50       181 unless (close $unlink_fh) {
92 0           warn "failed to close file:$filename:$!";
93             }
94             }
95             }
96             }
97             }
98              
99             1;
100             __END__