For some reason, the ProcStructLock spinlock is allocated in a shared
memory area of its own:
/* Create ProcStructLock spinlock, too */
ProcStructLock = (slock_t *) ShmemInitStruct("ProcStructLock spinlock",
sizeof(slock_t),
&found);
SpinLockInit(ProcStructLock);
I believe that's just for historical reasons. A long long time ago,
spinlocks had to be allocated separately rather than embedded in other
structs.
The spinlock protects the freeProcs list and some other fields in
ProcGlobal, so let's put it together with those fields. It's good for
cache locality to have it next to the thing it protects, and just makes
more sense anyway.
Any objections?
- Heikki
From 2744369be58fe226a737df3035c1c6601194b395 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Tue, 10 Feb 2026 19:33:36 +0200
Subject: [PATCH 1/1] Move ProcStructLock to the ProcGlobal struct
It protects the freeProcs and some other fields in ProcGlobal, so
let's put it together with the fields it protects. It's good for cache
locality to have it next to the thing it protects, and just makes more
sense anyway. I believe it was allocated as a separate shared memory
area just for historical reasons.
---
src/backend/postmaster/launch_backend.c | 3 --
src/backend/storage/lmgr/proc.c | 49 +++++++++----------------
src/include/storage/proc.h | 12 +++++-
3 files changed, 28 insertions(+), 36 deletions(-)
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 05b1feef3cf..e9134b9751b 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -104,7 +104,6 @@ typedef struct
char **LWLockTrancheNames;
int *LWLockCounter;
LWLockPadded *MainLWLockArray;
- slock_t *ProcStructLock;
PROC_HDR *ProcGlobal;
PGPROC *AuxiliaryProcs;
PGPROC *PreparedXactProcs;
@@ -735,7 +734,6 @@ save_backend_variables(BackendParameters *param,
param->LWLockTrancheNames = LWLockTrancheNames;
param->LWLockCounter = LWLockCounter;
param->MainLWLockArray = MainLWLockArray;
- param->ProcStructLock = ProcStructLock;
param->ProcGlobal = ProcGlobal;
param->AuxiliaryProcs = AuxiliaryProcs;
param->PreparedXactProcs = PreparedXactProcs;
@@ -995,7 +993,6 @@ restore_backend_variables(BackendParameters *param)
LWLockTrancheNames = param->LWLockTrancheNames;
LWLockCounter = param->LWLockCounter;
MainLWLockArray = param->MainLWLockArray;
- ProcStructLock = param->ProcStructLock;
ProcGlobal = param->ProcGlobal;
AuxiliaryProcs = param->AuxiliaryProcs;
PreparedXactProcs = param->PreparedXactProcs;
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 31ccdb1ef89..5045c939313 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -66,15 +66,6 @@ bool log_lock_waits = true;
/* Pointer to this process's PGPROC struct, if any */
PGPROC *MyProc = NULL;
-/*
- * This spinlock protects the freelist of recycled PGPROC structures.
- * We cannot use an LWLock because the LWLock manager depends on already
- * having a PGPROC and a wait semaphore! But these structures are touched
- * relatively infrequently (only at backend startup or shutdown) and not for
- * very long, so a spinlock is okay.
- */
-NON_EXEC_STATIC slock_t *ProcStructLock = NULL;
-
/* Pointers to shared-memory structures */
PROC_HDR *ProcGlobal = NULL;
NON_EXEC_STATIC PGPROC *AuxiliaryProcs = NULL;
@@ -378,12 +369,6 @@ InitProcGlobal(void)
*/
AuxiliaryProcs = &procs[MaxBackends];
PreparedXactProcs = &procs[MaxBackends + NUM_AUXILIARY_PROCS];
-
- /* Create ProcStructLock spinlock, too */
- ProcStructLock = (slock_t *) ShmemInitStruct("ProcStructLock spinlock",
- sizeof(slock_t),
- &found);
- SpinLockInit(ProcStructLock);
}
/*
@@ -429,17 +414,17 @@ InitProcess(void)
* Try to get a proc struct from the appropriate free list. If this
* fails, we must be out of PGPROC structures (not to mention semaphores).
*
- * While we are holding the ProcStructLock, also copy the current shared
+ * While we are holding the spinlock, also copy the current shared
* estimate of spins_per_delay to local storage.
*/
- SpinLockAcquire(ProcStructLock);
+ SpinLockAcquire(&ProcGlobal->freeProcsLock);
set_spins_per_delay(ProcGlobal->spins_per_delay);
if (!dlist_is_empty(procgloballist))
{
MyProc = dlist_container(PGPROC, links, dlist_pop_head_node(procgloballist));
- SpinLockRelease(ProcStructLock);
+ SpinLockRelease(&ProcGlobal->freeProcsLock);
}
else
{
@@ -449,7 +434,7 @@ InitProcess(void)
* error message. XXX do we need to give a different failure message
* in the autovacuum case?
*/
- SpinLockRelease(ProcStructLock);
+ SpinLockRelease(&ProcGlobal->freeProcsLock);
if (AmWalSenderProcess())
ereport(FATAL,
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
@@ -634,13 +619,13 @@ InitAuxiliaryProcess(void)
RegisterPostmasterChildActive();
/*
- * We use the ProcStructLock to protect assignment and releasing of
+ * We use the freeProcsLock to protect assignment and releasing of
* AuxiliaryProcs entries.
*
- * While we are holding the ProcStructLock, also copy the current shared
+ * While we are holding the spinlock, also copy the current shared
* estimate of spins_per_delay to local storage.
*/
- SpinLockAcquire(ProcStructLock);
+ SpinLockAcquire(&ProcGlobal->freeProcsLock);
set_spins_per_delay(ProcGlobal->spins_per_delay);
@@ -655,7 +640,7 @@ InitAuxiliaryProcess(void)
}
if (proctype >= NUM_AUXILIARY_PROCS)
{
- SpinLockRelease(ProcStructLock);
+ SpinLockRelease(&ProcGlobal->freeProcsLock);
elog(FATAL, "all AuxiliaryProcs are in use");
}
@@ -663,7 +648,7 @@ InitAuxiliaryProcess(void)
/* use volatile pointer to prevent code rearrangement */
((volatile PGPROC *) auxproc)->pid = MyProcPid;
- SpinLockRelease(ProcStructLock);
+ SpinLockRelease(&ProcGlobal->freeProcsLock);
MyProc = auxproc;
MyProcNumber = GetNumberFromPGProc(MyProc);
@@ -789,7 +774,7 @@ HaveNFreeProcs(int n, int *nfree)
Assert(n > 0);
Assert(nfree);
- SpinLockAcquire(ProcStructLock);
+ SpinLockAcquire(&ProcGlobal->freeProcsLock);
*nfree = 0;
dlist_foreach(iter, &ProcGlobal->freeProcs)
@@ -799,7 +784,7 @@ HaveNFreeProcs(int n, int *nfree)
break;
}
- SpinLockRelease(ProcStructLock);
+ SpinLockRelease(&ProcGlobal->freeProcsLock);
return (*nfree == n);
}
@@ -980,9 +965,9 @@ ProcKill(int code, Datum arg)
procgloballist = leader->procgloballist;
/* Leader exited first; return its PGPROC. */
- SpinLockAcquire(ProcStructLock);
+ SpinLockAcquire(&ProcGlobal->freeProcsLock);
dlist_push_head(procgloballist, &leader->links);
- SpinLockRelease(ProcStructLock);
+ SpinLockRelease(&ProcGlobal->freeProcsLock);
}
}
else if (leader != MyProc)
@@ -1013,7 +998,7 @@ ProcKill(int code, Datum arg)
proc->vxid.lxid = InvalidTransactionId;
procgloballist = proc->procgloballist;
- SpinLockAcquire(ProcStructLock);
+ SpinLockAcquire(&ProcGlobal->freeProcsLock);
/*
* If we're still a member of a locking group, that means we're a leader
@@ -1032,7 +1017,7 @@ ProcKill(int code, Datum arg)
/* Update shared estimate of spins_per_delay */
ProcGlobal->spins_per_delay = update_spins_per_delay(ProcGlobal->spins_per_delay);
- SpinLockRelease(ProcStructLock);
+ SpinLockRelease(&ProcGlobal->freeProcsLock);
}
/*
@@ -1072,7 +1057,7 @@ AuxiliaryProcKill(int code, Datum arg)
MyProcNumber = INVALID_PROC_NUMBER;
DisownLatch(&proc->procLatch);
- SpinLockAcquire(ProcStructLock);
+ SpinLockAcquire(&ProcGlobal->freeProcsLock);
/* Mark auxiliary proc no longer in use */
proc->pid = 0;
@@ -1082,7 +1067,7 @@ AuxiliaryProcKill(int code, Datum arg)
/* Update shared estimate of spins_per_delay */
ProcGlobal->spins_per_delay = update_spins_per_delay(ProcGlobal->spins_per_delay);
- SpinLockRelease(ProcStructLock);
+ SpinLockRelease(&ProcGlobal->freeProcsLock);
}
/*
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index ac0df4aeaaa..23e5cd98161 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -420,6 +420,16 @@ typedef struct PROC_HDR
/* Length of allProcs array */
uint32 allProcCount;
+
+ /*
+ * This spinlock protects the below freelists of PGPROC structures. We
+ * cannot use an LWLock because the LWLock manager depends on already
+ * having a PGPROC and a wait semaphore! But these structures are touched
+ * relatively infrequently (only at backend startup or shutdown) and not
+ * for very long, so a spinlock is okay.
+ */
+ slock_t freeProcsLock;
+
/* Head of list of free PGPROC structures */
dlist_head freeProcs;
/* Head of list of autovacuum & special worker free PGPROC structures */
@@ -428,6 +438,7 @@ typedef struct PROC_HDR
dlist_head bgworkerFreeProcs;
/* Head of list of walsender free PGPROC structures */
dlist_head walsenderFreeProcs;
+
/* First pgproc waiting for group XID clear */
pg_atomic_uint32 procArrayGroupFirst;
/* First pgproc waiting for group transaction status update */
@@ -489,7 +500,6 @@ extern PGDLLIMPORT int IdleSessionTimeout;
extern PGDLLIMPORT bool log_lock_waits;
#ifdef EXEC_BACKEND
-extern PGDLLIMPORT slock_t *ProcStructLock;
extern PGDLLIMPORT PGPROC *AuxiliaryProcs;
#endif
--
2.47.3