Acrux
view release on metacpan or search on metacpan
lib/Acrux/FileLock.pm view on Meta::CPAN
$self->{error} = "";
$self->{file} //= File::Spec->catfile(getcwd, sprintf("%s.lock", basename($0)));
$self->{pid} //= $$; # Current PID by default
$self->{own} //= 0; # Owner PID
$self->{uid} //= 0; # Owner UID
$self->{auto} //= 0;
$self->{retries} //= RETRIES;
$self->{delay} //= DELAY;
$self->{'flock'} ||= 0;
$self->{fh} = undef;
$self->{_is_locked} = 0;
# PID normalize
my $raw_pid = $self->{pid} || 0;
$self->{pid} = abs(int($raw_pid)) if defined($raw_pid) && $raw_pid =~ /^-?\d+$/;
unless (defined($self->{pid}) && $self->{pid} =~ /^[0-9]{1,11}$/) { # Protect
croak("Incorrect \"pid\" attribute: $raw_pid");
}
# Check etries and delay
croak("Incorrect \"retries\" attribute: " . $self->{retries}) unless $self->{retries} =~ /^[0-9]{1,5}$/;
lib/Acrux/FileLock.pm view on Meta::CPAN
my $self = shift;
if (scalar(@_) >= 1) {
$self->{error} = shift;
return $self;
}
return $self->{error};
}
sub lock {
my $self = shift;
$self->error(undef);
if ($self->_is_locked) {
$self->_debug(sprintf("File \"%s\" already locked", $self->file));
return $self;
}
# Signals
$SIG{HUP} = $SIG{QUIT} = $SIG{INT} = $SIG{TERM} = sub {
$self->_debug( "Caught SIG$_[0]" );
exit;
};
# Using flock
lib/Acrux/FileLock.pm view on Meta::CPAN
seek $fh, 0, 0;
# Write data
unless ($fh->printf("%d:%d\n", $self->pid || $$, $>)) {
$self->error(sprintf("Can't write message to \"%s\": %s", $self->file, $!));
$self->_debug($self->error);
return $self->_rollback;
}
# Ok
$self->{_is_locked} = 1;
$self->own($self->pid || $$)->uid($>);
$self->_debug("Got lock file (flock=true)");
} else {
$self->error(sprintf("Can't open \"%s\": %s", $self->file, $!));
$self->_debug($self->error);
}
return $self;
}
# Regular case: save temp file first
lib/Acrux/FileLock.pm view on Meta::CPAN
$self->error(sprintf("Can't close \"%s\": %s", $tmp_file, $!));
$self->_debug($self->error);
unlink $tmp_file if -f $tmp_file;
return $self;
}
# Rename temp file to lock file
for my $try (0 .. $self->{retries}) {
unless ($self->check()) { # not exists, ok
if (rename($tmp_file, $self->file)) {
$self->{_is_locked} = 1;
$self->_debug("Got lock file (flock=false)");
return $self;
}
}
if ($self->{retries} && ($try != $self->{retries})) {
$self->_debug(sprintf("Retrying in %d seconds", $self->{delay}));
sleep $self->{delay} unless ($try == $self->{retries});
}
}
} else {
lib/Acrux/FileLock.pm view on Meta::CPAN
# Ok
return $self;
}
sub check {
my $self = shift;
$self->error(undef);
# Read owner-data of existed file (see own and uid accessors)
$self->_read_owner;
return $self->_is_locked
if $self->_use_flock;
# File not exists. Returns 0
return 0 unless -f $self->file;
# Check current PID and owner PID
if ($self->own == $self->pid) {
$self->_debug(sprintf("An attempt to call the check method twice was detected for PID=%d", $self->own));
return 1;
}
lib/Acrux/FileLock.pm view on Meta::CPAN
$self->_debug("Found and removed stale lock file");
}
return 0;
}
sub unlock {
my $self = shift;
$self->error(undef);
# Remove lock file
if ($self->_is_locked) {
# Release file handler first
if ($self->{fh}) {
flock $self->{fh}, LOCK_UN;
$self->{fh} = undef;
}
# Unlink file
$self->error(sprintf("Can't remove \"%s\": %s", $self->file, $!))->_debug($self->error)
unless unlink $self->file;
$self->own(0)->uid(0) unless -f $self->file; # Reset owner PID and UID to 0
lib/Acrux/FileLock.pm view on Meta::CPAN
# Set owner PID and UID
$self->own($owner_pid)->uid($owner_uid);
$self->_debug(sprintf("Read owner PID=%d, UID=%d from \"%s\"", $owner_pid, $owner_uid, $self->file));
# Ok
return 1;
}
sub _rollback {
my $self = shift;
$self->{_is_locked} = 0;
$self->own(0)->uid(0);
return $self unless $self->{fh};
flock $self->{fh}, LOCK_UN;
$self->{fh}->close;
$self->{fh} = undef;
return $self;
}
sub _is_locked {
my $self = shift;
return ($self->{_is_locked} && -f $self->file) ? 1 : 0
}
sub _use_flock {shift->{'flock'} ? 1 : 0}
sub _debug {
my $self = shift;
warn sprintf("%s: %s\n", ref($self), join("\n", @_)) if $self->{debug};
return $self;
}
sub DESTROY {
my $self = shift;
lib/Acrux/Util.pm view on Meta::CPAN
=item append
This argument is a boolean option, defaulted to false (C<0>).
Setting this argument to true (C<1>) will cause the data to be be written at the end of the current file.
Internally this sets the sysopen mode flag C<O_APPEND>
=item binmode
Set the layers to write the file with. The default will be something sensible on your platform
=item locked
This argument is a boolean option, defaulted to false (C<0>).
Setting this argument to true (C<1>) will ensure an that existing file will not be overwritten
=item mode
This numeric argument sets the default mode of opening files to write.
By default this argument to C<(O_WRONLY | O_CREAT)>.
Please DO NOT set this argument unless really necessary!
lib/Acrux/Util.pm view on Meta::CPAN
my $file = shift // '';
my $data = shift // '';
my $args = @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {};
my $cleanup = 1;
# Get binmode layer, mode and perms
my $bm = $args->{binmode} // ':raw'; # read in :raw by default
my $perms = $args->{perms} // 0666; # set file permissions
my $mode = $args->{mode} // O_WRONLY | O_CREAT;
$mode |= O_APPEND if $args->{append};
$mode |= O_EXCL if $args->{locked};
# Open filehandle
my $fh;
if (ref($file)) {
$fh = $file;
$cleanup = 0; # Disable closing filehandle for passed filehandle
} else {
$fh = IO::File->new($file, $mode, $perms);
unless (defined $fh) {
carp qq/Can't open file "$file": $!/;
t/13-filelock.t view on Meta::CPAN
note "Current PID=$$";
subtest "Base call" => sub {
my $l = Acrux::FileLock->new(file => $file, debug => DEBUG, flock => FLOCK);
is $l->pid, $$, "$$ current process by default";
# Lock
ok !$l->lock->error, "$$ lock file" or diag $l->error;
# Check
ok $l->check, "$$ is locked";
# Get owner uid
if (my $owner_uid = $l->uid) {
is $owner_uid, $>, "$$ owner uid" and note "owner uid = $owner_uid";
}
# Unlock
ok $l->unlock, "$$ unlock file";
#note explain $l;
# Check
ok !$l->check, "$$ now is NOT locked";
};
subtest "Auto call" => sub {
my $l = Acrux::FileLock->new(file => $file, auto => 1, debug => DEBUG, flock => FLOCK);
# Check
ok $l->check, "$$ is locked";
# Lock again
ok !$l->lock->error, "$$ lock file again" or diag $l->error;
};
subtest "Fork mode" => sub {
# Parent process
if (my $child = fork) {
sleep 1;
my $l = Acrux::FileLock->new(file => $file, auto => 1, flock => FLOCK);
note sprintf "Parent PID: %s; Parent Owner PID: %s", $l->pid, $l->own;
# Check
ok $l->check, "$$ is locked";
waitpid $child, 0;
return;
}
# Child process
else {
my $l = Acrux::FileLock->new(file => $file, auto => 1, flock => FLOCK);
unless ($l->check) {
note sprintf "Start child process (Child PID: %s; Child Owner PID: %s)", $l->pid, $l->uid;
( run in 1.325 second using v1.01-cache-2.11-cpan-364913b4093 )