From a24babfe52c7ab946f192f4872dc480ce18c8c0f Mon Sep 17 00:00:00 2001 From: David Rowley Date: Thu, 27 Oct 2022 16:01:34 +1300 Subject: [PATCH v1] Add doubly linked count list implementation We have various requirements when using a dlist_head to keep track of the number of items in the list. This, traditionally, has been done by maintaining a counter variable in the calling code. Here we tidy this up by adding "dclist", which is very similar to dlists but also keeps track of the number of items stored in the list. Callers may use the new dclist_count() function when they need to know how many items are stored. Obtaining the count is an O(1) operation. For simplicity reasons, dclists and dlists both use dlist_node as their node type and dlist_iter/dlist_mutable_iter as their iterator type. dclists have all of the same functionality as dlists except there is no function named dclist_delete(). To remove an item from a list dclist_delete_from() must be used. This requires knowing which dclist the given item is stored in. Additionally, here we also convert some dlists which keep track of the number of items stored to make them use dclists instead. --- .../replication/logical/reorderbuffer.c | 58 ++-- src/backend/replication/logical/snapbuild.c | 2 +- src/backend/utils/activity/pgstat_xact.c | 34 +- src/backend/utils/adt/ri_triggers.c | 13 +- src/include/lib/ilist.h | 313 +++++++++++++++++- src/include/replication/reorderbuffer.h | 6 +- src/include/utils/pgstat_internal.h | 3 +- src/tools/pgindent/typedefs.list | 1 + 8 files changed, 351 insertions(+), 79 deletions(-) diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index c29894041e..603c861461 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -349,8 +349,6 @@ ReorderBufferAllocate(void) buffer->by_txn_last_xid = InvalidTransactionId; buffer->by_txn_last_txn = NULL; - buffer->catchange_ntxns = 0; - buffer->outbuf = NULL; buffer->outbufsize = 0; buffer->size = 0; @@ -368,7 +366,7 @@ ReorderBufferAllocate(void) dlist_init(&buffer->toplevel_by_lsn); dlist_init(&buffer->txns_by_base_snapshot_lsn); - dlist_init(&buffer->catchange_txns); + dclist_init(&buffer->catchange_txns); /* * Ensure there's no stale data from prior uses of this slot, in case some @@ -413,7 +411,7 @@ ReorderBufferGetTXN(ReorderBuffer *rb) dlist_init(&txn->changes); dlist_init(&txn->tuplecids); - dlist_init(&txn->subtxns); + dclist_init(&txn->subtxns); /* InvalidCommandId is not zero, so set it explicitly */ txn->command_id = InvalidCommandId; @@ -1066,14 +1064,13 @@ ReorderBufferAssignChild(ReorderBuffer *rb, TransactionId xid, subtxn->txn_flags |= RBTXN_IS_SUBXACT; subtxn->toplevel_xid = xid; - Assert(subtxn->nsubtxns == 0); + Assert(dclist_count(&subtxn->subtxns) == 0); /* set the reference to top-level transaction */ subtxn->toptxn = txn; /* add to subtransaction list */ - dlist_push_tail(&txn->subtxns, &subtxn->node); - txn->nsubtxns++; + dclist_push_tail(&txn->subtxns, &subtxn->node); /* Possibly transfer the subtxn's snapshot to its top-level txn. */ ReorderBufferTransferSnapToParent(txn, subtxn); @@ -1241,7 +1238,7 @@ ReorderBufferIterTXNInit(ReorderBuffer *rb, ReorderBufferTXN *txn, if (txn->nentries > 0) nr_txns++; - dlist_foreach(cur_txn_i, &txn->subtxns) + dclist_foreach(cur_txn_i, &txn->subtxns) { ReorderBufferTXN *cur_txn; @@ -1308,7 +1305,7 @@ ReorderBufferIterTXNInit(ReorderBuffer *rb, ReorderBufferTXN *txn, } /* add subtransactions if they contain changes */ - dlist_foreach(cur_txn_i, &txn->subtxns) + dclist_foreach(cur_txn_i, &txn->subtxns) { ReorderBufferTXN *cur_txn; @@ -1477,7 +1474,7 @@ ReorderBufferCleanupTXN(ReorderBuffer *rb, ReorderBufferTXN *txn) dlist_mutable_iter iter; /* cleanup subtransactions & their changes */ - dlist_foreach_modify(iter, &txn->subtxns) + dclist_foreach_modify(iter, &txn->subtxns) { ReorderBufferTXN *subtxn; @@ -1489,7 +1486,7 @@ ReorderBufferCleanupTXN(ReorderBuffer *rb, ReorderBufferTXN *txn) * ever recurse more than one level deep here. */ Assert(rbtxn_is_known_subxact(subtxn)); - Assert(subtxn->nsubtxns == 0); + Assert(dclist_count(&subtxn->subtxns) == 0); ReorderBufferCleanupTXN(rb, subtxn); } @@ -1546,19 +1543,14 @@ ReorderBufferCleanupTXN(ReorderBuffer *rb, ReorderBufferTXN *txn) * Remove TXN from its containing lists. * * Note: if txn is known as subxact, we are deleting the TXN from its - * parent's list of known subxacts; this leaves the parent's nsubxacts + * parent's list of known subxacts; this leaves the parent's subxacts * count too high, but we don't care. Otherwise, we are deleting the TXN * from the LSN-ordered list of toplevel TXNs. We remove the TXN from the * list of catalog modifying transactions as well. */ dlist_delete(&txn->node); if (rbtxn_has_catalog_changes(txn)) - { - dlist_delete(&txn->catchange_node); - rb->catchange_ntxns--; - - Assert(rb->catchange_ntxns >= 0); - } + dclist_delete_from(&rb->catchange_txns, &txn->catchange_node); /* now remove reference from buffer */ hash_search(rb->by_txn, @@ -1592,7 +1584,7 @@ ReorderBufferTruncateTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, bool txn_prep dlist_mutable_iter iter; /* cleanup subtransactions & their changes */ - dlist_foreach_modify(iter, &txn->subtxns) + dclist_foreach_modify(iter, &txn->subtxns) { ReorderBufferTXN *subtxn; @@ -1604,7 +1596,7 @@ ReorderBufferTruncateTXN(ReorderBuffer *rb, ReorderBufferTXN *txn, bool txn_prep * ever recurse more than one level deep here. */ Assert(rbtxn_is_known_subxact(subtxn)); - Assert(subtxn->nsubtxns == 0); + Assert(dclist_count(&subtxn->subtxns) == 0); ReorderBufferTruncateTXN(rb, subtxn, txn_prepared); } @@ -1788,7 +1780,7 @@ ReorderBufferCopySnap(ReorderBuffer *rb, Snapshot orig_snap, size = sizeof(SnapshotData) + sizeof(TransactionId) * orig_snap->xcnt + - sizeof(TransactionId) * (txn->nsubtxns + 1); + sizeof(TransactionId) * (dclist_count(&txn->subtxns) + 1); snap = MemoryContextAllocZero(rb->context, size); memcpy(snap, orig_snap, sizeof(SnapshotData)); @@ -1815,7 +1807,7 @@ ReorderBufferCopySnap(ReorderBuffer *rb, Snapshot orig_snap, */ snap->subxcnt = 1; - dlist_foreach(iter, &txn->subtxns) + dclist_foreach(iter, &txn->subtxns) { ReorderBufferTXN *sub_txn; @@ -3309,8 +3301,7 @@ ReorderBufferXidSetCatalogChanges(ReorderBuffer *rb, TransactionId xid, if (!rbtxn_has_catalog_changes(txn)) { txn->txn_flags |= RBTXN_HAS_CATALOG_CHANGES; - dlist_push_tail(&rb->catchange_txns, &txn->catchange_node); - rb->catchange_ntxns++; + dclist_push_tail(&rb->catchange_txns, &txn->catchange_node); } /* @@ -3323,8 +3314,7 @@ ReorderBufferXidSetCatalogChanges(ReorderBuffer *rb, TransactionId xid, if (toptxn != NULL && !rbtxn_has_catalog_changes(toptxn)) { toptxn->txn_flags |= RBTXN_HAS_CATALOG_CHANGES; - dlist_push_tail(&rb->catchange_txns, &toptxn->catchange_node); - rb->catchange_ntxns++; + dclist_push_tail(&rb->catchange_txns, &toptxn->catchange_node); } } @@ -3342,15 +3332,13 @@ ReorderBufferGetCatalogChangesXacts(ReorderBuffer *rb) size_t xcnt = 0; /* Quick return if the list is empty */ - if (rb->catchange_ntxns == 0) - { - Assert(dlist_is_empty(&rb->catchange_txns)); + if (dclist_count(&rb->catchange_txns) == 0) return NULL; - } /* Initialize XID array */ - xids = (TransactionId *) palloc(sizeof(TransactionId) * rb->catchange_ntxns); - dlist_foreach(iter, &rb->catchange_txns) + xids = (TransactionId *) palloc(sizeof(TransactionId) * + dclist_count(&rb->catchange_txns)); + dclist_foreach(iter, &rb->catchange_txns) { ReorderBufferTXN *txn = dlist_container(ReorderBufferTXN, catchange_node, @@ -3363,7 +3351,7 @@ ReorderBufferGetCatalogChangesXacts(ReorderBuffer *rb) qsort(xids, xcnt, sizeof(TransactionId), xidComparator); - Assert(xcnt == rb->catchange_ntxns); + Assert(xcnt == dclist_count(&rb->catchange_txns)); return xids; } @@ -3610,7 +3598,7 @@ ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn) (uint32) txn->nentries_mem, txn->xid); /* do the same to all child TXs */ - dlist_foreach(subtxn_i, &txn->subtxns) + dclist_foreach(subtxn_i, &txn->subtxns) { ReorderBufferTXN *subtxn; @@ -3975,7 +3963,7 @@ ReorderBufferStreamTXN(ReorderBuffer *rb, ReorderBufferTXN *txn) /* at the beginning we should have invalid command ID */ Assert(txn->command_id == InvalidCommandId); - dlist_foreach(subxact_i, &txn->subtxns) + dclist_foreach(subxact_i, &txn->subtxns) { ReorderBufferTXN *subtxn; diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c index f892d19bfb..5006a5c464 100644 --- a/src/backend/replication/logical/snapbuild.c +++ b/src/backend/replication/logical/snapbuild.c @@ -1688,7 +1688,7 @@ SnapBuildSerialize(SnapBuild *builder, XLogRecPtr lsn) /* Get the catalog modifying transactions that are yet not committed */ catchange_xip = ReorderBufferGetCatalogChangesXacts(builder->reorder); - catchange_xcnt = builder->reorder->catchange_ntxns; + catchange_xcnt = dclist_count(&builder->reorder->catchange_txns); needed_length = sizeof(SnapBuildOnDisk) + sizeof(TransactionId) * (builder->committed.xcnt + catchange_xcnt); diff --git a/src/backend/utils/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c index d6f660edf7..d5284cfbde 100644 --- a/src/backend/utils/activity/pgstat_xact.c +++ b/src/backend/utils/activity/pgstat_xact.c @@ -70,13 +70,10 @@ AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit) dlist_mutable_iter iter; int not_freed_count = 0; - if (xact_state->pending_drops_count == 0) - { - Assert(dlist_is_empty(&xact_state->pending_drops)); + if (dclist_count(&xact_state->pending_drops) == 0) return; - } - dlist_foreach_modify(iter, &xact_state->pending_drops) + dclist_foreach_modify(iter, &xact_state->pending_drops) { PgStat_PendingDroppedStatsItem *pending = dlist_container(PgStat_PendingDroppedStatsItem, node, iter.cur); @@ -101,8 +98,7 @@ AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit) not_freed_count++; } - dlist_delete(&pending->node); - xact_state->pending_drops_count--; + dclist_delete_from(&xact_state->pending_drops, &pending->node); pfree(pending); } @@ -144,19 +140,18 @@ AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, dlist_mutable_iter iter; int not_freed_count = 0; - if (xact_state->pending_drops_count == 0) + if (dclist_count(&xact_state->pending_drops) == 0) return; parent_xact_state = pgstat_get_xact_stack_level(nestDepth - 1); - dlist_foreach_modify(iter, &xact_state->pending_drops) + dclist_foreach_modify(iter, &xact_state->pending_drops) { PgStat_PendingDroppedStatsItem *pending = dlist_container(PgStat_PendingDroppedStatsItem, node, iter.cur); xl_xact_stats_item *it = &pending->item; - dlist_delete(&pending->node); - xact_state->pending_drops_count--; + dclist_delete_from(&xact_state->pending_drops, &pending->node); if (!isCommit && pending->is_create) { @@ -175,8 +170,7 @@ AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, * remove the stats object, the surrounding transaction might * still abort. Pass it on to the parent. */ - dlist_push_tail(&parent_xact_state->pending_drops, &pending->node); - parent_xact_state->pending_drops_count++; + dclist_push_tail(&parent_xact_state->pending_drops, &pending->node); } else { @@ -184,7 +178,7 @@ AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, } } - Assert(xact_state->pending_drops_count == 0); + Assert(dclist_count(&xact_state->pending_drops) == 0); if (not_freed_count > 0) pgstat_request_entry_refs_gc(); } @@ -250,8 +244,7 @@ pgstat_get_xact_stack_level(int nest_level) xact_state = (PgStat_SubXactStatus *) MemoryContextAlloc(TopTransactionContext, sizeof(PgStat_SubXactStatus)); - dlist_init(&xact_state->pending_drops); - xact_state->pending_drops_count = 0; + dclist_init(&xact_state->pending_drops); xact_state->nest_level = nest_level; xact_state->prev = pgStatXactStack; xact_state->first = NULL; @@ -291,10 +284,10 @@ pgstat_get_transactional_drops(bool isCommit, xl_xact_stats_item **items) Assert(!isCommit || xact_state->nest_level == 1); Assert(!isCommit || xact_state->prev == NULL); - *items = palloc(xact_state->pending_drops_count + *items = palloc(dclist_count(&xact_state->pending_drops) * sizeof(xl_xact_stats_item)); - dlist_foreach(iter, &xact_state->pending_drops) + dclist_foreach(iter, &xact_state->pending_drops) { PgStat_PendingDroppedStatsItem *pending = dlist_container(PgStat_PendingDroppedStatsItem, node, iter.cur); @@ -304,7 +297,7 @@ pgstat_get_transactional_drops(bool isCommit, xl_xact_stats_item **items) if (!isCommit && !pending->is_create) continue; - Assert(nitems < xact_state->pending_drops_count); + Assert(nitems < dclist_count(&xact_state->pending_drops)); (*items)[nitems++] = pending->item; } @@ -351,8 +344,7 @@ create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, Oid objoid, bool drop->item.dboid = dboid; drop->item.objoid = objoid; - dlist_push_tail(&xact_state->pending_drops, &drop->node); - xact_state->pending_drops_count++; + dclist_push_tail(&xact_state->pending_drops, &drop->node); } /* diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 1d503e7e01..25c09dc102 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -176,8 +176,7 @@ typedef struct RI_CompareHashEntry static HTAB *ri_constraint_cache = NULL; static HTAB *ri_query_cache = NULL; static HTAB *ri_compare_cache = NULL; -static dlist_head ri_constraint_cache_valid_list; -static int ri_constraint_cache_valid_count = 0; +static dclist_head ri_constraint_cache_valid_list; /* @@ -2174,8 +2173,7 @@ ri_LoadConstraintInfo(Oid constraintOid) * For efficient processing of invalidation messages below, we keep a * doubly-linked list, and a count, of all currently valid entries. */ - dlist_push_tail(&ri_constraint_cache_valid_list, &riinfo->valid_link); - ri_constraint_cache_valid_count++; + dclist_push_tail(&ri_constraint_cache_valid_list, &riinfo->valid_link); riinfo->valid = true; @@ -2233,10 +2231,10 @@ InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue) * O(N^2) behavior in situations where a session touches many foreign keys * and also does many ALTER TABLEs, such as a restore from pg_dump. */ - if (ri_constraint_cache_valid_count > 1000) + if (dclist_count(&ri_constraint_cache_valid_list) > 1000) hashvalue = 0; /* pretend it's a cache reset */ - dlist_foreach_modify(iter, &ri_constraint_cache_valid_list) + dclist_foreach_modify(iter, &ri_constraint_cache_valid_list) { RI_ConstraintInfo *riinfo = dlist_container(RI_ConstraintInfo, valid_link, iter.cur); @@ -2252,8 +2250,7 @@ InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue) { riinfo->valid = false; /* Remove invalidated entries from the list, too */ - dlist_delete(iter.cur); - ri_constraint_cache_valid_count--; + dclist_delete_from(&ri_constraint_cache_valid_list, iter.cur); } } } diff --git a/src/include/lib/ilist.h b/src/include/lib/ilist.h index 7ab0888f4f..221f7aac42 100644 --- a/src/include/lib/ilist.h +++ b/src/include/lib/ilist.h @@ -10,20 +10,33 @@ * the link fields in the remainder would be wasted space. But usually, * it saves space to not have separately-allocated list nodes.) * + * The doubly-linked list comes in 2 forms. dlist_head defines a head of a + * doubly-linked list of dlist_nodes. Whereas dclist_head defines the head of + * a doubly-linked list of dlist_nodes with an additional 'count' field to + * keep track of how many items are contained within the given list. For + * simplicity dlist_head and dclist_head share the same node type and + * iterator types. Each function to manipulate a dlist_head is prefixed by + * dlist, whereas functions to manipulate a dclist_head are prefixed with + * dclist. dclist_head comes with an additional function (dclist_count) to + * return the number of entries in the list. dclists are able to store a + * maximum of UINT32 elements. It is up to the caller to ensure no more than + * this many items are added to a dclist. + * * None of the functions here allocate any memory; they just manipulate * externally managed memory. The APIs for singly and doubly linked lists * are identical as far as capabilities of both allow. * * Each list has a list header, which exists even when the list is empty. * An empty singly-linked list has a NULL pointer in its header. - * There are two kinds of empty doubly linked lists: those that have been - * initialized to NULL, and those that have been initialized to circularity. - * (If a dlist is modified and then all its elements are deleted, it will be - * in the circular state.) We prefer circular dlists because there are some - * operations that can be done without branches (and thus faster) on lists - * that use circular representation. However, it is often convenient to - * initialize list headers to zeroes rather than setting them up with an - * explicit initialization function, so we also allow the other case. + * For both dlist_head and dclist_head there are two kinds of empty doubly + * linked lists: those that have been initialized to NULL, and those that have + * been initialized to circularity. (If a dlist is modified and then all its + * elements are deleted, it will be in the circular state.) We prefer circular + * dlists because there are some operations that can be done without branches + * (and thus faster) on lists that use circular representation. However, it + * is often convenient to initialize list headers to zeroes rather than + * setting them up with an explicit initialization function, so we also allow + * the other case. * * EXAMPLES * @@ -182,6 +195,19 @@ typedef struct dlist_mutable_iter dlist_node *end; /* last node we'll iterate to */ } dlist_mutable_iter; +/* + * Head of a doubly linked list with a count of the number of items + * + * This internally makes use of a dlist to implement the actual list and + * wrapper functions are used to maintain the count when items are added and + * removed from the list. + */ +typedef struct dclist_head +{ + dlist_head dlist; /* the actual list header */ + uint32 count; /* the number of items in the list */ +} dclist_head; + /* * Node of a singly linked list. * @@ -246,6 +272,7 @@ typedef struct slist_mutable_iter /* Static initializers */ #define DLIST_STATIC_INIT(name) {{&(name).head, &(name).head}} +#define DCLIST_STATIC_INIT(name) {{&(name).head, &(name).head}, 0} #define SLIST_STATIC_INIT(name) {{NULL}} @@ -270,6 +297,23 @@ extern void slist_check(slist_head *head); /* doubly linked list implementation */ +#ifdef ILIST_DEBUG +/* + * dlist_is_memberof + * Returns true if 'node' is a member of 'head'. + */ +static bool +dlist_is_memberof(dlist_node *node, dlist_head *head) +{ + for (dlist_node *cur = &head->head; cur != NULL; cur = cur->next) + { + if (cur == node) + return true; + } + return false; +} +#endif + /* * Initialize a doubly linked list. * Previous state will be thrown away without any cleanup. @@ -361,6 +405,19 @@ dlist_delete(dlist_node *node) node->next->prev = node->prev; } +/* + * As dlist_delete but performs checks in ILIST_DEBUG to ensure that 'node' + * belongs to 'head'. + */ +static inline void +dlist_delete_from(dlist_head *head, dlist_node *node) +{ +#ifdef ILIST_DEBUG + Assert(dlist_is_memberof(node, head)); +#endif + dlist_delete(node); +} + /* * Remove and return the first node from a list (there must be one). */ @@ -562,6 +619,246 @@ dlist_tail_node(dlist_head *head) (iter).cur != (iter).end; \ (iter).cur = (iter).cur->prev) +/* double linked list with count implementation */ + +/* + * Initialize a doubly linked count list. + * Previous state will be thrown away without any cleanup. + */ +static inline void +dclist_init(dclist_head *head) +{ + dlist_init(&head->dlist); + head->count = 0; +} + +/* + * dclist_is_empty + * Returns true if the list is empty, otherwise false. + */ +static inline bool +dclist_is_empty(dclist_head *head) +{ + Assert(dlist_is_empty(&head->dlist) == (head->count == 0)); + return dlist_is_empty(&head->dlist); +} + +/* + * dclist_push_head + * Insert a node at the beginning of the list. + */ +static inline void +dclist_push_head(dclist_head *head, dlist_node *node) +{ + if (head->dlist.head.next == NULL) /* convert NULL header to circular */ + dclist_init(head); + + dlist_push_head(&head->dlist, node); + head->count++; +} + +/* + * dclist_push_tail + * Insert a node at the end of the list. + */ +static inline void +dclist_push_tail(dclist_head *head, dlist_node *node) +{ + if (head->dlist.head.next == NULL) /* convert NULL header to circular */ + dclist_init(head); + + dlist_push_tail(&head->dlist, node); + head->count++; +} + +/* + * dclist_insert_after + * Insert a node after another *in the same list* + * + * Caller must ensure that 'after' is a member of 'head'. + */ +static inline void +dclist_insert_after(dclist_head *head, dlist_node *after, dlist_node *node) +{ +#ifdef ILIST_DEBUG + Assert(dlist_is_memberof(after, &head->dlist)); +#endif + + dlist_insert_after(after, node); + head->count++; +} + +/* + * dclist_insert_before + * Insert a node before another *in the same list* + * + * Caller must ensure that 'before' is a member of 'head'. + */ +static inline void +dclist_insert_before(dclist_head *head, dlist_node *before, dlist_node *node) +{ +#ifdef ILIST_DEBUG + Assert(dlist_is_memberof(before, &head->dlist)); +#endif + + dlist_insert_before(before, node); + head->count++; +} + +/* + * dclist_delete_from + * Deletes 'node' from 'head'. + * + * Caution: 'node' must be a member of 'head'. + */ +static inline void +dclist_delete_from(dclist_head *head, dlist_node *node) +{ + dlist_delete_from(&head->dlist, node); + head->count--; +} + +/* + * dclist_pop_head_node + * Remove and return the first node from a list (there must be one). + */ +static inline dlist_node * +dclist_pop_head_node(dclist_head *head) +{ + dlist_node *node; + + Assert(head->count > 0); + + node = dlist_pop_head_node(&head->dlist); + head->count--; + + return node; +} + +/* + * dclist_move_head + * Move 'node' from its current position in the list to the head position + * in 'head'. + * + * Caution: 'node' must be a member of 'head'. + */ +static inline void +dclist_move_head(dclist_head *head, dlist_node *node) +{ +#ifdef ILIST_DEBUG + Assert(dlist_is_memberof(node, &head->dlist)); +#endif + + dlist_move_head(&head->dlist, node); +} + +/* + * dclist_move_tail + * Move 'node' from its current position in the list to the tail position + * in 'head'. + * + * Caution: 'node' must be a member of 'head'. + */ +static inline void +dclist_move_tail(dclist_head *head, dlist_node *node) +{ +#ifdef ILIST_DEBUG + Assert(dlist_is_memberof(node, &head->dlist)); +#endif + + dlist_move_tail(&head->dlist, node); +} + +/* + * dclist_has_next + * Check whether 'node' has a following node. + * + * Caution: 'node' must be a member of 'head'. + */ +static inline bool +dclist_has_next(dclist_head *head, dlist_node *node) +{ +#ifdef ILIST_DEBUG + Assert(dlist_is_memberof(node, &head->dlist)); +#endif + + return dlist_has_next(&head->dlist, node); +} + +/* + * dclist_has_prev + * Check whether 'node' has a preceding node. + * + * Caution: 'node' must be a member of 'head'. + */ +static inline bool +dclist_has_prev(dclist_head *head, dlist_node *node) +{ +#ifdef ILIST_DEBUG + Assert(dlist_is_memberof(node, &head->dlist)); +#endif + + return dlist_has_prev(&head->dlist, node); +} + +/* + * dclist_next_node + * Return the next node in the list (there must be one). + */ +static inline dlist_node * +dclist_next_node(dclist_head *head, dlist_node *node) +{ + return dlist_next_node(&head->dlist, node); +} + +/* + * dclist_prev_node + * Return the prev node in the list (there must be one). + */ +static inline dlist_node * +dclist_prev_node(dclist_head *head, dlist_node *node) +{ + return dlist_prev_node(&head->dlist, node); +} + +/* + * dclist_head_node + * Return the first node in the list (there must be one). + */ +static inline dlist_node * +dclist_head_node(dclist_head *head) +{ + return (dlist_node *) dlist_head_element_off(&head->dlist, 0); +} + +/* + * Return the last node in the list (there must be one). + */ +static inline dlist_node * +dclist_tail_node(dclist_head *head) +{ + return (dlist_node *) dlist_tail_element_off(&head->dlist, 0); +} + +/* + * dclist_count + * Returns the stored number of entries in 'head' + */ +static inline uint32 +dclist_count(dclist_head *head) +{ + return head->count; +} + +/* Iterators for dclists */ +#define dclist_foreach(iter, lhead) \ + dlist_foreach(iter, &((lhead)->dlist)) + +#define dclist_foreach_modify(iter, lhead) \ + dlist_foreach_modify(iter, &((lhead)->dlist)) + +#define dclist_reverse_foreach(iter, lhead) \ + dlist_reverse_foreach(iter, &((lhead)->dlist)) /* singly linked list implementation */ diff --git a/src/include/replication/reorderbuffer.h b/src/include/replication/reorderbuffer.h index 02b59a1931..7301555b89 100644 --- a/src/include/replication/reorderbuffer.h +++ b/src/include/replication/reorderbuffer.h @@ -361,8 +361,7 @@ typedef struct ReorderBufferTXN * non-hierarchical list of subtransactions that are *not* aborted. Only * used in toplevel transactions. */ - dlist_head subtxns; - uint32 nsubtxns; + dclist_head subtxns; /* * Stored cache invalidations. This is not a linked list because we get @@ -534,8 +533,7 @@ struct ReorderBuffer /* * Transactions and subtransactions that have modified system catalogs. */ - dlist_head catchange_txns; - int catchange_ntxns; + dclist_head catchange_txns; /* * one-entry sized cache for by_txn. Very frequently the same txn gets diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h index 627c1389e4..9f32a6f6d3 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -162,8 +162,7 @@ typedef struct PgStat_SubXactStatus * if the transaction commits/aborts. To handle replicas and crashes, * stats drops are included in commit / abort records. */ - dlist_head pending_drops; - int pending_drops_count; + dclist_head pending_drops; /* * Tuple insertion/deletion counts for an open transaction can't be diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 2f02cc8f42..c15f990cbb 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -3194,6 +3194,7 @@ datapagemap_t dateKEY datetkn dce_uuid_t +dclist_head decimal deparse_columns deparse_context -- 2.34.1