diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index 7ffa87d..e958407 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_relocatable(&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_relocatable(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_relocatable(&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_relocatable(&lock->waiters, &MyProc->lwWaitLink); else - dlist_push_tail(&lock->waiters, &MyProc->lwWaitLink); + dlist_push_tail_relocatable(&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_relocatable(iter, &lock->waiters) { PGPROC *proc = dlist_container(PGPROC, lwWaitLink, iter.cur); if (proc == MyProc) { found = true; - dlist_delete(&proc->lwWaitLink); + dlist_delete_relocatable(&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_relocatable(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_relocatable(&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..345cc61 100644 --- a/src/include/lib/ilist.h +++ b/src/include/lib/ilist.h @@ -25,6 +25,12 @@ * 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_relocatable and then using only the _relocatable + * variants of the dlist functions, circularity can be avoided. This is + * useful for cases where a dlist_head object is mapped into different + * backends at different addresses, since the list is terminated by NULL + * pointers rather than pointers to the head object. + * * EXAMPLES * * Here's a simple example demonstrating how this can be used. Let's assume @@ -281,6 +287,15 @@ dlist_init(dlist_head *head) } /* + * Initialize a doubly linked list with non-circular layout. + */ +static inline void +dlist_init_relocatable(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 +326,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_relocatable(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 +371,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_relocatable(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 +433,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_relocatable(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 +629,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_relocatable(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.