AnyEvent-Semaphore

 view release on metacpan or  search on metacpan

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

package AnyEvent::Semaphore;

our $VERSION = '0.01';

use strict;
use warnings;

use AE;
use Scalar::Util ();
use Method::WeakCallback qw(weak_method_callback);

# internal representation of watcher is an array [$semaphore, $cb]

sub new {
    my ($class, $size) = @_;
    my $sem = { size => $size || 1,
                holes => 0,
                running => 0,
                watchers => [] };
    $sem->{schedule_cb} = weak_method_callback($sem, '_schedule'),
    bless $sem, $class;
}

sub size {
    my $sem = shift;
    if (@_) {
        $sem->{size} = shift;
        &AE::postpone($sem->{schedule_cb});
    }
    $sem->{size};
}

sub running { shift->{running} }

sub down {
    return unless defined $_[1];
    my ($sem) = @_;
    my $watchers = $sem->{watchers};
    my $w = [@_];
    bless $w, 'AnyEvent::Semaphore::Watcher';
    push @{$watchers}, $w;
    Scalar::Util::weaken($watchers->[-1]);
    &AE::postpone($sem->{schedule_cb});
    $w;
}

sub _schedule {
    my $sem = shift;
    my $watchers = $sem->{watchers};
    while ($sem->{size} > $sem->{running}) {
        if (defined (my $w = shift @$watchers)) {
            $sem->{running}++;
            my ($cb, @args) = splice @$w, 1;
            bless $w, 'AnyEvent::Semaphore::Down';
            $cb->(@args);
        }
        else {
            @$watchers or return;
        }
    }
}

sub AnyEvent::Semaphore::Watcher::DESTROY {
    local ($!, $@, $SIG{__DIE__});
    eval {
        my $watcher = shift;
        my $sem = $watcher->[0];
        my $holes = ++$sem->{holes};
        my $watchers = $sem->{watchers};
        if ($holes > 100 and $holes * 2 > @$watchers) {
            @{$sem->{watchers}} = grep defined, @$watchers;
            Scalar::Util::weaken $_ for @$watchers;
            $sem->{holes} = 0;
        }
    }
}

sub AnyEvent::Semaphore::Down::DESTROY {
    local ($!, $@, $SIG{__DIE__});
    eval {
        my $sem = shift->[0];
        $sem->{running}--;
        &AE::postpone($sem->{schedule_cb})
    }
}

1;

__END__


=head1 NAME

AnyEvent::Semaphore - Semaphore implementation for AnyEvent

=head1 SYNOPSIS

  use AnyEvent::Semaphore;

  my $sem = AnyEvent::Semaphore->new(5);
  ...

  my $watcher = $sem->down( sub { ... } );
  ...

  undef $watcher; # semaphore up



=head1 DESCRIPTION

This module provides a semaphore implementation intended to be used
with the L<AnyEvent> framework.

It tries to be as simple as possible and to follow AnyEvent style.

=head2 API

The module provides the following methods:

=over 4

=item $sem = AnyEvent::Semaphore->new($size);

Creates a new semaphore object of the given size.

=item $watcher = $sem->down($callback)

Queues a down (or wait) operation on the semaphore. The given callback
will be eventually invoked when the resouce guarded by the semaphore
becomes free.



( run in 2.115 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )