--- Coro/Coro/Semaphore.pm 2001/09/24 01:36:20 1.21 +++ Coro/Coro/Semaphore.pm 2013/05/08 00:55:41 1.130 @@ -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]; @@ -19,25 +19,30 @@ 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 -can C it. +associated with semaphores, so one thread can C it while another can +C it (or vice versa), C can be called before C and so on: +the semaphore is really just an integer counter that optionally blocks +when it is 0. 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. +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; -no warnings qw(uninitialized); +use common::sense; use Coro (); -$VERSION = 0.5; +our $VERSION = 6.29; =item new [inital count] @@ -46,65 +51,85 @@ 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 { - while ($_[0][0] <= 0) { - push @{$_[0][1]}, $Coro::current; - Coro::schedule; - } - --$_[0][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 { - 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 -semaphore. - -=cut - -sub waiters { - @{$_[0][1]}; -} +In scalar context, returns the number of threads waiting for this +semaphore. Might accidentally cause WW3 if called in other contexts, so +don't use these. =item $guard = $sem->guard @@ -115,23 +140,32 @@ 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 Coro::Semaphore::Guard::DESTROY { - &up(${${$_[0]}}); -} +#=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:: +# : (); +#} -1; +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 +