Skip to Content.
Sympa Menu

devel - Re: [sympa-developpers] Lock problem?

Subject: Developers of Sympa

List archive

Chronological Thread  
  • From: IKEDA Soji <address@concealed>
  • To: address@concealed
  • Subject: Re: [sympa-developpers] Lock problem?
  • Date: Wed, 20 Nov 2013 14:00:47 +0900

Hi,

On Tue, 06 Aug 2013 16:29:20 +0200
Guillaume Rousse <address@concealed> wrote:

> Le 04/08/2013 18:11, IKEDA Soji a écrit :
> > Hi,
> >
> > Though direct use of File::NFSLock is possible, I wrote a small
> > module wrapping it. Some notes:
> >
> > o The feature File::NFSLock argues support for NFS is anticipating
> > cache of filesystem. It may make lock processing a bit less
> > efficient on local filesystems.
> >
> > o Current Sympa::Lock takes care of upgrading "read" locking to
> > "write" one (shared to exclusive), but it seems not to be used.
> > So my module doesn't implement it.
> >
> > o Sympa::Lock::lock() is non-blocking, namely, it won't wait
> > competing lock and fails immediately. This module follows this
> > feature, but it can be discussed.
> That's easy to wrap a non-blocking function to make it blocking, that's
> easier to do the opposite direction, so I prefer this way.
>
> There is a primitive t/lock.t test suite I used when refactoring
> Sympa::Lock, feel free to adapt it to test your new implementation.

I wrote testcases copying your example t/lock.t.

And new LockedFile.pm fixed problems:
- Mix-up between blocking timeout and stale lock timeouts.
- Locking was always non-blocking (LOCK_NB was always enabled).

Regards,

--- Soji


--
株式会社 コンバージョン セキュリティ&OSSソリューション部 池田荘児
〒231-0004 神奈川県横浜市中区元浜町3-21-2 ヘリオス関内ビル7F
e-mail address@concealed TEL 045-640-3550
http://www.conversion.co.jp/
# Sympa - SYsteme de Multi-Postage Automatique
#
# Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel
# Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
# 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites
# Copyright (c) 2011, 2012, 2013 GIP RENATER
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

package Sympa::LockedFile;

use strict;
use warnings;

use Carp qw(croak);
use English qw(-no_match_vars);
use Fcntl qw(LOCK_EX LOCK_NB LOCK_SH);
use File::NFSLock;
$File::NFSLock::LOCK_EXTENSION = '.lock';

use Sympa::Log::Syslog;

use base qw(IO::File);

our %lock_of;
my $default_timeout    = 30;
my $stale_lock_timeout = 20 * 60;    # TODO should become a config parameter

sub open {
    Sympa::Log::Syslog::do_log( 'debug2', '(%s, %s, %s, %s)', @_ );
    my ( $self, $file, $blocking_timeout, $mode ) = @_;

    $blocking_timeout ||= $default_timeout;
    $mode ||= '<';

    my $lock_type;
    if ( $mode =~ /[+>aw]/ ) {
        $lock_type = LOCK_EX;
    } else {
        $lock_type = LOCK_SH;
    }
    if ( $blocking_timeout < 0 ) {
        $lock_type |= LOCK_NB;
    }

    my $lock = File::NFSLock->new(
        {   file               => $file,
            lock_type          => $lock_type,
            blocking_timeout   => $blocking_timeout,
            stale_lock_timeout => $stale_lock_timeout,
        }
    );
    unless ($lock) {
        Sympa::Log::Syslog::do_log( 'err', 'Failed locking %s: %s',
            $file, $ERRNO );
        return undef;
    }

    unless ( $self->SUPER::open( $file, $mode ) ) {
        Sympa::Log::Syslog::do_log( 'err', 'Failed opening %s: %s',
            $file, $ERRNO );
        $lock->unlock;    # make sure unlock to occur immediately.
        return undef;
    }

    $lock_of{ $self + 0 } = $lock;  # register lock object, i.e. keep locking.
    return 1;
}

sub close {
    Sympa::Log::Syslog::do_log( 'debug2', '(%s)', @_ );
    my ($self) = @_;

    my $ret = $self->SUPER::close;

    croak 'Lock not found'
        unless exists $lock_of{ $self + 0 };

    $lock_of{ $self + 0 }->unlock;    # make sure unlock to occur immediately.
    delete $lock_of{ $self + 0 };     # lock object will be destructed.
    return $ret;
}

# Destruct inside reference to lock object so that it will be released.
# Corresponding filehandle will be closed automatically.
sub DESTROY {
    my ($self) = @_;
    delete $lock_of{ $self + 0 };     # lock object will be destructed.
}

1;
__END__

=encoding us-ascii

=head1 NAME

Sympa::LockedFile - Filehandle with locking

=head1 SYNOPSIS

  use Sympa::LockedFile;
  
  # Create filehandle acquiring lock.
  my $fh = Sympa::LockedFile->new('/path/to/file', 20, '+<') or die;
  # or,
  my $fh = Sympa::LockedFile->new();
  $fh->open('/path/to/file', 20, '+<') or die;
  
  # Operations...
  while (<$fh>) { ... }
  seek $fh, 0, 0;
  truncate $fh, 0;
  print $fh "blah blah\n";
  # et cetera.
 
  # Close filehandle releasing lock.
  $fh->close;

=head1 DESCRIPTION

This class implements a filehadle with locking.

=head2 Class Method

=over

=item Sympa::LockedFile->new ( [ $file, [ $blocking_timeout, [ $mode ] ] ] )

Creates new object.
If any of optional parameters are specified, opens a file acquiring lock.

Parameters:

See open().

Returns:

New object or, if something went wrong, false value.

=back

=head2 Instance Methods

Instances of L<Sympa::LockedFile> support the methods provided by L<IO::File>.

=over

=item $fh->open ( $file, [ $blocking_timeout, [ $mode ] ] )

Opens a file specified by $file acquiring lock.

Parameters:

=over

=item $file

Path of file to be locked and opened.

=item $blocking_timeout

Programs will block up to the number of seconds specified by this option
before returning undef (could not get a lock).
If negative value was given, programs will not block but fail immediately.

Default is C<30>.

However, if existing lock is older than 1200 seconds i.e. 20 minutes,
lock will be stolen.

=item $mode

Mode to open file.
If it includes any writing operations (C<'E<gt>'>, C<'E<gt>E<gt>'>,
C<'+E<lt>'>, ...), trys to acquire exclusive lock (C<LOCK_EX>),
otherwise shared lock (C<LOCK_SH>).

Default is C<'E<lt>'>.

=back

Returns:

New filehandle.
If acquiring lock failed, won't open file.
If opening file failed, releases acquired lock.
In both cases returns false value.

=back

=over

=item $fh->close ( )

Closes filehandle and releases lock on it.

Parameters:

None.

Returns:

If close succeeded, returns true value, otherwise false value.

If filehandle had not been locked by current process,
this method will safely close it and die.

=back

=head1 SEE ALSO

L<perlfunc/"Functions for filehandles, files or directories">,
L<perlop/"I/O Operators">,
L<IO::File>, L<File::NFSLock>.

=head1 AUTHORS

L<Sympa::LockedFile> was initially wrote by:

IKEDA Soji <address@concealed>.

=cut

Attachment: LockedFile.t
Description: Troff document



  • Re: [sympa-developpers] Lock problem?, IKEDA Soji, 11/20/2013

Archive powered by MHonArc 2.6.19+.

Top of Page