IPC-Lock-WithTTL

 view release on metacpan or  search on metacpan

lib/IPC/Lock/WithTTL.pm  view on Meta::CPAN


    my $now = time();
    my $new_expiration;
    my $acquired = 0;
    if ($pid == 0) {
        # Previous task finished successfully
        if ($now >= $expiration) {
            # expired
            $new_expiration = $self->update_heartbeat;
            $acquired = 1;
        } else {
            # not expired
            $acquired = 0;
        }
    } elsif ($pid != $$) {
        # Other task is in process?
        if ($now >= $expiration) {
            # expired (Last task may have terminated abnormally)
            $new_expiration = $self->update_heartbeat;

            if ($self->kill_old_proc && $pid > 0) {
                kill 'KILL', $pid;
            }
            $acquired = 1;
        } else {
            # not expired (Still running)
            $acquired = 0;
        }
    } else {
        # Previous task done by this process
        if ($now >= $expiration) {
            # expired (Last task may have terminated abnormally)
            $new_expiration = $self->update_heartbeat;
            $acquired = 1;
        } else {
            # not expired (Last task may have terminated abnormally)
            $new_expiration = $self->update_heartbeat;
            $acquired = 1;
        }
    }

    flock $fh, LOCK_UN;
    if ($acquired) {
        return wantarray ? (1, { pid => $$,   expiration => $new_expiration })
                         : 1;
    } else {
        return wantarray ? (0, { pid => $pid, expiration => $expiration })
                         : 0;
    }
}

sub release {
    args(my $self);

    $self->update_heartbeat(pid => 0);
    undef $self->{_fh};

    return 1;
}

sub update_heartbeat {
    args(my $self,
         my $pid => { isa => 'Int', default => $$ },
       );

    my $fh = $self->_fh;

    my $expiration = time() + $self->ttl;

    seek $fh, 0, SEEK_SET;
    truncate $fh, 0;
    print {$fh} join(' ', $pid, $expiration)."\n";

    return $expiration;
}

1;

__END__

=encoding utf-8

=begin html

<a href="https://travis-ci.org/hirose31/IPC-Lock-WithTTL"><img src="https://travis-ci.org/hirose31/IPC-Lock-WithTTL.png?branch=master" alt="Build Status" /></a>
<a href="https://coveralls.io/r/hirose31/IPC-Lock-WithTTL?branch=master"><img src="https://coveralls.io/repos/hirose31/IPC-Lock-WithTTL/badge.png?branch=master" alt="Coverage Status" /></a>

=end html

=head1 NAME

IPC::Lock::WithTTL - run only one process up to given timeout

=head1 SYNOPSIS

    use IPC::Lock::WithTTL;
    
    my $lock = IPC::Lock::WithTTL->new(
        file          => '/tmp/lockme',
        ttl           => 5,
        kill_old_proc => 0,
       );
    
    my($r, $hb) = $lock->acquire;
    
    if ($r) {
        infof("Got lock! yay!!");
    } else {
        critf("Cannot get lock. Try after at %d", $hb->{expiration});
        exit 1;
    }
    
    $lock->release;

=head1 DESCRIPTION

IPC::Lock::WithTTL provides inter process locking feature.
This locking has timeout feature, so we can use following cases:

    * Once send an alert email, don't send same kind of alert email within 10 minutes.
    * We want to prevent the situation that script for failover some system is invoked more than one processes at same time and invoked many times in short time.



( run in 1.407 second using v1.01-cache-2.11-cpan-63c85eba8c4 )