From f43df64325efb80fc48a85009df016477238b21b Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Mon, 10 Aug 2015 23:13:41 +0200 Subject: 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. --- sys/src/libc/9sys/qlock.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'sys/src/libc') 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); } -- cgit v1.2.3