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   110235 use 5.006;
  1         4  
4 1     1   5 use strict;
  1         2  
  1         19  
5 1     1   5 use warnings;
  1         2  
  1         40  
6              
7 1     1   478 use IO::File;
  1         1014  
  1         109  
8 1     1   7 use Carp qw(croak);
  1         3  
  1         42  
9 1     1   5 use Fcntl qw(LOCK_EX LOCK_NB);
  1         2  
  1         40  
10 1     1   5 use English qw(-no_match_vars);
  1         6  
  1         12  
11              
12             our $VERSION = '0.064';
13              
14             sub new {
15 16     16 0 37720 my ( $class, %param ) = @ARG;
16              
17             # Mandatory options
18 16 100       84 if ( !exists $param{lock_file} ) {
19 1         175 croak q{Mandatory argument "lock_file" is missing};
20             }
21              
22             # Check "lock_file"
23 15         32 my $lock_file = $param{lock_file};
24 15 100       36 if ( !length $lock_file ) {
25 2         190 croak q{Bad argument "lock_file"};
26             }
27              
28             # Class to object
29 13         98 return bless(
30             {
31             is_locked => 0,
32             fh => undef,
33             error => undef,
34             lock_file => $lock_file,
35             },
36             $class,
37             );
38             } ## end sub new
39              
40             sub is_locked {
41 40     40 1 59 my ($self) = @ARG;
42              
43 40         165 return $self->{is_locked};
44             }
45              
46             sub lock_file {
47 26     26 1 2790 my ($self) = @ARG;
48              
49 26         4272 return $self->{lock_file};
50             }
51              
52             sub error {
53 5     5 1 1023 my ($self) = @ARG;
54              
55 5         156 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         15 return $self->_lock();
63             }
64              
65             # Or just return undef
66             sub try_lock {
67 4     4 0 14 my ($self) = @ARG;
68              
69 4         13 return $self->_lock( try => 1 );
70             }
71              
72             # You can manually unlock
73             sub unlock {
74 15     15 0 21 my ($self) = @ARG;
75              
76             # Re-unlocking changes nothing
77 15 100       31 return $self if !$self->is_locked();
78              
79             # Close filehandle before file removing
80 6         20 $self->_set_fh(undef);
81              
82 6 50       25 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         46 return $self->_set_is_locked(0);
89             }
90              
91             sub get_lock_data {
92 9     9 0 28 my ($self) = @_;
93              
94 9         15 my $line;
95             {
96 9 100       11 my $fh
  9         19  
97             = $self->is_locked()
98             ? $self->_fh()
99             : $self->_init_file_handle();
100              
101 9 50       23 return if !$fh;
102              
103 9         47 $fh->seek( 0, 0 );
104              
105 9         359 $line = $fh->getline();
106             }
107              
108 9 100       466 return if !$line;
109              
110 8         17 chomp $line;
111              
112 8         56 my ( $pid, $executable_name ) = $line =~ m{
113             ^
114             {
115             "pid":"(\d+)"
116             ,
117             "executable_name":"( [^""]+ )"
118             }
119             $
120             }x;
121              
122             return {
123 8         88 pid => $pid,
124             executable_name => $executable_name,
125             };
126             } ## end sub get_lock_data
127              
128             # unlocking on scope exit
129             sub DESTROY {
130 13     13   5986 my ($self) = @ARG;
131              
132 13         32 return $self->unlock();
133             }
134              
135             sub _init_file_handle {
136 11     11   30 my ($self) = @ARG;
137              
138 11         22 my $fh = IO::File->new( $self->lock_file(), O_RDWR | O_CREAT );
139              
140 11 50       1236 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   26 my ( $self, %param ) = @_;
149              
150             # Re-locking changes nothing
151 10 100       21 return $self if $self->is_locked();
152              
153 8         19 my $fh = $self->_init_file_handle();
154              
155 8 50       21 if ( !$fh ) {
156 0         0 croak sprintf( q{Can't open "%s": %s}, $self->lock_file(), $self->error() );
157             }
158              
159 8 100       89 if ( !flock $fh, LOCK_EX | LOCK_NB ) {
160 2         16 $self->_set_error($OS_ERROR);
161              
162 2 100       126 return if $param{try};
163              
164 1         9 croak sprintf( q{Can't lock "%s": %s}, $self->lock_file(), $self->error() );
165             }
166              
167 6 50       128 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         43 autoflush $fh 1;
174              
175 6 50       286 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         335 return $self->_set_fh($fh)->_set_is_locked(1);
182             } ## end sub _lock
183              
184             sub _lock_message {
185 6     6   45 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   30 my ( $self, $is_locked ) = @ARG;
194              
195 12         24 $self->{is_locked} = $is_locked;
196              
197 12         105 return $self;
198             }
199              
200             sub _fh {
201 6     6   11 my ($self) = @ARG;
202              
203 6         12 return $self->{fh};
204             }
205              
206             sub _set_fh {
207 12     12   25 my ( $self, $fh ) = @ARG;
208              
209 12         335 $self->{fh} = $fh;
210              
211 12         38 return $self;
212             }
213              
214             sub _set_error {
215 2     2   21 my ( $self, $error ) = @ARG;
216              
217 2   50     16 $self->{error} = $error || '';
218              
219 2         4 return $self;
220             }
221              
222             1;
223              
224             __END__