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

Reply via email to