From ee6b30cc910bcc53670bed65237518a59e88ae4b Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Sat, 7 Aug 2021 17:58:07 +0000 Subject: [PATCH v2 1/1] Disallow external access to MaxBackends. Presently, MaxBackends is externally visible, but it may still be uninitialized in places where it would be convenient to use (e.g., _PG_init()). This change makes MaxBackends static to postinit.c to disallow such direct access. Instead, MaxBackends should now be accessed via GetMaxBackends(). GetMaxBackends() also accepts a couple of flags to easily include commonly added values such as max_prepared_transactions. Separately, adjust the comments about needing to register background workers before initializing MaxBackends. Since 6bc8ef0b, InitializeMaxBackends() has used max_worker_processes instead of tallying up the number of registered background workers, so background worker registration is no longer a prerequisite. The ordering of this logic is still useful for allowing libraries to adjust GUCs, so the comments have been updated to mention that use- case. --- src/backend/access/nbtree/nbtutils.c | 4 +-- src/backend/access/transam/multixact.c | 2 +- src/backend/access/transam/twophase.c | 2 +- src/backend/commands/async.c | 10 +++--- src/backend/libpq/pqcomm.c | 2 +- src/backend/postmaster/auxprocess.c | 2 +- src/backend/postmaster/postmaster.c | 14 ++++----- src/backend/storage/ipc/dsm.c | 2 +- src/backend/storage/ipc/procarray.c | 2 +- src/backend/storage/ipc/procsignal.c | 2 +- src/backend/storage/ipc/sinvaladt.c | 4 +-- src/backend/storage/lmgr/deadlock.c | 31 +++++++++--------- src/backend/storage/lmgr/lock.c | 20 ++++++------ src/backend/storage/lmgr/predicate.c | 10 +++--- src/backend/storage/lmgr/proc.c | 17 +++++----- src/backend/utils/activity/backend_status.c | 10 +++--- src/backend/utils/adt/lockfuncs.c | 4 +-- src/backend/utils/init/postinit.c | 49 +++++++++++++++++++++++------ src/include/miscadmin.h | 12 ++++++- 19 files changed, 118 insertions(+), 81 deletions(-) diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index d524310723..578d4531a2 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -2065,7 +2065,7 @@ BTreeShmemSize(void) Size size; size = offsetof(BTVacInfo, vacuums); - size = add_size(size, mul_size(MaxBackends, sizeof(BTOneVacInfo))); + size = add_size(size, mul_size(GetMaxBackends(0), sizeof(BTOneVacInfo))); return size; } @@ -2094,7 +2094,7 @@ BTreeShmemInit(void) btvacinfo->cycle_ctr = (BTCycleId) time(NULL); btvacinfo->num_vacuums = 0; - btvacinfo->max_vacuums = MaxBackends; + btvacinfo->max_vacuums = GetMaxBackends(0); } else Assert(found); diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index e6c70ed0bc..7e9728bf88 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -285,7 +285,7 @@ typedef struct MultiXactStateData * Last element of OldestMemberMXactId and OldestVisibleMXactId arrays. * Valid elements are (1..MaxOldestSlot); element 0 is never used. */ -#define MaxOldestSlot (MaxBackends + max_prepared_xacts) +#define MaxOldestSlot (GetMaxBackends(GMB_MAX_PREPARED_XACTS)) /* Pointers to the state data in shared memory */ static MultiXactStateData *MultiXactState; diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index 6d3efb49a4..1ba84983f2 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -293,7 +293,7 @@ TwoPhaseShmemInit(void) * prepared transaction. Currently multixact.c uses that * technique. */ - gxacts[i].dummyBackendId = MaxBackends + 1 + i; + gxacts[i].dummyBackendId = GetMaxBackends(0) + 1 + i; } } else diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index 4b16fb5682..29c50595c2 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -511,7 +511,7 @@ AsyncShmemSize(void) Size size; /* This had better match AsyncShmemInit */ - size = mul_size(MaxBackends + 1, sizeof(QueueBackendStatus)); + size = mul_size(GetMaxBackends(0) + 1, sizeof(QueueBackendStatus)); size = add_size(size, offsetof(AsyncQueueControl, backend)); size = add_size(size, SimpleLruShmemSize(NUM_NOTIFY_BUFFERS, 0)); @@ -534,7 +534,7 @@ AsyncShmemInit(void) * The used entries in the backend[] array run from 1 to MaxBackends; the * zero'th entry is unused but must be allocated. */ - size = mul_size(MaxBackends + 1, sizeof(QueueBackendStatus)); + size = mul_size(GetMaxBackends(0) + 1, sizeof(QueueBackendStatus)); size = add_size(size, offsetof(AsyncQueueControl, backend)); asyncQueueControl = (AsyncQueueControl *) @@ -549,7 +549,7 @@ AsyncShmemInit(void) QUEUE_FIRST_LISTENER = InvalidBackendId; asyncQueueControl->lastQueueFillWarn = 0; /* zero'th entry won't be used, but let's initialize it anyway */ - for (int i = 0; i <= MaxBackends; i++) + for (int i = 0; i <= GetMaxBackends(0); i++) { QUEUE_BACKEND_PID(i) = InvalidPid; QUEUE_BACKEND_DBOID(i) = InvalidOid; @@ -1685,8 +1685,8 @@ SignalBackends(void) * preallocate the arrays? But in practice this is only run in trivial * transactions, so there should surely be space available. */ - pids = (int32 *) palloc(MaxBackends * sizeof(int32)); - ids = (BackendId *) palloc(MaxBackends * sizeof(BackendId)); + pids = (int32 *) palloc(GetMaxBackends(0) * sizeof(int32)); + ids = (BackendId *) palloc(GetMaxBackends(0) * sizeof(BackendId)); count = 0; LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE); diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index 89a5f901aa..4d74f9a3f5 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -556,7 +556,7 @@ StreamServerPort(int family, const char *hostName, unsigned short portNumber, * intended to provide a clamp on the request on platforms where an * overly large request provokes a kernel error (are there any?). */ - maxconn = MaxBackends * 2; + maxconn = GetMaxBackends(0) * 2; if (maxconn > PG_SOMAXCONN) maxconn = PG_SOMAXCONN; diff --git a/src/backend/postmaster/auxprocess.c b/src/backend/postmaster/auxprocess.c index 7452f908b2..208aa5803d 100644 --- a/src/backend/postmaster/auxprocess.c +++ b/src/backend/postmaster/auxprocess.c @@ -116,7 +116,7 @@ AuxiliaryProcessMain(AuxProcType auxtype) * This will need rethinking if we ever want more than one of a particular * auxiliary process type. */ - ProcSignalInit(MaxBackends + MyAuxProcType + 1); + ProcSignalInit(GetMaxBackends(0) + MyAuxProcType + 1); /* * Auxiliary processes don't run transactions, but they may need a diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index fc0bc8d99e..271fbce4b8 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -997,10 +997,8 @@ PostmasterMain(int argc, char *argv[]) LocalProcessControlFile(false); /* - * Register the apply launcher. Since it registers a background worker, - * it needs to be called before InitializeMaxBackends(), and it's probably - * a good idea to call it before any modules had chance to take the - * background worker slots. + * Register the apply launcher. It's probably a good idea to call it before + * any modules had a chance to take the background worker slots. */ ApplyLauncherRegister(); @@ -1021,8 +1019,8 @@ PostmasterMain(int argc, char *argv[]) #endif /* - * Now that loadable modules have had their chance to register background - * workers, calculate MaxBackends. + * Now that loadable modules have had their chance to alter any GUCs, + * calculate MaxBackends. */ InitializeMaxBackends(); @@ -6187,7 +6185,7 @@ save_backend_variables(BackendParameters *param, Port *port, param->query_id_enabled = query_id_enabled; param->max_safe_fds = max_safe_fds; - param->MaxBackends = MaxBackends; + param->MaxBackends = GetMaxBackends(0); #ifdef WIN32 param->PostmasterHandle = PostmasterHandle; @@ -6421,7 +6419,7 @@ restore_backend_variables(BackendParameters *param, Port *port) query_id_enabled = param->query_id_enabled; max_safe_fds = param->max_safe_fds; - MaxBackends = param->MaxBackends; + SetMaxBackends(param->MaxBackends); #ifdef WIN32 PostmasterHandle = param->PostmasterHandle; diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c index b461a5f7e9..bb279bb8d1 100644 --- a/src/backend/storage/ipc/dsm.c +++ b/src/backend/storage/ipc/dsm.c @@ -165,7 +165,7 @@ dsm_postmaster_startup(PGShmemHeader *shim) /* Determine size for new control segment. */ maxitems = PG_DYNSHMEM_FIXED_SLOTS - + PG_DYNSHMEM_SLOTS_PER_BACKEND * MaxBackends; + + PG_DYNSHMEM_SLOTS_PER_BACKEND * GetMaxBackends(0); elog(DEBUG2, "dynamic shared memory system will support %u segments", maxitems); segsize = dsm_control_bytes_needed(maxitems); diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index c7816fcfb3..9afd0209c8 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -365,7 +365,7 @@ ProcArrayShmemSize(void) Size size; /* Size of the ProcArray structure itself */ -#define PROCARRAY_MAXPROCS (MaxBackends + max_prepared_xacts) +#define PROCARRAY_MAXPROCS (GetMaxBackends(GMB_MAX_PREPARED_XACTS)) size = offsetof(ProcArrayStruct, pgprocnos); size = add_size(size, mul_size(sizeof(int), PROCARRAY_MAXPROCS)); diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c index defb75aa26..86b7c39782 100644 --- a/src/backend/storage/ipc/procsignal.c +++ b/src/backend/storage/ipc/procsignal.c @@ -85,7 +85,7 @@ typedef struct * possible auxiliary process type. (This scheme assumes there is not * more than one of any auxiliary process type at a time.) */ -#define NumProcSignalSlots (MaxBackends + NUM_AUXPROCTYPES) +#define NumProcSignalSlots (GetMaxBackends(0) + NUM_AUXPROCTYPES) /* Check whether the relevant type bit is set in the flags. */ #define BARRIER_SHOULD_CHECK(flags, type) \ diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c index 946bd8e3cb..2e85a99f68 100644 --- a/src/backend/storage/ipc/sinvaladt.c +++ b/src/backend/storage/ipc/sinvaladt.c @@ -205,7 +205,7 @@ SInvalShmemSize(void) Size size; size = offsetof(SISeg, procState); - size = add_size(size, mul_size(sizeof(ProcState), MaxBackends)); + size = add_size(size, mul_size(sizeof(ProcState), GetMaxBackends(0))); return size; } @@ -231,7 +231,7 @@ CreateSharedInvalidationState(void) shmInvalBuffer->maxMsgNum = 0; shmInvalBuffer->nextThreshold = CLEANUP_MIN; shmInvalBuffer->lastBackend = 0; - shmInvalBuffer->maxBackends = MaxBackends; + shmInvalBuffer->maxBackends = GetMaxBackends(0); SpinLockInit(&shmInvalBuffer->msgnumLock); /* The buffer[] array is initially all unused, so we need not fill it */ diff --git a/src/backend/storage/lmgr/deadlock.c b/src/backend/storage/lmgr/deadlock.c index 67733c0d1a..6ffc481009 100644 --- a/src/backend/storage/lmgr/deadlock.c +++ b/src/backend/storage/lmgr/deadlock.c @@ -143,6 +143,7 @@ void InitDeadLockChecking(void) { MemoryContext oldcxt; + int max_backends = GetMaxBackends(0); /* Make sure allocations are permanent */ oldcxt = MemoryContextSwitchTo(TopMemoryContext); @@ -151,16 +152,16 @@ InitDeadLockChecking(void) * FindLockCycle needs at most MaxBackends entries in visitedProcs[] and * deadlockDetails[]. */ - visitedProcs = (PGPROC **) palloc(MaxBackends * sizeof(PGPROC *)); - deadlockDetails = (DEADLOCK_INFO *) palloc(MaxBackends * sizeof(DEADLOCK_INFO)); + visitedProcs = (PGPROC **) palloc(max_backends * sizeof(PGPROC *)); + deadlockDetails = (DEADLOCK_INFO *) palloc(max_backends * sizeof(DEADLOCK_INFO)); /* * TopoSort needs to consider at most MaxBackends wait-queue entries, and * it needn't run concurrently with FindLockCycle. */ topoProcs = visitedProcs; /* re-use this space */ - beforeConstraints = (int *) palloc(MaxBackends * sizeof(int)); - afterConstraints = (int *) palloc(MaxBackends * sizeof(int)); + beforeConstraints = (int *) palloc(max_backends * sizeof(int)); + afterConstraints = (int *) palloc(max_backends * sizeof(int)); /* * We need to consider rearranging at most MaxBackends/2 wait queues @@ -169,8 +170,8 @@ InitDeadLockChecking(void) * MaxBackends total waiters. */ waitOrders = (WAIT_ORDER *) - palloc((MaxBackends / 2) * sizeof(WAIT_ORDER)); - waitOrderProcs = (PGPROC **) palloc(MaxBackends * sizeof(PGPROC *)); + palloc((max_backends / 2) * sizeof(WAIT_ORDER)); + waitOrderProcs = (PGPROC **) palloc(max_backends * sizeof(PGPROC *)); /* * Allow at most MaxBackends distinct constraints in a configuration. (Is @@ -180,7 +181,7 @@ InitDeadLockChecking(void) * limits the maximum recursion depth of DeadLockCheckRecurse. Making it * really big might potentially allow a stack-overflow problem. */ - maxCurConstraints = MaxBackends; + maxCurConstraints = max_backends; curConstraints = (EDGE *) palloc(maxCurConstraints * sizeof(EDGE)); /* @@ -191,7 +192,7 @@ InitDeadLockChecking(void) * last MaxBackends entries in possibleConstraints[] are reserved as * output workspace for FindLockCycle. */ - maxPossibleConstraints = MaxBackends * 4; + maxPossibleConstraints = max_backends * 4; possibleConstraints = (EDGE *) palloc(maxPossibleConstraints * sizeof(EDGE)); @@ -327,7 +328,7 @@ DeadLockCheckRecurse(PGPROC *proc) if (nCurConstraints >= maxCurConstraints) return true; /* out of room for active constraints? */ oldPossibleConstraints = nPossibleConstraints; - if (nPossibleConstraints + nEdges + MaxBackends <= maxPossibleConstraints) + if (nPossibleConstraints + nEdges + GetMaxBackends(0) <= maxPossibleConstraints) { /* We can save the edge list in possibleConstraints[] */ nPossibleConstraints += nEdges; @@ -388,7 +389,7 @@ TestConfiguration(PGPROC *startProc) /* * Make sure we have room for FindLockCycle's output. */ - if (nPossibleConstraints + MaxBackends > maxPossibleConstraints) + if (nPossibleConstraints + GetMaxBackends(0) > maxPossibleConstraints) return -1; /* @@ -486,7 +487,7 @@ FindLockCycleRecurse(PGPROC *checkProc, * record total length of cycle --- outer levels will now fill * deadlockDetails[] */ - Assert(depth <= MaxBackends); + Assert(depth <= GetMaxBackends(0)); nDeadlockDetails = depth; return true; @@ -500,7 +501,7 @@ FindLockCycleRecurse(PGPROC *checkProc, } } /* Mark proc as seen */ - Assert(nVisitedProcs < MaxBackends); + Assert(nVisitedProcs < GetMaxBackends(0)); visitedProcs[nVisitedProcs++] = checkProc; /* @@ -698,7 +699,7 @@ FindLockCycleRecurseMember(PGPROC *checkProc, /* * Add this edge to the list of soft edges in the cycle */ - Assert(*nSoftEdges < MaxBackends); + Assert(*nSoftEdges < GetMaxBackends(0)); softEdges[*nSoftEdges].waiter = checkProcLeader; softEdges[*nSoftEdges].blocker = leader; softEdges[*nSoftEdges].lock = lock; @@ -771,7 +772,7 @@ FindLockCycleRecurseMember(PGPROC *checkProc, /* * Add this edge to the list of soft edges in the cycle */ - Assert(*nSoftEdges < MaxBackends); + Assert(*nSoftEdges < GetMaxBackends(0)); softEdges[*nSoftEdges].waiter = checkProcLeader; softEdges[*nSoftEdges].blocker = leader; softEdges[*nSoftEdges].lock = lock; @@ -834,7 +835,7 @@ ExpandConstraints(EDGE *constraints, waitOrders[nWaitOrders].procs = waitOrderProcs + nWaitOrderProcs; waitOrders[nWaitOrders].nProcs = lock->waitProcs.size; nWaitOrderProcs += lock->waitProcs.size; - Assert(nWaitOrderProcs <= MaxBackends); + Assert(nWaitOrderProcs <= GetMaxBackends(0)); /* * Do the topo sort. TopoSort need not examine constraints after this diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index 364654e106..581bbc29d5 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -55,7 +55,7 @@ int max_locks_per_xact; /* set by guc.c */ #define NLOCKENTS() \ - mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts)) + mul_size(max_locks_per_xact, GetMaxBackends(GMB_MAX_PREPARED_XACTS)) /* @@ -2938,12 +2938,12 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp) vxids = (VirtualTransactionId *) MemoryContextAlloc(TopMemoryContext, sizeof(VirtualTransactionId) * - (MaxBackends + max_prepared_xacts + 1)); + (GetMaxBackends(GMB_MAX_PREPARED_XACTS) + 1)); } else vxids = (VirtualTransactionId *) palloc0(sizeof(VirtualTransactionId) * - (MaxBackends + max_prepared_xacts + 1)); + (GetMaxBackends(GMB_MAX_PREPARED_XACTS) + 1)); /* Compute hash code and partition lock, and look up conflicting modes. */ hashcode = LockTagHashCode(locktag); @@ -3100,7 +3100,7 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp) LWLockRelease(partitionLock); - if (count > MaxBackends + max_prepared_xacts) /* should never happen */ + if (count > GetMaxBackends(GMB_MAX_PREPARED_XACTS)) /* should never happen */ elog(PANIC, "too many conflicting locks found"); vxids[count].backendId = InvalidBackendId; @@ -3651,7 +3651,7 @@ GetLockStatusData(void) data = (LockData *) palloc(sizeof(LockData)); /* Guess how much space we'll need. */ - els = MaxBackends; + els = GetMaxBackends(0); el = 0; data->locks = (LockInstanceData *) palloc(sizeof(LockInstanceData) * els); @@ -3685,7 +3685,7 @@ GetLockStatusData(void) if (el >= els) { - els += MaxBackends; + els += GetMaxBackends(0); data->locks = (LockInstanceData *) repalloc(data->locks, sizeof(LockInstanceData) * els); } @@ -3717,7 +3717,7 @@ GetLockStatusData(void) if (el >= els) { - els += MaxBackends; + els += GetMaxBackends(0); data->locks = (LockInstanceData *) repalloc(data->locks, sizeof(LockInstanceData) * els); } @@ -3846,7 +3846,7 @@ GetBlockerStatusData(int blocked_pid) * for the procs[] array; the other two could need enlargement, though.) */ data->nprocs = data->nlocks = data->npids = 0; - data->maxprocs = data->maxlocks = data->maxpids = MaxBackends; + data->maxprocs = data->maxlocks = data->maxpids = GetMaxBackends(0); data->procs = (BlockedProcData *) palloc(sizeof(BlockedProcData) * data->maxprocs); data->locks = (LockInstanceData *) palloc(sizeof(LockInstanceData) * data->maxlocks); data->waiter_pids = (int *) palloc(sizeof(int) * data->maxpids); @@ -3949,7 +3949,7 @@ GetSingleProcBlockerStatusData(PGPROC *blocked_proc, BlockedProcsData *data) if (data->nlocks >= data->maxlocks) { - data->maxlocks += MaxBackends; + data->maxlocks += GetMaxBackends(0); data->locks = (LockInstanceData *) repalloc(data->locks, sizeof(LockInstanceData) * data->maxlocks); } @@ -3978,7 +3978,7 @@ GetSingleProcBlockerStatusData(PGPROC *blocked_proc, BlockedProcsData *data) if (queue_size > data->maxpids - data->npids) { - data->maxpids = Max(data->maxpids + MaxBackends, + data->maxpids = Max(data->maxpids + GetMaxBackends(0), data->npids + queue_size); data->waiter_pids = (int *) repalloc(data->waiter_pids, sizeof(int) * data->maxpids); diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index 56267bdc3c..d6199aa82d 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -257,7 +257,7 @@ (&MainLWLockArray[PREDICATELOCK_MANAGER_LWLOCK_OFFSET + (i)].lock) #define NPREDICATELOCKTARGETENTS() \ - mul_size(max_predicate_locks_per_xact, add_size(MaxBackends, max_prepared_xacts)) + mul_size(max_predicate_locks_per_xact, GetMaxBackends(GMB_MAX_PREPARED_XACTS)) #define SxactIsOnFinishedList(sxact) (!SHMQueueIsDetached(&((sxact)->finishedLink))) @@ -1222,7 +1222,7 @@ InitPredicateLocks(void) * Compute size for serializable transaction hashtable. Note these * calculations must agree with PredicateLockShmemSize! */ - max_table_size = (MaxBackends + max_prepared_xacts); + max_table_size = (GetMaxBackends(GMB_MAX_PREPARED_XACTS)); /* * Allocate a list to hold information on transactions participating in @@ -1374,7 +1374,7 @@ PredicateLockShmemSize(void) size = add_size(size, size / 10); /* transaction list */ - max_table_size = MaxBackends + max_prepared_xacts; + max_table_size = GetMaxBackends(GMB_MAX_PREPARED_XACTS); max_table_size *= 10; size = add_size(size, PredXactListDataSize); size = add_size(size, mul_size((Size) max_table_size, @@ -1905,7 +1905,7 @@ GetSerializableTransactionSnapshotInt(Snapshot snapshot, { ++(PredXact->WritableSxactCount); Assert(PredXact->WritableSxactCount <= - (MaxBackends + max_prepared_xacts)); + (GetMaxBackends(GMB_MAX_PREPARED_XACTS))); } MySerializableXact = sxact; @@ -5107,7 +5107,7 @@ predicatelock_twophase_recover(TransactionId xid, uint16 info, { ++(PredXact->WritableSxactCount); Assert(PredXact->WritableSxactCount <= - (MaxBackends + max_prepared_xacts)); + (GetMaxBackends(GMB_MAX_PREPARED_XACTS))); } /* diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index b7d9da0aa9..9600afec16 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -102,8 +102,7 @@ Size ProcGlobalShmemSize(void) { Size size = 0; - Size TotalProcs = - add_size(MaxBackends, add_size(NUM_AUXILIARY_PROCS, max_prepared_xacts)); + Size TotalProcs = GetMaxBackends(GMB_NUM_AUXILIARY_PROCS | GMB_MAX_PREPARED_XACTS); /* ProcGlobal */ size = add_size(size, sizeof(PROC_HDR)); @@ -127,7 +126,7 @@ ProcGlobalSemas(void) * We need a sema per backend (including autovacuum), plus one for each * auxiliary process. */ - return MaxBackends + NUM_AUXILIARY_PROCS; + return GetMaxBackends(GMB_NUM_AUXILIARY_PROCS); } /* @@ -162,7 +161,7 @@ InitProcGlobal(void) int i, j; bool found; - uint32 TotalProcs = MaxBackends + NUM_AUXILIARY_PROCS + max_prepared_xacts; + uint32 TotalProcs = GetMaxBackends(GMB_NUM_AUXILIARY_PROCS | GMB_MAX_PREPARED_XACTS); /* Create the ProcGlobal shared structure */ ProcGlobal = (PROC_HDR *) @@ -197,7 +196,7 @@ InitProcGlobal(void) MemSet(procs, 0, TotalProcs * sizeof(PGPROC)); ProcGlobal->allProcs = procs; /* XXX allProcCount isn't really all of them; it excludes prepared xacts */ - ProcGlobal->allProcCount = MaxBackends + NUM_AUXILIARY_PROCS; + ProcGlobal->allProcCount = GetMaxBackends(GMB_NUM_AUXILIARY_PROCS); /* * Allocate arrays mirroring PGPROC fields in a dense manner. See @@ -223,7 +222,7 @@ InitProcGlobal(void) * dummy PGPROCs don't need these though - they're never associated * with a real process */ - if (i < MaxBackends + NUM_AUXILIARY_PROCS) + if (i < GetMaxBackends(GMB_NUM_AUXILIARY_PROCS)) { procs[i].sem = PGSemaphoreCreate(); InitSharedLatch(&(procs[i].procLatch)); @@ -260,7 +259,7 @@ InitProcGlobal(void) ProcGlobal->bgworkerFreeProcs = &procs[i]; procs[i].procgloballist = &ProcGlobal->bgworkerFreeProcs; } - else if (i < MaxBackends) + else if (i < GetMaxBackends(0)) { /* PGPROC for walsender, add to walsenderFreeProcs list */ procs[i].links.next = (SHM_QUEUE *) ProcGlobal->walsenderFreeProcs; @@ -288,8 +287,8 @@ InitProcGlobal(void) * Save pointers to the blocks of PGPROC structures reserved for auxiliary * processes and prepared transactions. */ - AuxiliaryProcs = &procs[MaxBackends]; - PreparedXactProcs = &procs[MaxBackends + NUM_AUXILIARY_PROCS]; + AuxiliaryProcs = &procs[GetMaxBackends(0)]; + PreparedXactProcs = &procs[GetMaxBackends(GMB_NUM_AUXILIARY_PROCS)]; /* Create ProcStructLock spinlock, too */ ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t)); diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c index 2901f9f5a9..4ff834bbc5 100644 --- a/src/backend/utils/activity/backend_status.c +++ b/src/backend/utils/activity/backend_status.c @@ -35,7 +35,7 @@ * includes autovacuum workers and background workers as well. * ---------- */ -#define NumBackendStatSlots (MaxBackends + NUM_AUXPROCTYPES) +#define NumBackendStatSlots (GetMaxBackends(0) + NUM_AUXPROCTYPES) /* ---------- @@ -251,7 +251,7 @@ pgstat_beinit(void) /* Initialize MyBEEntry */ if (MyBackendId != InvalidBackendId) { - Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends); + Assert(MyBackendId >= 1 && MyBackendId <= GetMaxBackends(0)); MyBEEntry = &BackendStatusArray[MyBackendId - 1]; } else @@ -267,7 +267,7 @@ pgstat_beinit(void) * MaxBackends + AuxBackendType + 1 as the index of the slot for an * auxiliary process. */ - MyBEEntry = &BackendStatusArray[MaxBackends + MyAuxProcType]; + MyBEEntry = &BackendStatusArray[GetMaxBackends(0) + MyAuxProcType]; } /* Set up a process-exit hook to clean up */ @@ -892,7 +892,7 @@ pgstat_get_backend_current_activity(int pid, bool checkUser) int i; beentry = BackendStatusArray; - for (i = 1; i <= MaxBackends; i++) + for (i = 1; i <= GetMaxBackends(0); i++) { /* * Although we expect the target backend's entry to be stable, that @@ -978,7 +978,7 @@ pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen) if (beentry == NULL || BackendActivityBuffer == NULL) return NULL; - for (i = 1; i <= MaxBackends; i++) + for (i = 1; i <= GetMaxBackends(0); i++) { if (beentry->st_procpid == pid) { diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c index 5dc0a5882c..d262965800 100644 --- a/src/backend/utils/adt/lockfuncs.c +++ b/src/backend/utils/adt/lockfuncs.c @@ -561,11 +561,11 @@ pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS) Datum *blocker_datums; /* A buffer big enough for any possible blocker list without truncation */ - blockers = (int *) palloc(MaxBackends * sizeof(int)); + blockers = (int *) palloc(GetMaxBackends(0) * sizeof(int)); /* Collect a snapshot of processes waited for by GetSafeSnapshot */ num_blockers = - GetSafeSnapshotBlockingPids(blocked_pid, blockers, MaxBackends); + GetSafeSnapshotBlockingPids(blocked_pid, blockers, GetMaxBackends(0)); /* Convert int array to Datum array */ if (num_blockers > 0) diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 87dc060b20..89a03edaab 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -25,6 +25,7 @@ #include "access/session.h" #include "access/sysattr.h" #include "access/tableam.h" +#include "access/twophase.h" #include "access/xact.h" #include "access/xlog.h" #include "catalog/catalog.h" @@ -63,6 +64,9 @@ #include "utils/syscache.h" #include "utils/timeout.h" +static int MaxBackends = 0; +static int MaxBackendsInitialized = false; + static HeapTuple GetDatabaseTuple(const char *dbname); static HeapTuple GetDatabaseTupleByOid(Oid dboid); static void PerformAuthentication(Port *port); @@ -476,9 +480,8 @@ pg_split_opts(char **argv, int *argcp, const char *optstr) /* * Initialize MaxBackends value from config options. * - * This must be called after modules have had the chance to register background - * workers in shared_preload_libraries, and before shared memory size is - * determined. + * This must be called after modules have had the change to alter GUCs in + * shared_preload_libraries, and before shared memory size is determined. * * Note that in EXEC_BACKEND environment, the value is passed down from * postmaster to subprocesses via BackendParameters in SubPostmasterMain; only @@ -488,15 +491,41 @@ pg_split_opts(char **argv, int *argcp, const char *optstr) void InitializeMaxBackends(void) { - Assert(MaxBackends == 0); - /* the extra unit accounts for the autovacuum launcher */ - MaxBackends = MaxConnections + autovacuum_max_workers + 1 + - max_worker_processes + max_wal_senders; + SetMaxBackends(MaxConnections + autovacuum_max_workers + 1 + + max_worker_processes + max_wal_senders); +} + +int +GetMaxBackends(uint32 flags) +{ + int ret; + + if (!MaxBackendsInitialized) + elog(ERROR, "MaxBackends not yet initialized"); + + ret = MaxBackends; + + if (flags & GMB_MAX_PREPARED_XACTS) + ret += max_prepared_xacts; - /* internal error because the values were all checked previously */ - if (MaxBackends > MAX_BACKENDS) + if (flags & GMB_NUM_AUXILIARY_PROCS) + ret += NUM_AUXILIARY_PROCS; + + return ret; +} + +void +SetMaxBackends(int max_backends) +{ + if (MaxBackendsInitialized) + elog(ERROR, "MaxBackends already initialized"); + + if (max_backends > MAX_BACKENDS) elog(ERROR, "too many backends configured"); + + MaxBackends = max_backends; + MaxBackendsInitialized = true; } /* @@ -585,7 +614,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, SharedInvalBackendInit(false); - if (MyBackendId > MaxBackends || MyBackendId <= 0) + if (MyBackendId > GetMaxBackends(0) || MyBackendId <= 0) elog(FATAL, "bad backend ID: %d", MyBackendId); /* Now that we have a BackendId, we can participate in ProcSignal */ diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 2e2e9a364a..28e5cecae0 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -172,7 +172,6 @@ extern PGDLLIMPORT char *DataDir; extern PGDLLIMPORT int data_directory_mode; extern PGDLLIMPORT int NBuffers; -extern PGDLLIMPORT int MaxBackends; extern PGDLLIMPORT int MaxConnections; extern PGDLLIMPORT int max_worker_processes; extern PGDLLIMPORT int max_parallel_workers; @@ -452,9 +451,20 @@ extern AuxProcType MyAuxProcType; * POSTGRES initialization and cleanup definitions. * *****************************************************************************/ +/* + * Option flag bits for GetMaxBackends(). + */ +typedef enum GMBOption +{ + GMB_MAX_PREPARED_XACTS = 1 << 0, /* include max_prepared_xacts */ + GMB_NUM_AUXILIARY_PROCS = 1 << 1 /* include NUM_AUXILIARY_PROCS */ +} GMBOption; + /* in utils/init/postinit.c */ extern void pg_split_opts(char **argv, int *argcp, const char *optstr); extern void InitializeMaxBackends(void); +extern int GetMaxBackends(uint32 flags); +extern void SetMaxBackends(int max_backends); extern void InitPostgres(const char *in_dbname, Oid dboid, const char *username, Oid useroid, char *out_dbname, bool override_allow_connections); extern void BaseInit(void); -- 2.16.6