ref: 4fc4b0dda73c8a04caff079ea358c53ed3dbfc71
parent: 83fe7aaa5ce4776c394cb9edd89189b62efb89a9
author: cinap_lenrek <[email protected]>
date: Wed Oct 25 22:42:26 EDT 2017
libc: wunlock() part 2 the initial issue was that wunlock() would wakeup readers while holding the spinlock causing deadlock in libthread programs where rendezvous() would do a thread switch within the same process which then can acquire the RWLock again. the first fix tried to prevent holding the spinlock, waking up one reader at a time with releasing an re-acquiering the spinlock. this violates the invariant that readers can only wakup writers in runlock() when multiple readers where queued at the time of wunlock(). at the first wakeup, q->head != nil so runlock() would find a reader queued on runlock() when it expected a writer. this (hopefully last) fix unlinks *all* the reader QLp's atomically and in order while holding the spinlock and then traverses the dequeued chain of QLp structures again to call rendezvous() so the invariant described above holds.
--- a/sys/src/libc/9sys/qlock.c
+++ b/sys/src/libc/9sys/qlock.c
@@ -175,8 +175,8 @@
if(p->state != QueuingW)
abort();
q->head = p->next;
- if(q->head == 0)
- q->tail = 0;
+ if(q->head == nil)
+ q->tail = nil;
q->writer = 1;
unlock(&q->lock);
@@ -233,7 +233,7 @@
void
wunlock(RWLock *q)
{
- QLp *p;
+ QLp *p, *x;
lock(&q->lock);
if(q->writer == 0)
@@ -254,24 +254,31 @@
;
return;
}
-
if(p->state != QueuingR)
abort();
- q->writer = 0;
- do {
- /* wake waiting readers */
- q->head = p->next;
- if(q->head == nil)
- q->tail = nil;
+ /* collect waiting readers */
+ q->readers = 1;
+ for(x = p->next; x != nil && x->state == QueuingR; x = x->next){
q->readers++;
- unlock(&q->lock);
+ p = x;
+ }
+ p->next = nil;
+ p = q->head;
+
+ /* queue remaining writers */
+ q->head = x;
+ if(x == nil)
+ q->tail = nil;
+ q->writer = 0;
+ unlock(&q->lock);
+
+ /* wakeup waiting readers */
+ for(; p != nil; p = x){
+ x = p->next;
while((*_rendezvousp)(p, 0) == (void*)~0)
;
- lock(&q->lock);
- p = q->head;
- } while(p != nil && p->state == QueuingR && q->writer == 0);
- unlock(&q->lock);
+ }
}
void