package POPFile::Mutex;

#----------------------------------------------------------------------------
#
# This is a mutex object that uses mkdir() to provide exclusive access
# to a region on a per thread or per process basis.
#
# Copyright (c) 2001-2006 John Graham-Cumming
#
#   This file is part of POPFile
#
#   POPFile is free software; you can redistribute it and/or modify it
#   under the terms of version 2 of the GNU General Public License as
#   published by the Free Software Foundation.
#
#   POPFile 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 POPFile; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#----------------------------------------------------------------------------

use strict;

#----------------------------------------------------------------------------
# new
#
#   Create a new Mutex object (which may refer to a file referred to by
#   other mutexes) with a specific name generated from the name passed
#   in.
#
#----------------------------------------------------------------------------
sub new
{
    my ( $type, $name ) = @_;
    my $self;

    $self->{name__} = "popfile_mutex_${name}.mtx";
    release( $self );

    return bless $self, $type;
}

#----------------------------------------------------------------------------
#
# acquire
#
#   Returns 1 if it manages to grab the mutex (and will block if necessary)
#   and 0 if it fails.
#
#----------------------------------------------------------------------------
sub acquire
{
    my ( $self,             # Reference to this object
         $timeout ) = @_;   # Timeout in seconds to wait (undef = infinite)

    # If acquire() has been called without a matching release() then
    # fail at once

    if ( defined( $self->{locked__} ) ) {
        return 0;
    }

    # Wait a very long time if no timeout is specified

    $timeout = 0xFFFFFFFF if ( !defined( $timeout ) );
    my $now = time;

    # Try to create a directory during the timeout period

    do {
        if ( mkdir( $self->{name__}, 0755 ) ) { # Create a directory
            $self->{locked__} = 1;
            return 1;
        }
        select( undef, undef, undef, 0.01 );
    } while ( time < ( $now + $timeout ) );

    # Timed out so return 0
    return 0;
}

#----------------------------------------------------------------------------
#
# release
#
#   Release the lock if we acquired it with a call to acquire()
#
#----------------------------------------------------------------------------
sub release
{
    my ( $self ) = @_;

    rmdir( $self->{name__} ); # Delete the Mutex directory
    $self->{locked__} = undef;
}

1;
