AnyEvent-FileLock

 view release on metacpan or  search on metacpan

lib/AnyEvent/FileLock.pm  view on Meta::CPAN

    if (defined $file) {
        my $open_mode = delete $opts{open_mode} // $mode;
        $open_mode =~ /^\+?(?:<|>>?)/ or croak "bad mode specification";
        open $fh, $mode, $file or return
    }
    else {
        $fh = delete $opts{fh} // croak "file or fh argument is required";
    }

    my $self = { file => $file,
                 fh => $fh,
                 type => $type,
                 max_time => $max_time,
                 user_cb => $user_cb,
                 delay => $delay };

    bless $self, $class;

    if ($type eq 'fcntl') {
        $self->{$_} = delete($opts{lock_start}) // 0
            for qw(lock_start lock_whence lock_len);
        $self->{operation} = ($lock_mode eq '<' ? Fcntl::F_RDLCK() : Fcntl::F_WRLCK());
    }
    else {
        $self->{operation} = ($lock_mode eq '<' ? Fcntl::LOCK_SH() : Fcntl::LOCK_EX());
    }


    %opts and croak "unkwnown arguments found (".join(', ', sort keys %opts).")";

    my $alcb = $self->{acquire_lock_cb} = weak_method_callback($self, '_acquire_lock');
    &AE::postpone($alcb);

    $self;
}

sub _acquire_lock {
    my $self = shift;
    my $operation = $self->{opertation};
    my $now = AE::now;

    my $ok;
    if ($self->{type} eq 'flock') {
        $ok = CORE::flock($self->{fh}, $self->{operation}|Fcntl::LOCK_NB());
    }
    else {
        require Fcntl::Packer;
        my %flock = (type => $self->{operation});
        $flock{$_} = $self->{"lock_$_"} for qw(whence start len);
        $ok = fcntl($self->{fh}, Fcntl::F_SETLK, Fcntl::Packer::pack_fcntl_flock(\%flock));
    }
    if ($ok) {
        $self->{user_cb}->($self->{fh});
    }
    elsif ($! == Errno::EAGAIN() and
           (!defined($self->{max_time}) or $self->{max_time} >= $now)) {
        # we add some randomness into the delay to avoid the case
        # where all the contenders follow exactly the same pattern so
        # that they end looking for the pattern all at once every time
        # (and obviosly all but one failing).
        $self->{timer} = &AE::timer($self->{delay} * (0.8 + rand 0.40), 0, $self->{acquire_lock_cb});
        return;
    }
    else {
        $self->{user_cb}->();
    }
    # release all the references, the object is useless from this
    # point on time.
    %$self = ();
}

1;
__END__

=head1 NAME

AnyEvent::FileLock - Lock files asynchronously

=head1 SYNOPSIS

  use AnyEvent::FileLock;

  my $w = AnyEvent::FileLock->flock(file => $fn,
                                    cb => sub { ... },
                                    mode => '<',
                                    delay => $seconds,
                                    timeout => $timeout);


=head1 DESCRIPTION

This module tries to lock some file repeatedly until it success or a
timeout happens.

=head2 API

The function provides a unique method C<flock> accepting the following
arguments:

=over 4

=item fh => $file_handle

When this argument is given the passed file handle is used as the file
locking target.

=item file => $file_name

When this argument is given a file with the given name will be opened
or created and then the module will try to lock it.

=item type => $lock_type

C<$lock_type> may be C<fcntl> or C<flock> and determines the way used
to obtain the lock under the hood.

The default is C<flock>.

In order to use locks of type C<fcntl>, the module L<Fctnl::Packer>
has to be also installed.



( run in 0.790 second using v1.01-cache-2.11-cpan-efa8479b9fe )