diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index 7ffa87d..a408019 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -84,6 +84,7 @@ #include "storage/ipc.h" #include "storage/predicate.h" #include "storage/proc.h" +#include "storage/proclist.h" #include "storage/spin.h" #include "utils/memutils.h" @@ -717,7 +718,7 @@ LWLockInitialize(LWLock *lock, int tranche_id) pg_atomic_init_u32(&lock->nwaiters, 0); #endif lock->tranche = tranche_id; - dlist_init(&lock->waiters); + proclist_init(&lock->waiters, offsetof(PGPROC, lwWaitLink)); } /* @@ -920,25 +921,25 @@ LWLockWakeup(LWLock *lock) { bool new_release_ok; bool wokeup_somebody = false; - dlist_head wakeup; - dlist_mutable_iter iter; + proclist_head wakeup; + proclist_mutable_iter iter; - dlist_init(&wakeup); + proclist_init(&wakeup, offsetof(PGPROC, lwWaitLink)); new_release_ok = true; /* lock wait list while collecting backends to wake up */ LWLockWaitListLock(lock); - dlist_foreach_modify(iter, &lock->waiters) + proclist_foreach_modify(iter, &lock->waiters) { - PGPROC *waiter = dlist_container(PGPROC, lwWaitLink, iter.cur); + PGPROC *waiter = GetPGProcByNumber(iter.cur); if (wokeup_somebody && waiter->lwWaitMode == LW_EXCLUSIVE) continue; - dlist_delete(&waiter->lwWaitLink); - dlist_push_tail(&wakeup, &waiter->lwWaitLink); + proclist_delete(&lock->waiters, iter.cur); + proclist_push_tail(&wakeup, iter.cur); if (waiter->lwWaitMode != LW_WAIT_UNTIL_FREE) { @@ -963,7 +964,7 @@ LWLockWakeup(LWLock *lock) break; } - Assert(dlist_is_empty(&wakeup) || pg_atomic_read_u32(&lock->state) & LW_FLAG_HAS_WAITERS); + Assert(proclist_is_empty(&wakeup) || pg_atomic_read_u32(&lock->state) & LW_FLAG_HAS_WAITERS); /* unset required flags, and release lock, in one fell swoop */ { @@ -982,7 +983,7 @@ LWLockWakeup(LWLock *lock) else desired_state &= ~LW_FLAG_RELEASE_OK; - if (dlist_is_empty(&wakeup)) + if (proclist_is_empty(&wakeup)) desired_state &= ~LW_FLAG_HAS_WAITERS; desired_state &= ~LW_FLAG_LOCKED; /* release lock */ @@ -994,12 +995,12 @@ LWLockWakeup(LWLock *lock) } /* Awaken any waiters I removed from the queue. */ - dlist_foreach_modify(iter, &wakeup) + proclist_foreach_modify(iter, &wakeup) { - PGPROC *waiter = dlist_container(PGPROC, lwWaitLink, iter.cur); + PGPROC *waiter = GetPGProcByNumber(iter.cur); LOG_LWDEBUG("LWLockRelease", lock, "release waiter"); - dlist_delete(&waiter->lwWaitLink); + proclist_delete(&wakeup, iter.cur); /* * Guarantee that lwWaiting being unset only becomes visible once the @@ -1046,9 +1047,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); + proclist_push_head(&lock->waiters, MyProc->pgprocno); else - dlist_push_tail(&lock->waiters, &MyProc->lwWaitLink); + proclist_push_tail(&lock->waiters, MyProc->pgprocno); /* Can release the mutex now */ LWLockWaitListUnlock(lock); @@ -1070,7 +1071,7 @@ static void LWLockDequeueSelf(LWLock *lock) { bool found = false; - dlist_mutable_iter iter; + proclist_mutable_iter iter; #ifdef LWLOCK_STATS lwlock_stats *lwstats; @@ -1086,19 +1087,17 @@ 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) + proclist_foreach_modify(iter, &lock->waiters) { - PGPROC *proc = dlist_container(PGPROC, lwWaitLink, iter.cur); - - if (proc == MyProc) + if (iter.cur == MyProc->pgprocno) { found = true; - dlist_delete(&proc->lwWaitLink); + proclist_delete(&lock->waiters, iter.cur); break; } } - if (dlist_is_empty(&lock->waiters) && + if (proclist_is_empty(&lock->waiters) && (pg_atomic_read_u32(&lock->state) & LW_FLAG_HAS_WAITERS) != 0) { pg_atomic_fetch_and_u32(&lock->state, ~LW_FLAG_HAS_WAITERS); @@ -1719,12 +1718,12 @@ LWLockWaitForVar(LWLock *lock, uint64 *valptr, uint64 oldval, uint64 *newval) void LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val) { - dlist_head wakeup; - dlist_mutable_iter iter; + proclist_head wakeup; + proclist_mutable_iter iter; PRINT_LWDEBUG("LWLockUpdateVar", lock, LW_EXCLUSIVE); - dlist_init(&wakeup); + proclist_init(&wakeup, offsetof(PGPROC, lwWaitLink)); LWLockWaitListLock(lock); @@ -1737,15 +1736,15 @@ 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) + proclist_foreach_modify(iter, &lock->waiters) { - PGPROC *waiter = dlist_container(PGPROC, lwWaitLink, iter.cur); + PGPROC *waiter = GetPGProcByNumber(iter.cur); if (waiter->lwWaitMode != LW_WAIT_UNTIL_FREE) break; - dlist_delete(&waiter->lwWaitLink); - dlist_push_tail(&wakeup, &waiter->lwWaitLink); + proclist_delete(&lock->waiters, iter.cur); + proclist_push_tail(&wakeup, iter.cur); } /* We are done updating shared state of the lock itself. */ @@ -1754,11 +1753,11 @@ LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val) /* * Awaken any waiters I removed from the queue. */ - dlist_foreach_modify(iter, &wakeup) + proclist_foreach_modify(iter, &wakeup) { - PGPROC *waiter = dlist_container(PGPROC, lwWaitLink, iter.cur); + PGPROC *waiter = GetPGProcByNumber(iter.cur); - dlist_delete(&waiter->lwWaitLink); + proclist_delete(&wakeup, iter.cur); /* check comment in LWLockWakeup() about this barrier */ pg_write_barrier(); waiter->lwWaiting = false; diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index 3db11e4..959f5f1 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -18,7 +18,7 @@ #error "lwlock.h may not be included from frontend code" #endif -#include "lib/ilist.h" +#include "storage/proclist_types.h" #include "storage/s_lock.h" #include "port/atomics.h" @@ -59,7 +59,7 @@ typedef struct LWLock { uint16 tranche; /* tranche ID */ pg_atomic_uint32 state; /* state of exclusive/nonexclusive lockers */ - dlist_head waiters; /* list of waiting PGPROCs */ + proclist_head waiters; /* list of waiting PGPROCs */ #ifdef LOCK_DEBUG pg_atomic_uint32 nwaiters; /* number of waiters */ struct PGPROC *owner; /* last exclusive owner of the lock */ diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 775c66a..87a8e4c 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -19,6 +19,8 @@ #include "storage/latch.h" #include "storage/lock.h" #include "storage/pg_sema.h" +#include "storage/proclist_types.h" +#include "storage/shmem.h" /* * Each backend advertises up to PGPROC_MAX_CACHED_SUBXIDS TransactionIds @@ -112,7 +114,7 @@ struct PGPROC /* Info about LWLock the process is currently waiting for, if any. */ bool lwWaiting; /* true if waiting for an LW lock */ uint8 lwWaitMode; /* lwlock mode being waited for */ - dlist_node lwWaitLink; /* position in LW lock wait list */ + proclist_node lwWaitLink; /* position in LW lock wait list */ /* Info about lock the process is currently waiting for, if any. */ /* waitLock and waitProcLock are NULL if not currently waiting. */ @@ -243,6 +245,9 @@ extern PROC_HDR *ProcGlobal; extern PGPROC *PreparedXactProcs; +/* Accessor for PGPROC given a pgprocno. */ +#define GetPGProcByNumber(n) (&ProcGlobal->allProcs[(n)]) + /* * We set aside some extra PGPROC structures for auxiliary processes, * ie things that aren't full-fledged backends but need shmem access. diff --git a/src/include/storage/proclist.h b/src/include/storage/proclist.h new file mode 100644 index 0000000..32569fd --- /dev/null +++ b/src/include/storage/proclist.h @@ -0,0 +1,142 @@ +/*------------------------------------------------------------------------- + * + * proclist.h + * operations on doubly-linked lists of pgprocnos + * + * The interface is modelled on dlist from ilist.h, but uses pgprocno + * instead of pointers and doesn't use a circular list, so that a + * proclist_head object can be mapped at different addresses in different + * backends. + * + * See proclist_types.h for the structs that these functions operate on. They + * are separated to break a header dependency cycle with proc.h. + * + * Portions Copyright (c) 2016, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/include/storage/proclist.h + *------------------------------------------------------------------------- + */ +#ifndef PROCLIST_H +#define PROCLIST_H + +#include "storage/proc.h" +#include "storage/proclist_types.h" + +/* + * Initialize a proclist. The offset to the appropriate proclist_node within + * PGPROC should be provided as offsetof(PGPROC, ). + */ +static inline void +proclist_init(proclist_head *list, size_t offset) +{ + list->head = list->tail = INVALID_PGPROCNO; + list->offset = offset; +} + +/* + * Is the list empty? + */ +static inline bool +proclist_is_empty(proclist_head *list) +{ + return list->head == INVALID_PGPROCNO; +} + +/* + * Get a pointer to the appropriate proclist_node inside a PGPROC object, + * given a procno. + */ +static inline proclist_node * +proclist_node_get(proclist_head *list, int procno) +{ + char *entry = (char *) GetPGProcByNumber(procno); + + return (proclist_node *) (entry + list->offset); +} + +/* + * Insert a node at the beginning of a list. + */ +static inline void +proclist_push_head(proclist_head *list, int procno) +{ + proclist_node *node = proclist_node_get(list, procno); + + if (list->head == INVALID_PGPROCNO) + { + Assert(list->tail == INVALID_PGPROCNO); + node->next = node->prev = INVALID_PGPROCNO; + list->head = list->tail = procno; + } + else + { + Assert(list->tail != INVALID_PGPROCNO); + node->next = list->head; + proclist_node_get(list, node->next)->prev = procno; + node->prev = INVALID_PGPROCNO; + list->head = procno; + } +} + +/* + * Insert a node a the end of a list. + */ +static inline void +proclist_push_tail(proclist_head *list, int procno) +{ + proclist_node *node = proclist_node_get(list, procno); + + if (list->tail == INVALID_PGPROCNO) + { + Assert(list->head == INVALID_PGPROCNO); + node->next = node->prev = INVALID_PGPROCNO; + list->head = list->tail = procno; + } + else + { + Assert(list->head != INVALID_PGPROCNO); + node->prev = list->tail; + proclist_node_get(list, node->prev)->next = procno; + node->next = INVALID_PGPROCNO; + list->tail = procno; + } +} + +/* + * Delete a node. The node must be in the list. + */ +static inline void +proclist_delete(proclist_head *list, int procno) +{ + proclist_node *node = proclist_node_get(list, procno); + + if (node->prev == INVALID_PGPROCNO) + list->head = node->next; + else + proclist_node_get(list, node->prev)->next = node->next; + + if (node->next == INVALID_PGPROCNO) + list->tail = node->prev; + else + proclist_node_get(list, node->next)->prev = node->prev; +} + +/* + * Iterate through the list pointed at by 'lhead', storing the current + * position in 'iter'. Access the current position with iter.cur. + * + * The only list modification allowed while iterating is deleting the current + * node with proclist_delete(list, iter.cur). + */ +#define proclist_foreach_modify(iter, lhead) \ + for (AssertVariableIsOfTypeMacro(iter, proclist_mutable_iter), \ + AssertVariableIsOfTypeMacro(lhead, proclist_head *), \ + (iter).cur = (lhead)->head, \ + (iter).next = (iter).cur == INVALID_PGPROCNO ? INVALID_PGPROCNO : \ + proclist_node_get(lhead, (iter).cur)->next; \ + (iter).cur != INVALID_PGPROCNO; \ + (iter).cur = (iter).next, \ + (iter).next = proclist_node_get(lhead, (iter).cur)->next) \ + +#endif diff --git a/src/include/storage/proclist_types.h b/src/include/storage/proclist_types.h new file mode 100644 index 0000000..3b3ca99 --- /dev/null +++ b/src/include/storage/proclist_types.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------- + * + * proclist_types.h + * doubly-linked lists of pgprocnos + * + * See proclist.h for functions that operate on these types. + * + * Portions Copyright (c) 2016, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/include/storage/proclist_types.h + *------------------------------------------------------------------------- + */ + +#ifndef PROCLIST_TYPES_H +#define PROCLIST_TYPES_H + +#include + +/* + * A node in a list of processes. + */ +typedef struct proclist_node +{ + int next; /* pgprocno of the next PGPROC */ + int prev; /* pgprocno of the prev PGPROC */ +} proclist_node; + +/* + * Head of a doubly-linked list of PGPROCs, identified by pgprocno. + */ +typedef struct proclist_head +{ + size_t offset; /* offset in PGPROC of the proclist node */ + int head; /* pgprocno of the head PGPROC */ + int tail; /* pgprocno of the tail PGPROC */ +} proclist_head; + +/* + * List iterator allowing some modifications while iterating. + */ +typedef struct proclist_mutable_iter +{ + int cur; /* pgprocno of the current PGPROC */ + int next; /* pgprocno of the next PGPROC */ +} proclist_mutable_iter; + +#endif diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 0c61fc2..ae19379 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2660,6 +2660,9 @@ printTextRule priv_map process_file_callback_t process_sublinks_context +proclist_head +proclist_mutable_iter +proclist_node promptStatus_t pthread_attr_t pthread_key_t