| 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__ |