IPC-LockTicket
view release on metacpan or search on metacpan
lib/IPC/LockTicket.pm view on Meta::CPAN
return(true);
}
# Returns an array of integers which represents all registered PIDs of current lock file
sub _GetPIDs {
my $objSelf = shift;
try {
$objSelf->{_harData} = lock_retrieve($objSelf->{_uriPath});
}
catch ($strError) {
carp qq{"$objSelf->{_uriPath}": Mailformed shared memory file.\n$strError\n};
return(undef);
}
return(@{$objSelf->{_harData}{arePIDs}});
}
# Save a array of integer
sub _SetPIDs {
my $objSelf = shift;
my @intPIDs = @_;
if ( open(my $fh, "<", $objSelf->{_uriPath}) ) {
flock($fh, 2);
$objSelf->{_harData} = retrieve($objSelf->{_uriPath});
$objSelf->{_harData}{arePIDs} = [ @intPIDs ];
store($objSelf->{_harData}, $objSelf->{_uriPath});
my $strCaller = (caller(0))[3];
close($fh) or die qq{$strCaller(): Unable to close "$objSelf->{_uriPath}" properly\n};
}
return(true);
}
# Returns boolean value
sub _MultipleAllowed {
my $objSelf = shift;
try {
$objSelf->{_harData} = lock_retrieve($objSelf->{_uriPath});
}
catch ($strError) {
carp qq{"$objSelf->{_uriPath}": Mailformed shared memory file.\n$strError\n};
return(undef);
}
return($objSelf->{_harData}{bolAllowMultiple});
}
=head3 C<MainLock>
my $bolSuccess = $object->MainLock();
my $bolSuccess = $object->MainLock(1);
Checks if a lock file exists and creates it if not. Fails if file exists and process stored within is alive.
If a C<true> value is supplied, locking is non-exclusive but shared. If the file exists and was also created non-exclusively, locking is successful. This way the lock file is shared with several processes, requesting non-exclusive mode.
If C<false> is returned from C<MainLock> no lock file was created/claimed.
Meaning:
____________________________________________________________________________________________________________________________________
| Call mode | MainLock() | MainLock() | MainLock() | MainLock() | MainLock(1) | MainLock(1) | MainLock(1) | MainLock(1) |
| Lock file | empty* | shared | exclusive | non-existent | empty* | shared | exclusive | non-existent |
| MainLock returns | false | false | false | true | false | true | false | true |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯Â...
*= empty or any other non-Storable data.
=cut
# Creates the lock file
sub MainLock {
my $objSelf = shift;
my $bolMultipleAllowed = shift;
# If the file exists
if ( -e $objSelf->{_uriPath}
&& $objSelf->_Check() ) { # Dies in _Check if failed
# If multiple is allowed we register our PID
if ( $bolMultipleAllowed
&& $objSelf->_MultipleAllowed() ) {
$objSelf->TokenLock();
my @intPIDs = $objSelf->_GetPIDs();
if ( grep { $_ == $$ } @intPIDs ) {
carp qq{WARNING: Same process tried to MainLock() again.\n};
$objSelf->TokenUnlock();
return(false);
}
else {
$objSelf->_SetPIDs( @intPIDs, $$ );
}
$objSelf->TokenUnlock();
return(true);
}
# Or it must be exclusive
else {
$objSelf->TokenLock();
my @intPIDs = $objSelf->_GetPIDs();
local $SIG{CLD} = q{IGNORE};
local $SIG{CHLD} = q{IGNORE};
# Did we lock up?
if ( grep { $$ == $_ } @intPIDs ) {
carp qq{WARNING: Same process tried to MainLock() again.\n};
$objSelf->TokenUnlock();
return(false);
}
# There are processes running on this lock file
elsif ( grep { kill(0 => $_) } @intPIDs ) {
$objSelf->TokenUnlock();
return(false);
lib/IPC/LockTicket.pm view on Meta::CPAN
}
return(true);
}
=head3 C<MainUnlock>
$object->MainUnlock();
Removes lock. The lock file is deleted if this is the last process accessing it.
Returns only a true value.
If it is a shared lock and not the last process accessing it, only the PID entries are removed from the lock file.
=cut
# Removes lock file or the PID from those
sub MainUnlock {
my $objSelf = shift;
if ( ! -e $objSelf->{_uriPath} ) {
my $strCaller = (caller(0))[3];
croak qq{$strCaller(): Lock file missing\nHave you ever called MainLock() ?\n};
}
if ( $objSelf->_MultipleAllowed() ) {
my @intPIDs = ();
$objSelf->TokenLock();
@intPIDs = do {
local $SIG{CLD} = q{IGNORE};
local $SIG{CHLD} = q{IGNORE};
grep { kill(0 => $_) } grep { $_ != $$ } $objSelf->_GetPIDs();
};
if ( @intPIDs ) {
$objSelf->_SetPIDs(@intPIDs);
$objSelf->TokenUnlock();
}
else {
unlink($objSelf->{_uriPath});
}
}
else {
unlink($objSelf->{_uriPath});
}
return(true);
}
sub _CleanAgentsList (\@) {
my $areList = shift;
my %bolTestedParent = ();
local $SIG{CLD} = q{IGNORE};
local $SIG{CHLD} = q{IGNORE};
# if parent was not tested yet test if alive, but we don't test ourself, but any other agent
return(grep { ( $bolTestedParent{$_->{_pidParent}}++ || kill(0 => $_->{_pidParent}) ) && ( $_->{_pidAgent} == $$ || kill(0 => $_->{_pidAgent}) ) } @{$areList});
}
=head3 C<TokenLock>
$object->TokenLock();
Can only be called after C<MainLock> and before C<MainUnlock> otherwise C<die>s.
Requests exclusive lock, i.e., any C<TokenLock> is blocking until C<TokenUnlock> was called, or the locking process is dead. Process checks are done to prevent infinite locks through broken children or parents, but this is not proper usage. Don't jus...
Returns a true value or blocks.
=cut
# Integrated lock system
sub TokenLock {
my $objSelf = shift;
my $bolInit = true;
my $strCaller = (caller(0))[3];
if ( ! -e $objSelf->{_uriPath} ) {
croak qq{$strCaller(): Lock file missing\nHave you ever called MainLock() ?\n};
}
while ( true ) {
if ( -e $objSelf->{_uriPath}
&& open(my $fh, "<", $objSelf->{_uriPath}) ) {
flock($fh, 2);
# Load current data
$objSelf->{_harData} = retrieve($objSelf->{_uriPath});
@{$objSelf->{_harData}{areToken}} = _CleanAgentsList(@{$objSelf->{_harData}{areToken}});
# If we never got a token, we request one
if ( $bolInit
&& ! first { $_->{_pidAgent} == $$ } @{$objSelf->{_harData}{areToken}} ) {
$bolInit = false;
push(@{$objSelf->{_harData}{areToken}}, { _pidAgent => $$, _pidParent => $objSelf->{_pidParent} });
}
# Die if something strange happened
elsif ( ! -e $objSelf->{_uriPath}
|| ( ! $bolInit
&& ! first { $_->{_pidParent} == $objSelf->{_pidParent} } @{$objSelf->{_harData}{areToken}} ) ) {
# Parent exited (and maybe we weren't informed to exit)
close($fh) or die qq{$strCaller(): Unable to close "$objSelf->{_uriPath}" properly\n};
exit(120);
}
store($objSelf->{_harData}, $objSelf->{_uriPath});
close($fh) or die qq{$strCaller(): Unable to close "$objSelf->{_uriPath}" properly\n};
# Check if it's our turn
if ( $objSelf->{_harData}{areToken}[0]{_pidAgent} == $$ ) {
return(true);
}
# Wait if it isn't our turn yet
else {
Time::HiRes::sleep(0.01); # Needed to prevent permanent spamming on CPU and FS
}
( run in 0.746 second using v1.01-cache-2.11-cpan-39bf76dae61 )