diff options
author | davidxu <davidxu@FreeBSD.org> | 2010-02-03 03:56:32 +0000 |
---|---|---|
committer | davidxu <davidxu@FreeBSD.org> | 2010-02-03 03:56:32 +0000 |
commit | 324cd07ff3ef4ca1ff19b51105bff7d2b63c2ef2 (patch) | |
tree | 1ed05cd59ae33319068ffb7c1da01d9034a93f23 /sys/kern/kern_umtx.c | |
parent | b1734cfc4af9b14e503bb9e2f4e71638ad196601 (diff) | |
download | FreeBSD-src-324cd07ff3ef4ca1ff19b51105bff7d2b63c2ef2.zip FreeBSD-src-324cd07ff3ef4ca1ff19b51105bff7d2b63c2ef2.tar.gz |
After busied the lock, re-read state word before checking waiters flag,
otherwise, the waiters bit may not be set and a wakeup is lost.
Submitted by: justin.teller at gmail dot com
MFC after: 3 days
Diffstat (limited to 'sys/kern/kern_umtx.c')
-rw-r--r-- | sys/kern/kern_umtx.c | 12 |
1 files changed, 12 insertions, 0 deletions
diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c index 71287c4..135f7c7 100644 --- a/sys/kern/kern_umtx.c +++ b/sys/kern/kern_umtx.c @@ -2526,6 +2526,12 @@ do_rw_rdlock(struct thread *td, struct urwlock *rwlock, long fflag, int timo) umtxq_busy(&uq->uq_key); umtxq_unlock(&uq->uq_key); + /* + * re-read the state, in case it changed between the try-lock above + * and the check below + */ + state = fuword32(__DEVOLATILE(int32_t *, &rwlock->rw_state)); + /* set read contention bit */ while ((state & wrflags) && !(state & URWLOCK_READ_WAITERS)) { oldstate = casuword32(&rwlock->rw_state, state, state | URWLOCK_READ_WAITERS); @@ -2658,6 +2664,12 @@ do_rw_wrlock(struct thread *td, struct urwlock *rwlock, int timo) umtxq_busy(&uq->uq_key); umtxq_unlock(&uq->uq_key); + /* + * re-read the state, in case it changed between the try-lock above + * and the check below + */ + state = fuword32(__DEVOLATILE(int32_t *, &rwlock->rw_state)); + while (((state & URWLOCK_WRITE_OWNER) || URWLOCK_READER_COUNT(state) != 0) && (state & URWLOCK_WRITE_WAITERS) == 0) { oldstate = casuword32(&rwlock->rw_state, state, state | URWLOCK_WRITE_WAITERS); |