--- Coro/Coro/Semaphore.pm 2004/08/10 01:56:30 1.41 +++ Coro/Coro/Semaphore.pm 2009/07/28 02:04:21 1.100 @@ -1,6 +1,6 @@ =head1 NAME -Coro::Semaphore - non-binary semaphores +Coro::Semaphore - counting semaphores =head1 SYNOPSIS @@ -19,12 +19,12 @@ 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 coroutine can C it while another +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. Coroutines then increment the count when resources are added +resources. Threads then increment the count when resources are added and decrement the count when resources are removed. =over 4 @@ -33,11 +33,11 @@ package Coro::Semaphore; -BEGIN { eval { require warnings } && warnings->unimport ("uninitialized") } +no warnings; use Coro (); -$VERSION = 1.0; +$VERSION = 5.162; =item new [inital count] @@ -46,131 +46,120 @@ 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 $status = $sem->timed_down($timeout) +=item $sem->wait -Like C, but returns false if semaphore couldn't be acquired within -$timeout seconds, otherwise true. +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 { - while ($_[0][0] <= 0) { - push @{$_[0][1]}, $Coro::current; - Coro::schedule; - } - --$_[0][0]; -} - -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 $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 { - if (++$_[0][0] > 0) { - (shift @{$_[0][1]})->ready if @{$_[0][1]}; - } -} - =item $sem->try Try to C the semaphore. Returns true when this was possible, otherwise return false and leave the semaphore unchanged. -=cut - -sub try { - if ($_[0][0] > 0) { - --$_[0][0]; - return 1; - } else { - return 0; - } -} - =item $sem->waiters -In scalar context, returns the number of coroutines waiting for this +In scalar context, returns the number of threads waiting for this semaphore. -=cut - -sub waiters { - @{$_[0][1]}; -} - =item $guard = $sem->guard This method calls C and then creates a guard object. When the guard object is destroyed it automatically calls C. -=item $guard = $sem->timed_guard($timeout) - -Like C, but returns undef if semaphore couldn't be acquired within -$timeout seconds, otherwise the guard object. - =cut sub guard { &down; - # double indirection because bless works on the referenced - # object, not (only) on the reference itself. - bless \\$_[0], Coro::Semaphore::guard::; + bless [$_[0]], Coro::Semaphore::guard:: } -sub timed_guard { - &timed_down - ? bless \\$_[0], Coro::Semaphore::guard:: - : (); -} +#=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]}}); + &up($_[0][0]); } -1; - =back =head1 AUTHOR - Marc Lehmann - http://www.goof.com/pcg/marc/ + Marc Lehmann + http://home.schmorp.de/ =cut +1 +