On Wed, 2004-12-01 at 21:51 -0500, Bruce Momjian wrote:
> Neil, where are we on this? Should we add comments? Add a TODO? A patch?
I'm not sure what the right resolution is. As I said, I don't think it's
wise to apply a patch that could have a significant impact on
performance without (a) testing its performance effect and/or (b) having
any evidence that the problem it addresses actually effects anyone in
the real world. I'll try to run some benchmarks when I get a chance.
I wrote up most of a patch to implement the "wake up all shared wakers
on LWLockRelease()" behavior to see how that would change performance,
but the patch has a subtle bug in it that I can't seem to find (I've
attached it -- comments welcome).
Certainly if we decide to leave things as they are I think we ought to
document why the behavior is intentional, but I don't think we have
enough data to make that decision yet.
-Neil
--- src/backend/storage/lmgr/lwlock.c
+++ src/backend/storage/lmgr/lwlock.c
@@ -411,7 +411,7 @@
LWLockRelease(LWLockId lockid)
{
volatile LWLock *lock = LWLockArray + lockid;
- PGPROC *head;
+ PGPROC *release = NULL;
PGPROC *proc;
int i;
@@ -450,34 +450,67 @@
* any waiters if someone has already awakened waiters that haven't
* yet acquired the lock.
*/
- head = lock->head;
- if (head != NULL)
+ if (lock->head != NULL)
{
if (lock->exclusive == 0 && lock->shared == 0 && lock->releaseOK)
{
/*
- * Remove the to-be-awakened PGPROCs from the queue. If the
- * front waiter wants exclusive lock, awaken him only.
- * Otherwise awaken as many waiters as want shared access.
+ * Remove the to-be-awakened PGPROCs from the queue. If
+ * the front waiter wants an exclusive lock, awaken him
+ * only. Otherwise, awaken all the shared waiters in the
+ * queue.
*/
- proc = head;
- if (!proc->lwExclusive)
+ proc = lock->head;
+ if (proc->lwExclusive)
{
- while (proc->lwWaitLink != NULL &&
- !proc->lwWaitLink->lwExclusive)
+ lock->head = proc->lwWaitLink;
+ proc->lwWaitLink = NULL;
+ release = proc;
+ }
+ else
+ {
+ /*
+ * Walk through the wait queue. We form two linked
+ * lists: exclusive waiters, who will be the lock's
+ * new wait queue, and shared waiters, all of whom
+ * will be awaken.
+ */
+ PGPROC *exclusive_head = NULL;
+ PGPROC *exclusive_tail = NULL;
+ PGPROC *shared_head = proc;
+ PGPROC *shared_tail = proc;
+ proc = proc->lwWaitLink;
+ while (proc != NULL)
+ {
+ if (proc->lwExclusive)
+ {
+ if (!exclusive_head)
+ exclusive_head = exclusive_tail = proc;
+ else
+ {
+ exclusive_tail->lwWaitLink = proc;
+ exclusive_tail = proc;
+ }
+ }
+ else
+ {
+ shared_tail->lwWaitLink = proc;
+ shared_tail = proc;
+ }
proc = proc->lwWaitLink;
+ }
+
+ /* NULL terminate both lists */
+ shared_tail->lwWaitLink = NULL;
+ if (exclusive_tail)
+ exclusive_tail->lwWaitLink = NULL;
+ /* The exclusive list is the new wait queue */
+ lock->head = exclusive_head;
+ release = shared_head;
}
- /* proc is now the last PGPROC to be released */
- lock->head = proc->lwWaitLink;
- proc->lwWaitLink = NULL;
/* prevent additional wakeups until retryer gets to run */
lock->releaseOK = false;
}
- else
- {
- /* lock is still held, can't awaken anything */
- head = NULL;
- }
}
/* We are done updating shared state of the lock itself. */
@@ -486,11 +519,11 @@
/*
* Awaken any waiters I removed from the queue.
*/
- while (head != NULL)
+ while (release != NULL)
{
LOG_LWDEBUG("LWLockRelease", lockid, "release waiter");
- proc = head;
- head = proc->lwWaitLink;
+ proc = release;
+ release = proc->lwWaitLink;
proc->lwWaitLink = NULL;
proc->lwWaiting = false;
PGSemaphoreUnlock(&proc->sem);
---------------------------(end of broadcast)---------------------------
TIP 4: Don't 'kill -9' the postmaster