On 8/12/26 23:19, Matthias van de Meent wrote:
On Fri, 7 Aug 2026 at 16:05, Maksim.Melnikov<[email protected]> wrote:
On 7/17/26 11:54, Matthias van de Meent wrote:
On Thu, 11 Jun 2026 at 16:31, Maksim.Melnikov<[email protected]> wrote:
Patch with fixes was attached. Thanks for review.
You removed the pgxactoff field with its comment from PGPROC, but
haven't yet added a comment on PROC_HDR's pgxactoffs. Please add a
comment to the pgxactoffs array that describes its contents. A simple
adaptation of the comment removed with PGPROC's pgxactoff field should
be sufficient.

The comments on PROC_HDR and PGPROC also reference the deleted
PGPROC->pgxactoff field, so that also must be adjusted; there may be
several other places, as I didn't do a full check on the codebase.

Once the relevant comments are added and adjusted I think this is
ready for a committer.


Kind regards,

Matthias van de Meent
Databricks (https://www.databricks.com)


Sorry for the delay, was unavailable for several weeks.

Thanks for review, patch was updated.
I noticed this patch wasn't registered at the commitfest yet, so I
took the liberty to do that for you. It's registered at [0].
Thanks!
The CFBot then noticed crashes caused by the patch in various tests,
which I think can be caused by a lack of initialization of pgxactoffs
in ProcArrayShmemAttach, but I haven't worked on a fix.
Yes, thanks, there was the problem with alignment of pgxactoff array on shmem, I've fixed it with re-ordering of elements. Also I've rebased master and noticed new commit c6d55714ba4, that introduced new shmem alloc functions, so I've added minor fix.
I've attached incremental patch for ease of review of last CI fixes.
Attached are some copy-edits of comments on top of your v3, and a
missed replacement of "GetNumberFromPGProc(MyProc)" with
"MyProcNumber".

This is an incremental patch on your v3, and doesn't fix the CFBot failures.

Thanks. I've attached overall patch with your edits for commitfest too.

Kind regards

Melnikov Maksim
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index e6075dafe9..7fef120726 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -462,6 +462,7 @@ static void
 ProcArrayShmemAttach(void *arg)
 {
 	allProcs = ProcGlobal->allProcs;
+	pgxactoffs = ProcGlobal->pgxactoffs;
 }
 
 /*
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index e64ab6e597..be0ca07161 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -269,12 +269,12 @@ ProcGlobalShmemInit(void *arg)
 	ProcGlobal->xids = (TransactionId *) ptr;
 	ptr = ptr + (TotalProcs * sizeof(*ProcGlobal->xids));
 
+	ProcGlobal->pgxactoffs = (int *) ptr;
+	ptr = ptr + (TotalProcs * sizeof(*ProcGlobal->pgxactoffs));
+
 	ProcGlobal->subxidStates = (XidCacheStatus *) ptr;
 	ptr = ptr + (TotalProcs * sizeof(*ProcGlobal->subxidStates));
 
-	ProcGlobal->pgxactoffs = (int *) ptr;
-	ptr = (char *) ptr + TotalProcs * sizeof(*ProcGlobal->pgxactoffs);
-
 	ProcGlobal->statusFlags = (uint8 *) ptr;
 	ptr = ptr + (TotalProcs * sizeof(*ProcGlobal->statusFlags));
 
From a62922a732b261ccfe5c7623aa4fe0e9b18c4aeb Mon Sep 17 00:00:00 2001
From: Maksim Melnikov <[email protected]>
Date: Tue, 18 Nov 2025 17:20:09 +0300
Subject: [PATCH v5] This patch reduce connection init/close time.

ProcArrayRemove/ProcArrayAdd are expensive in terms of accessing pgxactoff
field in PGPROC, because its are placed on different pages and it the
reason of page_faults occurence. Now one of the use case with pgxactoff
is iteration with gaps over PGPROCs and read/write pgxactoff, PGPROC
allocate ~1KB and it quite enough to have page_faults in case of such
accessing.

So we placed all pgxactoff in the separate array pgxactoffs to have adjacent
pages for them all. Indexes in allProcs aligned with pgxactoffs array, so
the Nth element in pgxactoffs refer to Nth PGPROC.

Eventually it helps to avoid extra page_faults and reduce connection
init/close time. The main benefit is seen on configurations without huge
pages.
---
 src/backend/access/transam/varsup.c       | 10 ++---
 src/backend/commands/indexcmds.c          |  2 +-
 src/backend/commands/vacuum.c             |  2 +-
 src/backend/replication/logical/logical.c |  2 +-
 src/backend/replication/slot.c            |  2 +-
 src/backend/replication/walsender.c       |  2 +-
 src/backend/storage/ipc/procarray.c       | 55 ++++++++++++++---------
 src/backend/storage/lmgr/proc.c           |  6 ++-
 src/include/storage/proc.h                | 23 ++++++----
 9 files changed, 63 insertions(+), 41 deletions(-)

diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index cb68d8fa974..d67c00b35c8 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -85,7 +85,7 @@ GetNewTransactionId(bool isSubXact)
 	{
 		Assert(!isSubXact);
 		MyProc->xid = BootstrapTransactionId;
-		ProcGlobal->xids[MyProc->pgxactoff] = BootstrapTransactionId;
+		ProcGlobal->xids[ProcGetMyXactOff()] = BootstrapTransactionId;
 		return FullTransactionIdFromEpochAndXid(0, BootstrapTransactionId);
 	}
 
@@ -244,18 +244,18 @@ GetNewTransactionId(bool isSubXact)
 	 */
 	if (!isSubXact)
 	{
-		Assert(ProcGlobal->subxidStates[MyProc->pgxactoff].count == 0);
-		Assert(!ProcGlobal->subxidStates[MyProc->pgxactoff].overflowed);
+		Assert(ProcGlobal->subxidStates[ProcGetMyXactOff()].count == 0);
+		Assert(!ProcGlobal->subxidStates[ProcGetMyXactOff()].overflowed);
 		Assert(MyProc->subxidStatus.count == 0);
 		Assert(!MyProc->subxidStatus.overflowed);
 
 		/* LWLockRelease acts as barrier */
 		MyProc->xid = xid;
-		ProcGlobal->xids[MyProc->pgxactoff] = xid;
+		ProcGlobal->xids[ProcGetMyXactOff()] = xid;
 	}
 	else
 	{
-		XidCacheStatus *substat = &ProcGlobal->subxidStates[MyProc->pgxactoff];
+		XidCacheStatus *substat = &ProcGlobal->subxidStates[ProcGetMyXactOff()];
 		int			nxids = MyProc->subxidStatus.count;
 
 		Assert(substat->count == MyProc->subxidStatus.count);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 5a0312fe772..4c66e7bb917 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -4811,6 +4811,6 @@ set_indexsafe_procflags(void)
 
 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 	MyProc->statusFlags |= PROC_IN_SAFE_IC;
-	ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
+	ProcGlobal->statusFlags[ProcGetMyXactOff()] = MyProc->statusFlags;
 	LWLockRelease(ProcArrayLock);
 }
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 72c7f680a84..a2b853e85a4 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -2081,7 +2081,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params,
 		MyProc->statusFlags |= PROC_IN_VACUUM;
 		if (params.is_wraparound)
 			MyProc->statusFlags |= PROC_VACUUM_FOR_WRAPAROUND;
-		ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
+		ProcGlobal->statusFlags[ProcGetMyXactOff()] = MyProc->statusFlags;
 		LWLockRelease(ProcArrayLock);
 	}
 
diff --git a/src/backend/replication/logical/logical.c b/src/backend/replication/logical/logical.c
index 98e5f1dd8f9..53c521f129f 100644
--- a/src/backend/replication/logical/logical.c
+++ b/src/backend/replication/logical/logical.c
@@ -274,7 +274,7 @@ StartupDecodingContext(List *output_plugin_options,
 	{
 		LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 		MyProc->statusFlags |= PROC_IN_LOGICAL_DECODING;
-		ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
+		ProcGlobal->statusFlags[ProcGetMyXactOff()] = MyProc->statusFlags;
 		LWLockRelease(ProcArrayLock);
 	}
 
diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index 63ce6d27885..9c2a2cb9888 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -834,7 +834,7 @@ ReplicationSlotRelease(void)
 	/* might not have been set when we've been a plain slot */
 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 	MyProc->statusFlags &= ~PROC_IN_LOGICAL_DECODING;
-	ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
+	ProcGlobal->statusFlags[ProcGetMyXactOff()] = MyProc->statusFlags;
 	LWLockRelease(ProcArrayLock);
 
 	if (am_walsender)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c65dd324325..98217943dfa 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -358,7 +358,7 @@ InitWalSender(void)
 		Assert(MyProc->xmin == InvalidTransactionId);
 		LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 		MyProc->statusFlags |= PROC_AFFECTS_ALL_HORIZONS;
-		ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
+		ProcGlobal->statusFlags[ProcGetMyXactOff()] = MyProc->statusFlags;
 		LWLockRelease(ProcArrayLock);
 	}
 
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index b7e03134ed8..090338fb63b 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -284,6 +284,13 @@ typedef enum KAXCompressReason
 
 static PGPROC *allProcs;
 
+/*
+ * offsets into various ProcGlobal->arrays with data mirrored from
+ * appropriate PGPROCs, procno define offset index in pgxactoffs array(See
+ * PROC_HDR for details).
+ */
+static int *pgxactoffs;
+
 /*
  * Cache to reduce overhead of repeated calls to TransactionIdIsInProgress()
  */
@@ -449,12 +456,14 @@ ProcArrayShmemInit(void *arg)
 	TransamVariables->xactCompletionCount = 1;
 
 	allProcs = ProcGlobal->allProcs;
+	pgxactoffs = ProcGlobal->pgxactoffs;
 }
 
 static void
 ProcArrayShmemAttach(void *arg)
 {
 	allProcs = ProcGlobal->allProcs;
+	pgxactoffs = ProcGlobal->pgxactoffs;
 }
 
 /*
@@ -498,7 +507,7 @@ ProcArrayAdd(PGPROC *proc)
 		int			this_procno = arrayP->pgprocnos[index];
 
 		Assert(this_procno >= 0 && this_procno < (arrayP->maxProcs + NUM_AUXILIARY_PROCS));
-		Assert(allProcs[this_procno].pgxactoff == index);
+		Assert(pgxactoffs[this_procno] == index);
 
 		/* If we have found our right position in the array, break */
 		if (this_procno > pgprocno)
@@ -520,23 +529,23 @@ ProcArrayAdd(PGPROC *proc)
 			movecount * sizeof(*ProcGlobal->statusFlags));
 
 	arrayP->pgprocnos[index] = pgprocno;
-	proc->pgxactoff = index;
+	pgxactoffs[arrayP->pgprocnos[index]] = index;
 	ProcGlobal->xids[index] = proc->xid;
 	ProcGlobal->subxidStates[index] = proc->subxidStatus;
 	ProcGlobal->statusFlags[index] = proc->statusFlags;
 
 	arrayP->numProcs++;
 
-	/* adjust pgxactoff for all following PGPROCs */
+	/* adjust appropriate pgxactoff for all following PGPROCs */
 	index++;
 	for (; index < arrayP->numProcs; index++)
 	{
 		int			procno = arrayP->pgprocnos[index];
 
 		Assert(procno >= 0 && procno < (arrayP->maxProcs + NUM_AUXILIARY_PROCS));
-		Assert(allProcs[procno].pgxactoff == index - 1);
+		Assert(pgxactoffs[procno] == index - 1);
 
-		allProcs[procno].pgxactoff = index;
+		pgxactoffs[procno] = index;
 	}
 
 	/*
@@ -574,10 +583,10 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 	LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
 
-	myoff = proc->pgxactoff;
+	myoff = ProcGetXactOff(GetNumberFromPGProc(proc));
 
 	Assert(myoff >= 0 && myoff < arrayP->numProcs);
-	Assert(ProcGlobal->allProcs[arrayP->pgprocnos[myoff]].pgxactoff == myoff);
+	Assert(ProcGlobal->pgxactoffs[arrayP->pgprocnos[myoff]] == myoff);
 
 	if (TransactionIdIsValid(latestXid))
 	{
@@ -624,17 +633,17 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
 	arrayP->numProcs--;
 
 	/*
-	 * Adjust pgxactoff of following procs for removed PGPROC (note that
-	 * numProcs already has been decremented).
+	 * Adjust appropriate pgxactoff of following procs for removed PGPROC
+	 * (note that numProcs already has been decremented).
 	 */
 	for (int index = myoff; index < arrayP->numProcs; index++)
 	{
 		int			procno = arrayP->pgprocnos[index];
 
 		Assert(procno >= 0 && procno < (arrayP->maxProcs + NUM_AUXILIARY_PROCS));
-		Assert(allProcs[procno].pgxactoff - 1 == index);
+		Assert(pgxactoffs[procno] - 1 == index);
 
-		allProcs[procno].pgxactoff = index;
+		pgxactoffs[procno] = index;
 	}
 
 	/*
@@ -706,11 +715,13 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 		/* avoid unnecessarily dirtying shared cachelines */
 		if (proc->statusFlags & PROC_VACUUM_STATE_MASK)
 		{
+			int			pgxactoff = ProcGetXactOff(GetNumberFromPGProc(proc));
+
 			Assert(!LWLockHeldByMe(ProcArrayLock));
 			LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
-			Assert(proc->statusFlags == ProcGlobal->statusFlags[proc->pgxactoff]);
+			Assert(proc->statusFlags == ProcGlobal->statusFlags[pgxactoff]);
 			proc->statusFlags &= ~PROC_VACUUM_STATE_MASK;
-			ProcGlobal->statusFlags[proc->pgxactoff] = proc->statusFlags;
+			ProcGlobal->statusFlags[pgxactoff] = proc->statusFlags;
 			LWLockRelease(ProcArrayLock);
 		}
 	}
@@ -724,7 +735,7 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 static inline void
 ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId latestXid)
 {
-	int			pgxactoff = proc->pgxactoff;
+	int			pgxactoff = ProcGetXactOff(GetNumberFromPGProc(proc));
 
 	/*
 	 * Note: we need exclusive lock here because we're going to change other
@@ -747,7 +758,7 @@ ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId latestXid)
 	if (proc->statusFlags & PROC_VACUUM_STATE_MASK)
 	{
 		proc->statusFlags &= ~PROC_VACUUM_STATE_MASK;
-		ProcGlobal->statusFlags[proc->pgxactoff] = proc->statusFlags;
+		ProcGlobal->statusFlags[pgxactoff] = proc->statusFlags;
 	}
 
 	/* Clear the subtransaction-XID cache too while holding the lock */
@@ -903,7 +914,7 @@ ProcArrayClearTransaction(PGPROC *proc)
 	/*
 	 * Currently we need to lock ProcArrayLock exclusively here, as we
 	 * increment xactCompletionCount below. We also need it at least in shared
-	 * mode for pgproc->pgxactoff to stay the same below.
+	 * mode for proc's pgxactoff to stay the same below.
 	 *
 	 * We could however, as this action does not actually change anyone's view
 	 * of the set of running XIDs (our entry is duplicate with the gxact that
@@ -916,7 +927,7 @@ ProcArrayClearTransaction(PGPROC *proc)
 	 */
 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 
-	pgxactoff = proc->pgxactoff;
+	pgxactoff = ProcGetXactOff(GetNumberFromPGProc(proc));
 
 	ProcGlobal->xids[pgxactoff] = InvalidTransactionId;
 	proc->xid = InvalidTransactionId;
@@ -1475,7 +1486,7 @@ TransactionIdIsInProgress(TransactionId xid)
 	}
 
 	/* No shortcuts, gotta grovel through the array */
-	mypgxactoff = MyProc->pgxactoff;
+	mypgxactoff = ProcGetMyXactOff();
 	numProcs = arrayP->numProcs;
 	for (int pgxactoff = 0; pgxactoff < numProcs; pgxactoff++)
 	{
@@ -2184,7 +2195,7 @@ GetSnapshotData(Snapshot snapshot)
 	}
 
 	latest_completed = TransamVariables->latestCompletedXid;
-	mypgxactoff = MyProc->pgxactoff;
+	mypgxactoff = ProcGetMyXactOff();
 	myxid = other_xids[mypgxactoff];
 	Assert(myxid == MyProc->xid);
 
@@ -2223,7 +2234,7 @@ GetSnapshotData(Snapshot snapshot)
 			TransactionId xid = UINT32_ACCESS_ONCE(other_xids[pgxactoff]);
 			uint8		statusFlags;
 
-			Assert(allProcs[arrayP->pgprocnos[pgxactoff]].pgxactoff == pgxactoff);
+			Assert(pgxactoffs[arrayP->pgprocnos[pgxactoff]] == pgxactoff);
 
 			/*
 			 * If the transaction has no XID assigned, we can skip it; it
@@ -2596,7 +2607,7 @@ ProcArrayInstallRestoredXmin(TransactionId xmin, PGPROC *proc)
 		MyProc->xmin = TransactionXmin = xmin;
 		MyProc->statusFlags = (MyProc->statusFlags & ~PROC_XMIN_FLAGS) |
 			(proc->statusFlags & PROC_XMIN_FLAGS);
-		ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
+		ProcGlobal->statusFlags[ProcGetMyXactOff()] = MyProc->statusFlags;
 
 		result = true;
 	}
@@ -4024,7 +4035,7 @@ XidCacheRemoveRunningXids(TransactionId xid,
 	 */
 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 
-	mysubxidstat = &ProcGlobal->subxidStates[MyProc->pgxactoff];
+	mysubxidstat = &ProcGlobal->subxidStates[ProcGetMyXactOff()];
 
 	/*
 	 * Under normal circumstances xid and xids[] will be in increasing order,
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 624cecc6bc5..053538ccebd 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -163,6 +163,7 @@ ProcGlobalShmemRequest(void *arg)
 	size = add_size(size, mul_size(TotalProcs, sizeof(PGPROC)));
 	size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->xids)));
 	size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->subxidStates)));
+	size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->pgxactoffs)));
 	size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->statusFlags)));
 	ProcGlobalAllProcsShmemSize = size;
 	ShmemRequestStruct(.name = "PGPROC structures",
@@ -267,6 +268,9 @@ ProcGlobalShmemInit(void *arg)
 	ProcGlobal->xids = (TransactionId *) ptr;
 	ptr = ptr + (TotalProcs * sizeof(*ProcGlobal->xids));
 
+	ProcGlobal->pgxactoffs = (int *) ptr;
+	ptr = ptr + (TotalProcs * sizeof(*ProcGlobal->pgxactoffs));
+
 	ProcGlobal->subxidStates = (XidCacheStatus *) ptr;
 	ptr = ptr + (TotalProcs * sizeof(*ProcGlobal->subxidStates));
 
@@ -1555,7 +1559,7 @@ ProcSleep(LOCALLOCK *locallock)
 			 * the lock held, which is much more undesirable.
 			 */
 			LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
-			statusFlags = ProcGlobal->statusFlags[autovac->pgxactoff];
+			statusFlags = ProcGlobal->statusFlags[ProcGetXactOff(GetNumberFromPGProc(autovac))];
 			lockmethod_copy = lock->tag.locktag_lockmethodid;
 			locktag_copy = lock->tag;
 			LWLockRelease(ProcArrayLock);
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 03a1a466fa8..8cbc8928dd5 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -174,7 +174,8 @@ typedef enum
  *
  * Some fields in PGPROC (see "mirrored in ..." comment) are mirrored into an
  * element of more densely packed ProcGlobal arrays. These arrays are indexed
- * by PGPROC->pgxactoff. Both copies need to be maintained coherently.
+ * by a PGPROC's pgxactoffs entry. Both copies need to be maintained
+ * coherently.
  *
  * NB: The pgxactoff indexed value can *never* be accessed without holding
  * locks.
@@ -210,9 +211,6 @@ typedef struct PGPROC
 	Oid			tempNamespaceId;	/* OID of temp schema this backend is
 									 * using */
 
-	int			pgxactoff;		/* offset into various ProcGlobal->arrays with
-								 * data mirrored from this PGPROC */
-
 	uint8		statusFlags;	/* this backend's status flags, see PROC_*
 								 * above. mirrored in
 								 * ProcGlobal->statusFlags[pgxactoff] */
@@ -402,10 +400,11 @@ extern PGDLLIMPORT PGPROC *MyProc;
  * for PGPROCs that have been added to the shared array with ProcArrayAdd()
  * (in contrast to PGPROC array which has unused PGPROCs interspersed).
  *
- * The dense arrays are indexed by PGPROC->pgxactoff. Any concurrent
- * ProcArrayAdd() / ProcArrayRemove() can lead to pgxactoff of a procarray
- * member to change.  Therefore it is only safe to use PGPROC->pgxactoff to
- * access the dense array while holding either ProcArrayLock or XidGenLock.
+ * The dense arrays are indexed by the PGPROC's corresponding offset in
+ * pgxactoff.  Any concurrent ProcArrayAdd() / ProcArrayRemove() can cause
+ * the pgxactoff of a procarray member to change.  Therefore it is only safe
+ * to use a proc's pgxactoff to access the dense array while holding either
+ * ProcArrayLock or XidGenLock.
  *
  * As long as a PGPROC is in the procarray, the mirrored values need to be
  * maintained in both places in a coherent manner.
@@ -446,6 +445,12 @@ typedef struct PROC_HDR
 	/* Array of PGPROC structures (not including dummies for prepared txns) */
 	PGPROC	   *allProcs;
 
+	/*
+	 * Dense offsets into various ProcGlobal->arrays with data mirrored from
+	 * appropriate PGPROCs.  Like allProcs, values are indexed by ProcNumber.
+	 */
+	int		   *pgxactoffs;
+
 	/* Array mirroring PGPROC.xid for each PGPROC currently in the procarray */
 	TransactionId *xids;
 
@@ -511,6 +516,8 @@ extern PGDLLIMPORT PGPROC *PreparedXactProcs;
  */
 #define GetPGProcByNumber(n) (&ProcGlobal->allProcs[(n)])
 #define GetNumberFromPGProc(proc) ((proc) - &ProcGlobal->allProcs[0])
+#define ProcGetXactOff(procno) (ProcGlobal->pgxactoffs[(procno)])
+#define ProcGetMyXactOff() (ProcGetXactOff(MyProcNumber))
 
 /*
  * We set aside some extra PGPROC structures for "special worker" processes,
-- 
2.43.0

Reply via email to