File Coverage

blib/lib/JIP/LockFile.pm
Criterion Covered Total %
statement 83 91 91.2
branch 22 28 78.5
condition 1 2 50.0
subroutine 23 23 100.0
pod 3 8 37.5
total 132 152 86.8


line stmt bran cond sub pod time code
1             package JIP::LockFile;
2              
3 1     1   101208 use 5.006;
  1         4  
4 1     1   6 use strict;
  1         2  
  1         19  
5 1     1   16 use warnings;
  1         3  
  1         29  
6              
7 1     1   494 use IO::File;
  1         1079  
  1         132  
8 1     1   8 use Carp qw(croak);
  1         1  
  1         41  
9 1     1   5 use Fcntl qw(LOCK_EX LOCK_NB);
  1         2  
  1         36  
10 1     1   4 use English qw(-no_match_vars);
  1         2  
  1         7  
11              
12             our $VERSION = '0.062';
13              
14             sub new {
15 16     16 0 32495 my ($class, %param) = @ARG;
16              
17             # Mandatory options
18 16 100       52 if (!exists $param{'lock_file'}) {
19 1         163 croak q{Mandatory argument "lock_file" is missing};
20             }
21              
22             # Check "lock_file"
23 15         25 my $lock_file = $param{'lock_file'};
24 15 100       33 if (!length $lock_file) {
25 2         188 croak q{Bad argument "lock_file"};
26             }
27              
28             # Class to object
29 13         84 return bless(
30             {
31             is_locked => 0,
32             fh => undef,
33             error => undef,
34             lock_file => $lock_file,
35             },
36             $class,
37             );
38             }
39              
40             sub is_locked {
41 40     40 1 68 my ($self) = @ARG;
42              
43 40         141 return $self->{'is_locked'};
44             }
45              
46             sub lock_file {
47 26     26 1 1375 my ($self) = @ARG;
48              
49 26         4808 return $self->{'lock_file'};
50             }
51              
52             sub error {
53 5     5 1 817 my ($self) = @ARG;
54              
55 5         127 return $self->{'error'};
56             }
57              
58             # Lock or raise an exception
59             sub lock {
60 6     6 0 18 my ($self) = @ARG;
61              
62 6         13 return $self->_lock();
63             }
64              
65             # Or just return undef
66             sub try_lock {
67 4     4 0 10 my ($self) = @ARG;
68              
69 4         11 return $self->_lock(try => 1);
70             }
71              
72             # You can manually unlock
73             sub unlock {
74 15     15 0 22 my ($self) = @ARG;
75              
76             # Re-unlocking changes nothing
77 15 100       30 return $self if !$self->is_locked;
78              
79             # Close filehandle before file removing
80 6         18 $self->_set_fh(undef);
81              
82 6 50       22 if (!unlink $self->lock_file) {
83 0         0 $self->_set_error($OS_ERROR);
84              
85 0         0 croak sprintf(q{Can't unlink "%s": %s}, $self->lock_file, $self->error);
86             }
87              
88 6         43 return $self->_set_is_locked(0);
89             }
90              
91             sub get_lock_data {
92 9     9 0 24 my ($self) = @_;
93              
94 9         14 my $line;
95             {
96 9 100       11 my $fh
  9         17  
97             = $self->is_locked
98             ? $self->_fh
99             : $self->_init_file_handle;
100              
101 9 50       23 return if !$fh;
102              
103 9         37 $fh->seek(0, 0);
104              
105 9         343 $line = $fh->getline();
106             }
107              
108 9 100       432 return if !$line;
109              
110 8         13 chomp $line;
111              
112 8         57 my ($pid, $executable_name) = $line =~ m{
113             ^
114             {
115             "pid":"(\d+)"
116             ,
117             "executable_name":"( [^""]+ )"
118             }
119             $
120             }x;
121              
122             return {
123 8         65 pid => $pid,
124             executable_name => $executable_name,
125             };
126             }
127              
128             # unlocking on scope exit
129             sub DESTROY {
130 13     13   4945 my ($self) = @ARG;
131              
132 13         30 return $self->unlock;
133             }
134              
135             sub _init_file_handle {
136 11     11   25 my ($self) = @ARG;
137              
138 11         22 my $fh = IO::File->new($self->lock_file, O_RDWR | O_CREAT);
139              
140 11 50       1159 if (!$fh) {
141 0         0 $self->_set_error($OS_ERROR);
142             }
143              
144 11         23 return $fh;
145             }
146              
147             sub _lock {
148 10     10   20 my ($self, %param) = @_;
149              
150             # Re-locking changes nothing
151 10 100       17 return $self if $self->is_locked;
152              
153 8         19 my $fh = $self->_init_file_handle;
154              
155 8 50       19 if (!$fh) {
156 0         0 croak sprintf(q{Can't open "%s": %s}, $self->lock_file, $self->error);
157             }
158              
159 8 100       82 if (!flock $fh, LOCK_EX | LOCK_NB) {
160 2         12 $self->_set_error($OS_ERROR);
161              
162 2 100       106 return if $param{'try'};
163              
164 1         5 croak sprintf(q{Can't lock "%s": %s}, $self->lock_file, $self->error);
165             }
166              
167 6 50       135 if (!truncate $fh, 0) {
168 0         0 $self->_set_error($OS_ERROR);
169              
170 0         0 croak sprintf(q{Can't truncate "%s": %s}, $self->lock_file, $self->error);
171             }
172              
173 6         41 autoflush $fh 1;
174              
175 6 50       260 if (!$fh->print($self->_lock_message)) {
176 0         0 $self->_set_error($OS_ERROR);
177              
178 0         0 croak sprintf(q{Can't write message to file: %s}, $self->error);
179             }
180              
181 6         268 return $self->_set_fh($fh)->_set_is_locked(1);
182             }
183              
184             sub _lock_message {
185 6     6   43 return sprintf(
186             q[{"pid":"%s","executable_name":"%s"}],
187             $PROCESS_ID,
188             $EXECUTABLE_NAME,
189             );
190             }
191              
192             sub _set_is_locked {
193 12     12   29 my ($self, $is_locked) = @ARG;
194              
195 12         22 $self->{'is_locked'} = $is_locked;
196              
197 12         86 return $self;
198             }
199              
200             sub _fh {
201 6     6   8 my ($self) = @ARG;
202              
203 6         11 return $self->{'fh'};
204             }
205              
206             sub _set_fh {
207 12     12   24 my ($self, $fh) = @ARG;
208              
209 12         311 $self->{'fh'} = $fh;
210              
211 12         40 return $self;
212             }
213              
214             sub _set_error {
215 2     2   15 my ($self, $error) = @ARG;
216              
217 2   50     7 $self->{'error'} = $error || '';
218              
219 2         5 return $self;
220             }
221              
222             1;
223              
224             __END__