File Coverage

lib/IPC/SRLock/Sysv.pm
Criterion Covered Total %
statement 44 45 100.0
branch n/a
condition n/a
subroutine 14 15 100.0
pod 4 4 100.0
total 62 64 100.0


line stmt bran cond sub pod time code
1             package IPC::SRLock::Sysv;
2              
3 1     1   556 use namespace::autoclean;
  1         2  
  1         8  
4              
5 1     1   62 use English qw( -no_match_vars );
  1         1  
  1         7  
6 1     1   321 use File::DataClass::Types qw( Object OctalNum PositiveInt );
  1         1  
  1         15  
7 1     1   1054 use IPC::ShareLite qw( :lock );
  1         3621  
  1         108  
8 1     1   4 use IPC::SRLock::Utils qw( Unspecified hash_from loop_until throw );
  1         1  
  1         7  
9 1     1   264 use Storable qw( nfreeze thaw );
  1         2  
  1         36  
10 1     1   4 use Try::Tiny;
  1         1  
  1         41  
11 1     1   4 use Moo;
  1         1  
  1         6  
12              
13             extends q(IPC::SRLock::Base);
14              
15             # Attribute constructors
16             my $_build__share = sub {
17 2     2   453 my $self = shift; my $share;
  2         1  
18              
19 2     2   62 try { $share = IPC::ShareLite->new( '-key' => $self->lockfile,
20             '-create' => 1,
21             '-mode' => $self->mode,
22             '-size' => $self->size ) }
23             catch {
24             # uncoverable subroutine
25 0     0   0 throw "${_}: ${OS_ERROR}"; # uncoverable statement
26 2         12 };
27              
28 2         328 return $share;
29             };
30              
31             # Public attributes
32             has 'lockfile' => is => 'ro', isa => PositiveInt, default => 12_244_237;
33              
34             has 'mode' => is => 'ro', isa => OctalNum, coerce => 1, default => '0666';
35              
36             has 'size' => is => 'ro', isa => PositiveInt, default => 65_536;
37              
38             # Private attributes
39             has '_share' => is => 'lazy', isa => Object, builder => $_build__share;
40              
41             # Construction
42             sub BUILD {
43 2     2 1 571 my $self = shift; $self->_share; return;
  2         16  
  2         51  
44             }
45              
46             # Private methods
47             my $_expire_lock = sub {
48             my ($self, $data, $key, $lock) = @_;
49              
50             $self->log->error
51             ( $self->_timeout_error
52             ( $key, $lock->{spid}, $lock->{stime}, $lock->{timeout} ) );
53              
54             delete $data->{ $key };
55             return 0;
56             };
57              
58             my $_unlock_share = sub {
59             my $self = shift; defined $self->_share->unlock and return 1;
60              
61             throw 'Failed to unset semaphore'; # uncoverable statement
62             };
63              
64             my $_write_shared_mem = sub {
65             my ($self, $data) = @_;
66              
67             try { $self->_share->store( nfreeze $data ) }
68             catch {
69             throw "${_}: ${OS_ERROR}"; # uncoverable statement
70             };
71              
72             return $self->$_unlock_share;
73             };
74              
75             my $_read_shared_mem = sub {
76             my ($self, $for_update, $async) = @_; my $data;
77              
78             my $mode = $for_update ? LOCK_EX : LOCK_SH; $async and $mode |= LOCK_NB;
79             my $lock = $self->_share->lock( $mode );
80              
81             defined $lock or throw 'Failed to set semaphore';
82             $lock or return; # Async operation would have blocked
83              
84             try { $data = $self->_share->fetch; $data = $data ? thaw( $data ) : {} }
85             catch {
86             throw "${_}: ${OS_ERROR}"; # uncoverable statement
87             };
88              
89             not $for_update and $self->$_unlock_share;
90             return $data;
91             };
92              
93             my $_reset = sub {
94             my ($self, $args) = @_; my $key = $args->{k}; my $pid = $args->{p};
95              
96             my $shm_content = $self->$_read_shared_mem( 1 );
97              
98             my $lock; exists $shm_content->{ $key }
99             and $lock = $shm_content->{ $key }
100             and $lock->{spid} != $pid
101             and $self->$_unlock_share
102             and throw 'Lock [_1] set by another process', [ $key ];
103              
104             not delete $shm_content->{ $key } and $self->$_unlock_share
105             and throw 'Lock [_1] not set', [ $key ];
106              
107             return $self->$_write_shared_mem( $shm_content );
108             };
109              
110             my $_set = sub {
111             my ($self, $args, $now) = @_; my $key = $args->{k}; my $pid = $args->{p};
112              
113             my $shm_content = $self->$_read_shared_mem( 1, $args->{async} ) or return 0;
114              
115             my $lock; exists $shm_content->{ $key }
116             and $lock = $shm_content->{ $key }
117             and $lock->{timeout}
118             and $now > $lock->{stime} + $lock->{timeout}
119             and $lock = $self->$_expire_lock( $shm_content, $key, $lock );
120              
121             $lock and $self->$_unlock_share and return 0;
122              
123             $shm_content->{ $key }
124             = { spid => $pid, stime => $now, timeout => $args->{t} };
125             $self->$_write_shared_mem( $shm_content );
126             $self->log->debug( "Lock ${key} set by ${pid}" );
127             return 1;
128             };
129              
130             # Public methods
131             sub list {
132 6     6 1 112 my $self = shift; my $data = $self->$_read_shared_mem; my $list = [];
  6         9  
  6         8  
133              
134 6         5 while (my ($key, $info) = each %{ $data }) {
  10         26  
135 4         13 push @{ $list }, { key => $key,
136             pid => $info->{spid },
137             stime => $info->{stime },
138 4         3 timeout => $info->{timeout} };
139             }
140              
141 6         24 return $list;
142             }
143              
144             sub reset {
145 3     3 1 2220 my $self = shift; return $self->$_reset( $self->_get_args( @_ ) );
  3         8  
146             }
147              
148             sub set {
149 3     3 1 442 my ($self, @args) = @_; return loop_until( $_set )->( $self, @args );
  3         9  
150             }
151              
152             1;
153              
154             __END__
155              
156             =pod
157              
158             =encoding utf-8
159              
160             =head1 Name
161              
162             IPC::SRLock::Sysv - Set / reset locks using System V IPC
163              
164             =head1 Synopsis
165              
166             use IPC::SRLock;
167              
168             my $config = { type => q(sysv) };
169              
170             my $lock_obj = IPC::SRLock->new( $config );
171              
172             =head1 Description
173              
174             Uses System V semaphores to lock access to a shared memory file
175              
176             =head1 Configuration and Environment
177              
178             This class defines accessors for these attributes:
179              
180             =over 3
181              
182             =item C<lockfile>
183              
184             The key the the semaphore. Defaults to 12_244_237
185              
186             =item C<mode>
187              
188             Mode to create the shared memory file. Defaults to 0666
189              
190             =item C<size>
191              
192             Maximum size of a shared memory segment. Defaults to 65_536
193              
194             =back
195              
196             =head1 Subroutines/Methods
197              
198             =head2 BUILD
199              
200             Create the shared memory segment at construction time
201              
202             =head2 list
203              
204             List the contents of the lock table
205              
206             =head2 reset
207              
208             Delete a lock from the lock table
209              
210             =head2 set
211              
212             Set a lock in the lock table
213              
214             =head1 Diagnostics
215              
216             None
217              
218             =head1 Dependencies
219              
220             =over 3
221              
222             =item L<File::DataClass>
223              
224             =item L<IPC::ShareLite>
225              
226             =item L<IPC::SRLock::Base>
227              
228             =item L<Moo>
229              
230             =item L<Storable>
231              
232             =item L<Time::HiRes>
233              
234             =item L<Try::Tiny>
235              
236             =back
237              
238             =head1 Incompatibilities
239              
240             There are no known incompatibilities in this module
241              
242             =head1 Bugs and Limitations
243              
244             There are no known bugs in this module.
245             Please report problems to the address below.
246             Patches are welcome
247              
248             =head1 Author
249              
250             Peter Flanigan, C<< <pjfl@cpan.org> >>
251              
252             =head1 License and Copyright
253              
254             Copyright (c) 2016 Peter Flanigan. All rights reserved
255              
256             This program is free software; you can redistribute it and/or modify it
257             under the same terms as Perl itself. See L<perlartistic>
258              
259             This program is distributed in the hope that it will be useful,
260             but WITHOUT WARRANTY; without even the implied warranty of
261             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
262              
263             =cut
264              
265             # Local Variables:
266             # mode: perl
267             # tab-width: 3
268             # End: