From: Nathan Bossart Date: Thu, 9 Jul 2026 15:21:13 -0500 Subject: [PATCH v1 7/8] convert ParallelBlockTableScanDescData->phs_{start,num}block to atomics --- src/backend/access/heap/heapam_handler.c | 2 +- src/backend/access/table/tableam.c | 59 +++++++++++------------- src/include/access/relscan.h | 8 ++-- 3 files changed, 31 insertions(+), 38 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index bf87430cf01..0f24a132564 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -1965,7 +1965,7 @@ heapam_scan_get_blocks_done(HeapScanDesc hscan) if (hscan->rs_base.rs_parallel != NULL) { bpscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel; - startblock = bpscan->phs_startblock; + startblock = pg_atomic_read_u32(&bpscan->phs_startblock); } else startblock = hscan->rs_startblock; diff --git a/src/backend/access/table/tableam.c b/src/backend/access/table/tableam.c index 68ff0966f1c..f2038ea9205 100644 --- a/src/backend/access/table/tableam.c +++ b/src/backend/access/table/tableam.c @@ -421,9 +421,8 @@ table_block_parallelscan_initialize(Relation rel, ParallelTableScanDesc pscan) bpscan->base.phs_syncscan = synchronize_seqscans && !RelationUsesLocalBuffers(rel) && bpscan->phs_nblocks > NBuffers / 4; - SpinLockInit(&bpscan->phs_mutex); - bpscan->phs_startblock = InvalidBlockNumber; - bpscan->phs_numblock = InvalidBlockNumber; + pg_atomic_init_u32(&bpscan->phs_startblock, InvalidBlockNumber); + pg_atomic_init_u32(&bpscan->phs_numblock, InvalidBlockNumber); pg_atomic_init_u64(&bpscan->phs_nallocated, 0); return sizeof(ParallelBlockTableScanDescData); @@ -459,25 +458,22 @@ table_block_parallelscan_startblock_init(Relation rel, StaticAssertDecl(MaxBlockNumber <= 0xFFFFFFFE, "pg_nextpower2_32 may be too small for non-standard BlockNumber width"); - BlockNumber sync_startpage = InvalidBlockNumber; BlockNumber scan_nblocks; /* Reset the state we use for controlling allocation size. */ memset(pbscanwork, 0, sizeof(*pbscanwork)); -retry: - /* Grab the spinlock. */ - SpinLockAcquire(&pbscan->phs_mutex); - /* * When the caller specified a limit on the number of blocks to scan, set * that in the ParallelBlockTableScanDesc, if it's not been done by * another worker already. */ - if (numblocks != InvalidBlockNumber && - pbscan->phs_numblock == InvalidBlockNumber) + if (numblocks != InvalidBlockNumber) { - pbscan->phs_numblock = numblocks; + uint32 expected = InvalidBlockNumber; + + pg_atomic_compare_exchange_u32(&pbscan->phs_numblock, &expected, + numblocks); } /* @@ -485,36 +481,35 @@ retry: * so now. If a startblock was specified, start there, otherwise if this * is not a synchronized scan, we just start at block 0, but if it is a * synchronized scan, we must get the starting position from the - * synchronized scan machinery. We can't hold the spinlock while doing - * that, though, so release the spinlock, get the information we need, and - * retry. If nobody else has initialized the scan in the meantime, we'll - * fill in the value we fetched on the second time through. + * synchronized scan machinery. + * + * If another worker initializes phs_startblock concurrently, just use + * their value. */ - if (pbscan->phs_startblock == InvalidBlockNumber) + if (pg_atomic_read_u32(&pbscan->phs_startblock) == InvalidBlockNumber) { + BlockNumber newstartblock; + uint32 expected = InvalidBlockNumber; + if (startblock != InvalidBlockNumber) - pbscan->phs_startblock = startblock; + newstartblock = startblock; else if (!pbscan->base.phs_syncscan) - pbscan->phs_startblock = 0; - else if (sync_startpage != InvalidBlockNumber) - pbscan->phs_startblock = sync_startpage; + newstartblock = 0; else - { - SpinLockRelease(&pbscan->phs_mutex); - sync_startpage = ss_get_location(rel, pbscan->phs_nblocks); - goto retry; - } + newstartblock = ss_get_location(rel, pbscan->phs_nblocks); + + pg_atomic_compare_exchange_u32(&pbscan->phs_startblock, &expected, + newstartblock); } - SpinLockRelease(&pbscan->phs_mutex); /* * Figure out how many blocks we're going to scan; either all of them, or * just phs_numblock's worth, if a limit has been imposed. */ - if (pbscan->phs_numblock == InvalidBlockNumber) + if (pg_atomic_read_u32(&pbscan->phs_numblock) == InvalidBlockNumber) scan_nblocks = pbscan->phs_nblocks; else - scan_nblocks = pbscan->phs_numblock; + scan_nblocks = pg_atomic_read_u32(&pbscan->phs_numblock); /* * We determine the chunk size based on scan_nblocks. First we split @@ -595,10 +590,10 @@ table_block_parallelscan_nextpage(Relation rel, */ /* First, figure out how many blocks we're planning on scanning */ - if (pbscan->phs_numblock == InvalidBlockNumber) + if (pg_atomic_read_u32(&pbscan->phs_numblock) == InvalidBlockNumber) scan_nblocks = pbscan->phs_nblocks; else - scan_nblocks = pbscan->phs_numblock; + scan_nblocks = pg_atomic_read_u32(&pbscan->phs_numblock); /* * Now check if we have any remaining blocks in a previous chunk for this @@ -644,7 +639,7 @@ table_block_parallelscan_nextpage(Relation rel, if (nallocated >= scan_nblocks) page = InvalidBlockNumber; /* all blocks have been allocated */ else - page = (nallocated + pbscan->phs_startblock) % pbscan->phs_nblocks; + page = (nallocated + pg_atomic_read_u32(&pbscan->phs_startblock)) % pbscan->phs_nblocks; /* * Report scan location. Normally, we report the current page number. @@ -658,7 +653,7 @@ table_block_parallelscan_nextpage(Relation rel, if (page != InvalidBlockNumber) ss_report_location(rel, page); else if (nallocated == pbscan->phs_nblocks) - ss_report_location(rel, pbscan->phs_startblock); + ss_report_location(rel, pg_atomic_read_u32(&pbscan->phs_startblock)); } return page; diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h index 2ea06a67a63..2305d0159f3 100644 --- a/src/include/access/relscan.h +++ b/src/include/access/relscan.h @@ -19,7 +19,6 @@ #include "nodes/tidbitmap.h" #include "port/atomics.h" #include "storage/relfilelocator.h" -#include "storage/spin.h" #include "utils/relcache.h" @@ -99,10 +98,9 @@ typedef struct ParallelBlockTableScanDescData ParallelTableScanDescData base; BlockNumber phs_nblocks; /* # blocks in relation at start of scan */ - slock_t phs_mutex; /* mutual exclusion for setting startblock */ - BlockNumber phs_startblock; /* starting block number */ - BlockNumber phs_numblock; /* # blocks to scan, or InvalidBlockNumber if - * no limit */ + pg_atomic_uint32 phs_startblock; /* starting block number */ + pg_atomic_uint32 phs_numblock; /* # blocks to scan, or InvalidBlockNumber + * if no limit */ pg_atomic_uint64 phs_nallocated; /* number of blocks allocated to * workers so far. */ } ParallelBlockTableScanDescData; -- 2.50.1 (Apple Git-155) --Ot5x3ffkWEuxilWO Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v1-0008-convert-FastPathStrongRelationLocks-to-atomics.patch