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   636 use namespace::autoclean;
  1         1  
  1         9  
4              
5 1     1   72 use English qw( -no_match_vars );
  1         1  
  1         8  
6 1     1   324 use File::DataClass::Types qw( Object OctalNum PositiveInt );
  1         2  
  1         16  
7 1     1   1153 use IPC::ShareLite qw( :lock );
  1         3877  
  1         117  
8 1     1   6 use IPC::SRLock::Utils qw( Unspecified hash_from loop_until throw );
  1         1  
  1         8  
9 1     1   301 use Storable qw( nfreeze thaw );
  1         1  
  1         40  
10 1     1   3 use Try::Tiny;
  1         2  
  1         38  
11 1     1   4 use Moo;
  1         1  
  1         8  
12              
13             extends q(IPC::SRLock::Base);
14              
15             # Attribute constructors
16             my $_build__share = sub {
17 2     2   455 my $self = shift; my $share;
  2         3  
18              
19 2     2   72 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         13 };
27              
28 2         330 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 643 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 122 my $self = shift; my $data = $self->$_read_shared_mem; my $list = [];
  6         10  
  6         9  
133              
134 6         7 while (my ($key, $info) = each %{ $data }) {
  10         28  
135 4         14 push @{ $list }, { key => $key,
136             pid => $info->{spid },
137             stime => $info->{stime },
138 4         4 timeout => $info->{timeout} };
139             }
140              
141 6         28 return $list;
142             }
143              
144             sub reset {
145 3     3 1 2861 my $self = shift; return $self->$_reset( $self->_get_args( @_ ) );
  3         10  
146             }
147              
148             sub set {
149 3     3 1 449 my ($self, @args) = @_; return loop_until( $_set )->( $self, @args );
  3         10  
150             }
151              
152             1;
153              
154             __END__