Skip to content

Commit

Permalink
kernel - Fix condvar races
Browse files Browse the repository at this point in the history
* Interlocked sleep was not using the PINTERLOCKED flag.

* Misc other adjustments.
  • Loading branch information
Matthew Dillon committed Aug 21, 2012
1 parent 68cdd77 commit 42ad053
Showing 1 changed file with 19 additions and 14 deletions.
33 changes: 19 additions & 14 deletions sys/kern/kern_condvar.c
Expand Up @@ -18,20 +18,27 @@ cv_destroy(struct cv *c)
}

int
_cv_timedwait(struct cv *c, struct lock *l, int timo, int wakesig)
_cv_timedwait(struct cv *c, struct lock *lk, int timo, int wakesig)
{
int flags = wakesig ? PCATCH : 0;
int error;

spin_lock(&c->cv_lock);
/*
* Can interlock without critical section/spinlock as long
* as we don't block before calling *sleep(). PINTERLOCKED
* must be passed to the *sleep() to use the manual interlock
* (else a new one is created which opens a timing race).
*/
tsleep_interlock(c, flags);

spin_lock(&c->cv_lock);
c->cv_waiters++;
spin_unlock(&c->cv_lock);
if (l != NULL)
lockmgr(l, LK_RELEASE);
error = tsleep(c, flags, c->cv_desc, timo);
if (l != NULL)
lockmgr(l, LK_EXCLUSIVE);

if (lk)
error = lksleep(c, lk, flags | PINTERLOCKED, c->cv_desc, timo);
else
error = tsleep(c, flags | PINTERLOCKED, c->cv_desc, timo);

return (error);
}
Expand All @@ -40,19 +47,17 @@ void
_cv_signal(struct cv *c, int broadcast)
{
spin_lock(&c->cv_lock);
if (c->cv_waiters == 0)
goto out;

if (broadcast) {
if (c->cv_waiters == 0) {
spin_unlock(&c->cv_lock);
} else if (broadcast) {
c->cv_waiters = 0;
spin_unlock(&c->cv_lock); /* must unlock first */
wakeup(c);
} else {
c->cv_waiters--;
spin_unlock(&c->cv_lock); /* must unlock first */
wakeup_one(c);
}

out:
spin_unlock(&c->cv_lock);
}

int
Expand Down

0 comments on commit 42ad053

Please sign in to comment.