Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[PATCH] cond_wait() and arbitrary lock variables

0 views
Skip to first unread message

Mike Pomraning

unread,
Nov 24, 2003, 8:52:24 AM11/24/03
to perl5-...@perl.org
The following patch against 5.8.2 allows a shared variable to be cond_wait()d
with a distinct, shared lock variable:

use threads::shared;

cond_wait($v); # $v is both condition and mutex
cond_wait($v, $v); # same as above, but explicit

cond_wait($v, $lockvar); # $v's predicate is protected by $lockvar, which
# presumably protects other predicates as well

This is useful for, e.g., a Queue object which has client threads waiting on
one of two interesting predicates: "items ready" ($q->dequeue) or "queue
empty" ($q->await_empty).


--- ext/threads/shared/shared.xs.orig 2003-11-02 15:50:30.000000000 -0600
+++ ext/threads/shared/shared.xs 2003-11-23 23:26:32.000000000 -0600
@@ -1039,21 +1039,38 @@
Perl_sharedsv_lock(aTHX_ shared);

void
-cond_wait_enabled(SV *ref)
- PROTOTYPE: \[$@%]
- CODE:
+cond_wait_enabled(SV *ref_cond, SV *ref_lock = 0)
+ PROTOTYPE: \[$@%];\[$@%]
+ PREINIT:
shared_sv* shared;
+ perl_cond* user_condition;
int locks;
- if(!SvROK(ref))
+ int same = 0;
+
+ CODE:
+ if (!ref_lock || ref_lock == ref_cond) same = 1;
+
+ if(!SvROK(ref_cond))
Perl_croak(aTHX_ "Argument to cond_wait needs to be passed as ref");
- ref = SvRV(ref);
- if(SvROK(ref))
- ref = SvRV(ref);
- shared = Perl_sharedsv_find(aTHX_ ref);
+ ref_cond = SvRV(ref_cond);
+ if(SvROK(ref_cond))
+ ref_cond = SvRV(ref_cond);
+ shared = Perl_sharedsv_find(aTHX_ ref_cond);
if(!shared)
croak("cond_wait can only be used on shared values");
- if(shared->lock.owner != aTHX)
- croak("You need a lock before you can cond_wait");
+
+ user_condition = &shared->user_cond;
+ if (! same) {
+ if (!SvROK(ref_lock))
+ Perl_croak(aTHX_ "cond_wait lock needs to be passed as ref");
+ ref_lock = SvRV(ref_lock);
+ if (SvROK(ref_lock)) ref_lock = SvRV(ref_lock);
+ shared = Perl_sharedsv_find(aTHX_ ref_lock);
+ if (!shared)
+ croak("cond_wait lock must be a shared value");
+ }
+ if(shared->lock.owner != aTHX)
+ croak("You need a lock before you can cond_wait");
/* Stealing the members of the lock object worries me - NI-S */
MUTEX_LOCK(&shared->lock.mutex);
shared->lock.owner = NULL;
@@ -1063,10 +1080,12 @@
/* since we are releasing the lock here we need to tell other
people that is ok to go ahead and use it */
COND_SIGNAL(&shared->lock.cond);
- COND_WAIT(&shared->user_cond, &shared->lock.mutex);
+ COND_WAIT(user_condition, &shared->lock.mutex);
while(shared->lock.owner != NULL) {
- COND_WAIT(&shared->lock.cond,&shared->lock.mutex);
- }
+ /* OK -- we got the signal, now wait for threads to give us
+ back the lock */
+ COND_WAIT(&shared->lock.cond, &shared->lock.mutex);
+ }
shared->lock.owner = aTHX;
shared->lock.locks = locks;
MUTEX_UNLOCK(&shared->lock.mutex);
--- ext/threads/shared/shared.pm.orig 2003-11-22 22:40:15.000000000 -0600
+++ ext/threads/shared/shared.pm 2003-11-23 23:09:41.000000000 -0600
@@ -24,10 +24,10 @@
# saves on average about 4K of memory per thread.

eval <<'EOD';
-sub cond_wait (\[$@%]) { undef }
-sub cond_signal (\[$@%]) { undef }
-sub cond_broadcast (\[$@%]) { undef }
-sub share (\[$@%]) { return $_[0] }
+sub cond_wait (\[$@%];\[$@%]) { undef }
+sub cond_signal (\[$@%]) { undef }
+sub cond_broadcast (\[$@%]) { undef }
+sub share (\[$@%]) { return $_[0] }
EOD
}
}
@@ -122,6 +122,8 @@

=item cond_wait VARIABLE

+=item cond_wait CONDVAR LOCKVAR
+
The C<cond_wait> function takes a B<locked> variable as a parameter,
unlocks the variable, and blocks until another thread does a
C<cond_signal> or C<cond_broadcast> for that same locked variable.
@@ -134,6 +136,11 @@
blocked wait state are atomic, The two actions of exiting from the
blocked wait state and relocking the variable are not.

+In its second form, C<cond_wait> takes a shared, B<unlocked> variable
+followed by a shared, B<locked> variable. The second variable is
+unlocked and thread execution suspended until another thread signals
+the first variable.
+
It is important to note that the variable can be notified even if
no thread C<cond_signal> or C<cond_broadcast> on the variable.
It is therefore important to check the value of the variable and

-Mike

Elizabeth Mattijsen

unread,
Nov 24, 2003, 9:15:13 AM11/24/03
to Mike Pomraning, perl5-...@perl.org
At 07:52 -0600 11/24/03, Mike Pomraning wrote:
>The following patch against 5.8.2 allows a shared variable to be cond_wait()d
>with a distinct, shared lock variable:
>
> use threads::shared;
>
> cond_wait($v); # $v is both condition and mutex
> cond_wait($v, $v); # same as above, but explicit
>
> cond_wait($v, $lockvar); # $v's predicate is protected by $lockvar, which
> # presumably protects other predicates as well
>
>This is useful for, e.g., a Queue object which has client threads waiting on
>one of two interesting predicates: "items ready" ($q->dequeue) or "queue
>empty" ($q->await_empty).

I'm probably dense, but I don't see the application of this (yet).
Could you elaborate? How would this be different from using nested
locks? Or is this a quicker way to do that?


Questions, questions, questions... ;-) Just wondering if and when
I would need to add similar functionality to forks.pm ;-)


Liz

Mike Pomraning

unread,
Nov 25, 2003, 1:12:18 AM11/25/03
to Elizabeth Mattijsen, perl5-...@perl.org
On Mon, 24 Nov 2003, Elizabeth Mattijsen wrote:

> Could you elaborate? How would this be different from using nested
> locks? Or is this a quicker way to do that?

I imagine a shared $color object with some threads awaiting state "red", some
"green", and some "blue". If $color uses a single cond+lock variable, then
some waiters will be woken up needlessly upon a color change.

If $color uses one lock for the color change and three distinct cond+lock vars
for the various states, then you have awkward and non-atomic nested
_un_locking concerns: lock $color, inspect it, and cond_wait for "red",
making sure that you've somehow unlocked both $color and the separate
cond+lock var ($color->{_red}?).

Less contrivedly, an extended queue object might want to awaken threads
whenever it is "not full" ($bq->enqueue), "not empty" ($bq->dequeue), or
"empty" ($bq->await_empty). Using three separate cond variables spares
needless wake-ups, and arbitrary lock association makes it safe, since each
waiter uses the same lock. (Apache2's apr_queue.c implements a similar queue,
with multiple conditions beneath one big mutex.)

> Questions, questions, questions... ;-) Just wondering if and when
> I would need to add similar functionality to forks.pm ;-)

Sooner rather than later, I hope. :-) I find it persuasive that the
two-argument form of cond_wait() mirrors (not just pthread_cond_wait and
cthreads' condition_wait, but also) the COND_WAIT macro inside Perl itself.

-Mike

Rafael Garcia-Suarez

unread,
Nov 30, 2003, 4:05:43 PM11/30/03
to Mike Pomraning, perl5-...@perl.org
Mike Pomraning wrote:
> The following patch against 5.8.2 allows a shared variable to be cond_wait()d
> with a distinct, shared lock variable:
>
> use threads::shared;
>
> cond_wait($v); # $v is both condition and mutex
> cond_wait($v, $v); # same as above, but explicit
>
> cond_wait($v, $lockvar); # $v's predicate is protected by $lockvar, which
> # presumably protects other predicates as well
>
> This is useful for, e.g., a Queue object which has client threads waiting on
> one of two interesting predicates: "items ready" ($q->dequeue) or "queue
> empty" ($q->await_empty).

This looks useful indeed. However, regression tests for this new feature
would be most appreciated.
(bumping the $VERSION might be a good idea as well.)

Mike Pomraning

unread,
Dec 1, 2003, 10:32:37 PM12/1/03
to Rafael Garcia-Suarez, perl5-...@perl.org
On Sun, 30 Nov 2003, Rafael Garcia-Suarez wrote:

> Mike Pomraning wrote:
> >
> > cond_wait($v, $lockvar); # $v's predicate is protected by $lockvar, which
> > # presumably protects other predicates as well

[....]


> This looks useful indeed. However, regression tests for this new feature
> would be most appreciated.
> (bumping the $VERSION might be a good idea as well.)

Wilco. I'm nearly done with cond_timedwait(), too -- impetus:
$sema->trydown($timeout) -- so that'll arrive with tests for both.

-Mike

0 new messages