diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2015-08-10 23:13:41 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2015-08-10 23:13:41 +0200 |
commit | f43df64325efb80fc48a85009df016477238b21b (patch) | |
tree | c6672fcbeea366a8f46da57346f3c9b72adf5810 /sys/src/libc | |
parent | bc895417f804bbb78410a365412bcf8ab59a44b4 (diff) |
libc: fix wunlock() libthread deadlock
when wunlock() was used by threads running within the same proc,
the wunlock() can deadlock as it keeps holding the RWLock.lock
spinlock while indirectly calling _threadrendezvous(). when
_threadrendezvous() switches to another thread in the same proc,
then that thread can hang at rlock()/wlock()/runlock() again
waiting for wunlock() to release the spinlock which will never
happen as lock() does not schedule threads.
wunlock() is changed to release the spinlock during rendezvous
wakeup of readers. note that this is a bit dangerous as more
readers might queue concurrently now which means that if
we cannot keep up with the wakeups, we might keep on waking
readers forever. that will be another patch for the future.
Diffstat (limited to 'sys/src/libc')
-rw-r--r-- | sys/src/libc/9sys/qlock.c | 17 |
1 files changed, 9 insertions, 8 deletions
diff --git a/sys/src/libc/9sys/qlock.c b/sys/src/libc/9sys/qlock.c index fb2d80238..4f03cd38c 100644 --- a/sys/src/libc/9sys/qlock.c +++ b/sys/src/libc/9sys/qlock.c @@ -58,7 +58,6 @@ qlock(QLock *q) return; } - /* chain into waiting list */ mp = getqlp(); p = q->tail; @@ -259,17 +258,19 @@ wunlock(RWLock *q) if(p->state != QueuingR) abort(); - /* wake waiting readers */ - while(q->head != nil && q->head->state == QueuingR){ - p = q->head; + q->writer = 0; + do { + /* wake waiting readers */ q->head = p->next; + if(q->head == nil) + q->tail = nil; q->readers++; + unlock(&q->lock); while((*_rendezvousp)(p, 0) == (void*)~0) ; - } - if(q->head == nil) - q->tail = nil; - q->writer = 0; + lock(&q->lock); + p = q->head; + } while(p != nil && p->state == QueuingR && q->writer == 0); unlock(&q->lock); } |