Fugu

 view release on metacpan or  search on metacpan

lib/Fugu/Pidfile.pm  view on Meta::CPAN


# $self->acquire($pid):
#	Write the PID and keep the locked handle open. The lock lives
#	until the object is destroyed, or until the process exits. A
#	second acquire on
#	the same file, from any process, fails while the first holds
#	it. The method returns 1, or undef with ->error set.
sub acquire ( $self, $pid = $$ )
{
	if ( $self->{fh} ) {
		$self->{error} = "already acquired $self->{path}";
		return;
	}

	my $fh = $self->_open_locked(1) or return;

	unless ( $self->_store( $fh, $pid ) ) {
		close $fh;
		return;
	}

	$self->{fh} = $fh;

	return 1;
}

# $self->read_pid:
#	Read the PID from the file. The method returns the PID, or
#	undef when the file is absent or holds no decimal PID.
sub read_pid ($self)
{
	open my $fh, '<', $self->{path} or do {
		$self->{error} = "open $self->{path}: $!";
		return;
	};
	my $line = <$fh>;
	close $fh;

	return unless defined $line;
	chomp $line;
	return unless $line =~ /^\d+$/;

	return $line;
}

# $self->remove:
#	Remove the PID file. The method returns 1 when the file is
#	absent afterwards.
sub remove ($self)
{
	return 1 unless -e $self->{path};
	unless ( unlink $self->{path} ) {
		$self->{error} = "unlink $self->{path}: $!";
		return;
	}

	return 1;
}

# $self->is_running:
#	Return the PID from the file when that process is alive.
#	Otherwise return undef.
sub is_running ($self)
{
	my $pid = $self->read_pid;
	return unless defined $pid;
	return unless Fugu::Process->is_alive($pid);

	return $pid;
}

# $self->is_stale:
#	Report if the file names a process that is not alive now. An
#	absent PID file is not stale.
sub is_stale ($self)
{
	my $pid = $self->read_pid;
	return 0 unless defined $pid;

	return !Fugu::Process->is_alive($pid);
}

# $self->_open_locked($nonblocking):
#	Open the file for read and write, create it when it is absent,
#	and take the exclusive lock. The open must not truncate. A
#	truncate before the lock lets a concurrent reader see an empty
#	file.
sub _open_locked ( $self, $nonblocking )
{
	sysopen my $fh, $self->{path}, O_CREAT | O_RDWR, 0644 or do {
		$self->{error} = "open $self->{path}: $!";
		return;
	};

	my $how = $nonblocking ? LOCK_EX | LOCK_NB : LOCK_EX;
	unless ( flock $fh, $how ) {
		$self->{error} = "lock $self->{path}: $!";
		close $fh;
		return;
	}

	return $fh;
}

# $self->_store($fh, $pid):
#	Truncate the locked handle and write the PID. The write is a
#	syswrite, so the bytes reach the file with no flush.
sub _store ( $self, $fh, $pid )
{
	unless ( truncate $fh, 0 ) {
		$self->{error} = "truncate $self->{path}: $!";
		return;
	}
	unless ( defined sysseek( $fh, 0, SEEK_SET ) ) {
		$self->{error} = "seek $self->{path}: $!";
		return;
	}

	my $data    = "$pid\n";
	my $written = syswrite $fh, $data;
	unless ( defined $written && $written == length $data ) {
		$self->{error} = "write $self->{path}: $!";
		return;
	}

	return 1;
}

1;



( run in 0.803 second using v1.01-cache-2.11-cpan-14f38c9f855 )