diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 7ffa87d..dbfddd9 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -717,7 +717,7 @@ LWLockInitialize(LWLock *lock, int tranche_id)
 	pg_atomic_init_u32(&lock->nwaiters, 0);
 #endif
 	lock->tranche = tranche_id;
-	dlist_init(&lock->waiters);
+	dlist_init_noncircular(&lock->waiters);
 }
 
 /*
@@ -930,14 +930,14 @@ LWLockWakeup(LWLock *lock)
 	/* lock wait list while collecting backends to wake up */
 	LWLockWaitListLock(lock);
 
-	dlist_foreach_modify(iter, &lock->waiters)
+	dlist_foreach_modify_noncircular(iter, &lock->waiters)
 	{
 		PGPROC	   *waiter = dlist_container(PGPROC, lwWaitLink, iter.cur);
 
 		if (wokeup_somebody && waiter->lwWaitMode == LW_EXCLUSIVE)
 			continue;
 
-		dlist_delete(&waiter->lwWaitLink);
+		dlist_delete_noncircular(&lock->waiters, &waiter->lwWaitLink);
 		dlist_push_tail(&wakeup, &waiter->lwWaitLink);
 
 		if (waiter->lwWaitMode != LW_WAIT_UNTIL_FREE)
@@ -1046,9 +1046,9 @@ LWLockQueueSelf(LWLock *lock, LWLockMode mode)
 
 	/* LW_WAIT_UNTIL_FREE waiters are always at the front of the queue */
 	if (mode == LW_WAIT_UNTIL_FREE)
-		dlist_push_head(&lock->waiters, &MyProc->lwWaitLink);
+		dlist_push_head_noncircular(&lock->waiters, &MyProc->lwWaitLink);
 	else
-		dlist_push_tail(&lock->waiters, &MyProc->lwWaitLink);
+		dlist_push_tail_noncircular(&lock->waiters, &MyProc->lwWaitLink);
 
 	/* Can release the mutex now */
 	LWLockWaitListUnlock(lock);
@@ -1086,14 +1086,14 @@ LWLockDequeueSelf(LWLock *lock)
 	 * Can't just remove ourselves from the list, but we need to iterate over
 	 * all entries as somebody else could have unqueued us.
 	 */
-	dlist_foreach_modify(iter, &lock->waiters)
+	dlist_foreach_modify_noncircular(iter, &lock->waiters)
 	{
 		PGPROC	   *proc = dlist_container(PGPROC, lwWaitLink, iter.cur);
 
 		if (proc == MyProc)
 		{
 			found = true;
-			dlist_delete(&proc->lwWaitLink);
+			dlist_delete_noncircular(&lock->waiters, &proc->lwWaitLink);
 			break;
 		}
 	}
@@ -1737,14 +1737,14 @@ LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val)
 	 * See if there are any LW_WAIT_UNTIL_FREE waiters that need to be woken
 	 * up. They are always in the front of the queue.
 	 */
-	dlist_foreach_modify(iter, &lock->waiters)
+	dlist_foreach_modify_noncircular(iter, &lock->waiters)
 	{
 		PGPROC	   *waiter = dlist_container(PGPROC, lwWaitLink, iter.cur);
 
 		if (waiter->lwWaitMode != LW_WAIT_UNTIL_FREE)
 			break;
 
-		dlist_delete(&waiter->lwWaitLink);
+		dlist_delete_noncircular(&lock->waiters, &waiter->lwWaitLink);
 		dlist_push_tail(&wakeup, &waiter->lwWaitLink);
 	}
 
diff --git a/src/include/lib/ilist.h b/src/include/lib/ilist.h
index e189e4f..12edb05 100644
--- a/src/include/lib/ilist.h
+++ b/src/include/lib/ilist.h
@@ -25,6 +25,14 @@
  * initialize list headers to zeroes rather than setting them up with an
  * explicit initialization function, so we also allow the other case.
  *
+ * By using the dlist_init_noncircular and then using only the _noncircular
+ * variants of the dlist functions, circularity can be avoided.  This is
+ * useful for lists in dynamic shared memory where the dlist_head object may
+ * be mapped into different backends at different addresses, since the list is
+ * terminated by NULL pointers rather than pointers to the head object.  Other
+ * nodes must still be mapped at the same address in each backend in this
+ * case.
+ *
  * EXAMPLES
  *
  * Here's a simple example demonstrating how this can be used.  Let's assume
@@ -281,6 +289,15 @@ dlist_init(dlist_head *head)
 }
 
 /*
+ * Initialize a doubly linked list with non-circular layout.
+ */
+static inline void
+dlist_init_noncircular(dlist_head *head)
+{
+	head->head.next = head->head.prev = NULL;
+}
+
+/*
  * Is the list empty?
  *
  * An empty list has either its first 'next' pointer set to NULL, or to itself.
@@ -311,6 +328,34 @@ dlist_push_head(dlist_head *head, dlist_node *node)
 }
 
 /*
+ * Insert a node at the beginning of a list, without converting it to circular
+ * layout.
+ */
+static inline void
+dlist_push_head_noncircular(dlist_head *head, dlist_node *node)
+{
+	Assert(head->head.next != &head->head);
+	Assert(head->head.prev != &head->head);
+
+	if (head->head.next == NULL)
+	{
+		/* It was empty, and this node becomes the only entry. */
+		Assert(head->head.prev == NULL);
+		node->next = node->prev = NULL;
+		head->head.next = head->head.prev = node;
+	}
+	else
+	{
+		/* Insert before head. */
+		Assert(head->head.prev != NULL);
+		node->next = head->head.next;
+		node->next->prev = node;
+		node->prev = NULL;
+		head->head.next = node;
+	}
+}
+
+/*
  * Insert a node at the end of the list.
  */
 static inline void
@@ -328,6 +373,34 @@ dlist_push_tail(dlist_head *head, dlist_node *node)
 }
 
 /*
+ * Insert a node at the end of a list, without converting it to circular
+ * layout.
+ */
+static inline void
+dlist_push_tail_noncircular(dlist_head *head, dlist_node *node)
+{
+	Assert(head->head.next != &head->head);
+	Assert(head->head.prev != &head->head);
+
+	if (head->head.prev == NULL)
+	{
+		/* It was empty, and this node becomes the only entry. */
+		Assert(head->head.next == NULL);
+		node->next = node->prev = NULL;
+		head->head.next = head->head.prev = node;
+	}
+	else
+	{
+		/* Insert after tail. */
+		Assert(head->head.next != NULL);
+		node->prev = head->head.prev;
+		node->prev->next = node;
+		node->next = NULL;
+		head->head.prev = node;
+	}
+}
+
+/*
  * Insert a node after another *in the same list*
  */
 static inline void
@@ -362,6 +435,33 @@ dlist_delete(dlist_node *node)
 }
 
 /*
+ * Delete 'node' from 'list' (it must be in that list, which must be
+ * noncircular).
+ */
+static inline void
+dlist_delete_noncircular(dlist_head *head, dlist_node *node)
+{
+	Assert(head->head.next != &head->head);
+	Assert(head->head.prev != &head->head);
+
+	if (node->prev == NULL)
+	{
+		Assert(head->head.next == node);
+		head->head.next = node->next;
+	}
+	else
+		node->prev->next = node->next;
+
+	if (node->next == NULL)
+	{
+		Assert(head->head.prev == node);
+		head->head.prev = node->prev;
+	}
+	else
+		node->next->prev = node->prev;
+}
+
+/*
  * Remove and return the first node from a list (there must be one).
  */
 static inline dlist_node *
@@ -531,6 +631,18 @@ dlist_tail_node(dlist_head *head)
 		 (iter).cur = (iter).next, (iter).next = (iter).cur->next)
 
 /*
+ * Like dlist_foreach_modify, but for noncircular lists.
+ */
+#define dlist_foreach_modify_noncircular(iter, lhead)						\
+	for (AssertVariableIsOfTypeMacro(iter, dlist_mutable_iter),				\
+		 AssertVariableIsOfTypeMacro(lhead, dlist_head *),					\
+		 (iter).cur = (lhead)->head.next,									\
+		 (iter).next = (iter).cur == NULL ? NULL : (iter).cur->next;		\
+		 (iter).cur != NULL;												\
+		 (iter).cur = (iter).next,											\
+		 (iter).next = (iter).cur == NULL ? NULL : (iter).cur->next)
+
+/*
  * Iterate through the list in reverse order.
  *
  * It is *not* allowed to manipulate the list during iteration.
