--- Coro/Coro/Semaphore.pm 2001/07/19 02:45:09 1.7 +++ Coro/Coro/Semaphore.pm 2011/08/03 14:52:19 1.119 @@ -1,10 +1,10 @@ =head1 NAME -Coro::Semaphore - non-binary semaphores +Coro::Semaphore - counting semaphores =head1 SYNOPSIS - use Coro::Semaphore; + use Coro; $sig = new Coro::Semaphore [initial value]; @@ -16,81 +16,153 @@ =head1 DESCRIPTION +This module implements counting semaphores. You can initialize a mutex +with any level of parallel users, that is, you can intialize a sempahore +that can be Ced more than once until it blocks. There is no owner +associated with semaphores, so one thread can C it while another +can C it. + +Counting semaphores are typically used to coordinate access to +resources, with the semaphore count initialized to the number of free +resources. Threads then increment the count when resources are added +and decrement the count when resources are removed. + +You don't have to load C manually, it will be loaded +automatically when you C and call the C constructor. + =over 4 =cut package Coro::Semaphore; +use common::sense; + use Coro (); -$VERSION = 0.08; +our $VERSION = 6.04; -=item new [inital count, default zero] +=item new [inital count] Creates a new sempahore object with the given initial lock count. The -default lock count is 1, which means it is unlocked by default. +default lock count is 1, which means it is unlocked by default. Zero (or +negative values) are also allowed, in which case the semaphore is locked +by default. -=cut +=item $sem->count -sub new { - bless [defined $_[1] ? $_[1] : 1], $_[0]; -} +Returns the current semaphore count. + +=item $sem->adjust ($diff) + +Atomically adds the amount given to the current semaphore count. If the +count becomes positive, wakes up any waiters. Does not block if the count +becomes negative, however. =item $sem->down Decrement the counter, therefore "locking" the semaphore. This method waits until the semaphore is available if the counter is zero. +=item $sem->wait + +Similar to C, but does not actually decrement the counter. Instead, +when this function returns, a following call to C or C is +guaranteed to succeed without blocking, until the next thread switch +(C etc.). + +Note that using C is much less efficient than using C, so try +to prefer C whenever possible. + +=item $sem->wait ($callback) + +If you pass a callback argument to C, it will not wait, but +immediately return. The callback will be called as soon as the semaphore +becomes available (which might be instantly), and gets passed the +semaphore as first argument. + +The callback might C the semaphore exactly once, might wake up other +threads, but is I allowed to block (switch to other threads). + =cut -sub down { - my $self = shift; - while ($self->[0] <= 0) { - push @{$self->[1]}, $Coro::current; - Coro::schedule; - } - --$self->[0]; -} +#=item $status = $sem->timed_down ($timeout) +# +#Like C, but returns false if semaphore couldn't be acquired within +#$timeout seconds, otherwise true. + +#sub timed_down { +# require Coro::Timer; +# my $timeout = Coro::Timer::timeout ($_[1]); +# +# while ($_[0][0] <= 0) { +# push @{$_[0][1]}, $Coro::current; +# &Coro::schedule; +# if ($timeout) { +# # ugly as hell. slow, too, btw! +# for (0..$#{$_[0][1]}) { +# if ($_[0][1][$_] == $Coro::current) { +# splice @{$_[0][1]}, $_, 1; +# return; +# } +# } +# die; +# } +# } +# +# --$_[0][0]; +# return 1; +#} =item $sem->up Unlock the semaphore again. -=cut - -sub up { - my $self = shift; - if (++$self->[0] > 0) { - (shift @{$self->[1]})->ready if @{$self->[1]}; - } -} - =item $sem->try Try to C the semaphore. Returns true when this was possible, otherwise return false and leave the semaphore unchanged. +=item $sem->waiters + +In scalar context, returns the number of threads waiting for this +semaphore. + +=item $guard = $sem->guard + +This method calls C and then creates a guard object. When the guard +object is destroyed it automatically calls C. + =cut -sub try { - my $self = shift; - if ($self->[0] > 0) { - --$self->[0]; - return 1; - } else { - return 0; - } +sub guard { + &down; + bless [$_[0]], Coro::Semaphore::guard:: } -1; +#=item $guard = $sem->timed_guard ($timeout) +# +#Like C, but returns undef if semaphore couldn't be acquired within +#$timeout seconds, otherwise the guard object. + +#sub timed_guard { +# &timed_down +# ? bless \\$_[0], Coro::Semaphore::guard:: +# : (); +#} + +sub Coro::Semaphore::guard::DESTROY { + &up($_[0][0]); +} =back =head1 AUTHOR - Marc Lehmann - http://www.goof.com/pcg/marc/ + Marc Lehmann + http://home.schmorp.de/ =cut +1 +