From d76d6b308f12cdd3e09d5b1d43a734ef4805b06e Mon Sep 17 00:00:00 2001
From: Nathan Bossart <bossartn@amazon.com>
Date: Tue, 10 Aug 2021 00:05:00 +0000
Subject: [PATCH v3 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().

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             | 16 ++++++------
 src/backend/utils/activity/backend_status.c | 10 ++++----
 src/backend/utils/adt/lockfuncs.c           |  4 +--
 src/backend/utils/init/postinit.c           | 39 +++++++++++++++++++++--------
 src/include/miscadmin.h                     |  3 ++-
 19 files changed, 99 insertions(+), 80 deletions(-)

diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index d524310723..427a3b57dd 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(), 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();
 	}
 	else
 		Assert(found);
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index e6c70ed0bc..2eed5fbd01 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() + 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..10ce1538a6 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() + 1 + i;
 		}
 	}
 	else
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 4b16fb5682..b2d5548c8d 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() + 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() + 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(); 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() * sizeof(int32));
+	ids = (BackendId *) palloc(GetMaxBackends() * sizeof(BackendId));
 	count = 0;
 
 	LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 89a5f901aa..00e1d9c0b3 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() * 2;
 		if (maxconn > PG_SOMAXCONN)
 			maxconn = PG_SOMAXCONN;
 
diff --git a/src/backend/postmaster/auxprocess.c b/src/backend/postmaster/auxprocess.c
index 7452f908b2..201560181f 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() + 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..f7688a86a1 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();
 
 #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..84ad54a82a 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();
 	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..8506f12372 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() + 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..b4fd8010ea 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() + 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..2a3bd0216d 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()));
 
 	return size;
 }
@@ -231,7 +231,7 @@ CreateSharedInvalidationState(void)
 	shmInvalBuffer->maxMsgNum = 0;
 	shmInvalBuffer->nextThreshold = CLEANUP_MIN;
 	shmInvalBuffer->lastBackend = 0;
-	shmInvalBuffer->maxBackends = MaxBackends;
+	shmInvalBuffer->maxBackends = GetMaxBackends();
 	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..6c540b0c0d 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();
 
 	/* 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() <= 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() > 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());
 				nDeadlockDetails = depth;
 
 				return true;
@@ -500,7 +501,7 @@ FindLockCycleRecurse(PGPROC *checkProc,
 		}
 	}
 	/* Mark proc as seen */
-	Assert(nVisitedProcs < MaxBackends);
+	Assert(nVisitedProcs < GetMaxBackends());
 	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());
 					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());
 					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());
 
 		/*
 		 * 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..b1c78c1401 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, add_size(GetMaxBackends(), 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() + max_prepared_xacts + 1));
 	}
 	else
 		vxids = (VirtualTransactionId *)
 			palloc0(sizeof(VirtualTransactionId) *
-					(MaxBackends + max_prepared_xacts + 1));
+					(GetMaxBackends() + 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() + 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();
 	el = 0;
 	data->locks = (LockInstanceData *) palloc(sizeof(LockInstanceData) * els);
 
@@ -3685,7 +3685,7 @@ GetLockStatusData(void)
 
 			if (el >= els)
 			{
-				els += MaxBackends;
+				els += GetMaxBackends();
 				data->locks = (LockInstanceData *)
 					repalloc(data->locks, sizeof(LockInstanceData) * els);
 			}
@@ -3717,7 +3717,7 @@ GetLockStatusData(void)
 
 			if (el >= els)
 			{
-				els += MaxBackends;
+				els += GetMaxBackends();
 				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();
 	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();
 			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(),
 							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..3ffb74e1f7 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, add_size(GetMaxBackends(), 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() + 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() + 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() + 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() + max_prepared_xacts));
 		}
 
 		/*
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index b7d9da0aa9..9046ccec2d 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -103,7 +103,7 @@ ProcGlobalShmemSize(void)
 {
 	Size		size = 0;
 	Size		TotalProcs =
-	add_size(MaxBackends, add_size(NUM_AUXILIARY_PROCS, max_prepared_xacts));
+	add_size(GetMaxBackends(), add_size(NUM_AUXILIARY_PROCS, max_prepared_xacts));
 
 	/* ProcGlobal */
 	size = add_size(size, sizeof(PROC_HDR));
@@ -127,7 +127,7 @@ ProcGlobalSemas(void)
 	 * We need a sema per backend (including autovacuum), plus one for each
 	 * auxiliary process.
 	 */
-	return MaxBackends + NUM_AUXILIARY_PROCS;
+	return GetMaxBackends() + NUM_AUXILIARY_PROCS;
 }
 
 /*
@@ -162,7 +162,7 @@ InitProcGlobal(void)
 	int			i,
 				j;
 	bool		found;
-	uint32		TotalProcs = MaxBackends + NUM_AUXILIARY_PROCS + max_prepared_xacts;
+	uint32		TotalProcs = GetMaxBackends() + NUM_AUXILIARY_PROCS + max_prepared_xacts;
 
 	/* Create the ProcGlobal shared structure */
 	ProcGlobal = (PROC_HDR *)
@@ -197,7 +197,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() + NUM_AUXILIARY_PROCS;
 
 	/*
 	 * Allocate arrays mirroring PGPROC fields in a dense manner. See
@@ -223,7 +223,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() + NUM_AUXILIARY_PROCS)
 		{
 			procs[i].sem = PGSemaphoreCreate();
 			InitSharedLatch(&(procs[i].procLatch));
@@ -260,7 +260,7 @@ InitProcGlobal(void)
 			ProcGlobal->bgworkerFreeProcs = &procs[i];
 			procs[i].procgloballist = &ProcGlobal->bgworkerFreeProcs;
 		}
-		else if (i < MaxBackends)
+		else if (i < GetMaxBackends())
 		{
 			/* PGPROC for walsender, add to walsenderFreeProcs list */
 			procs[i].links.next = (SHM_QUEUE *) ProcGlobal->walsenderFreeProcs;
@@ -288,8 +288,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()];
+	PreparedXactProcs = &procs[GetMaxBackends() + 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..7a1b4eebd7 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() + NUM_AUXPROCTYPES)
 
 
 /* ----------
@@ -251,7 +251,7 @@ pgstat_beinit(void)
 	/* Initialize MyBEEntry */
 	if (MyBackendId != InvalidBackendId)
 	{
-		Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends);
+		Assert(MyBackendId >= 1 && MyBackendId <= GetMaxBackends());
 		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() + 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(); 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(); i++)
 	{
 		if (beentry->st_procpid == pid)
 		{
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 5dc0a5882c..f905824215 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() * sizeof(int));
 
 	/* Collect a snapshot of processes waited for by GetSafeSnapshot */
 	num_blockers =
-		GetSafeSnapshotBlockingPids(blocked_pid, blockers, MaxBackends);
+		GetSafeSnapshotBlockingPids(blocked_pid, blockers, GetMaxBackends());
 
 	/* 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 5089dd43ae..ba413be4f8 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 chance 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,31 @@ 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);
+}
 
-	/* internal error because the values were all checked previously */
-	if (MaxBackends > MAX_BACKENDS)
+int
+GetMaxBackends(void)
+{
+	if (!MaxBackendsInitialized)
+		elog(ERROR, "MaxBackends not yet initialized");
+
+	return MaxBackends;
+}
+
+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;
 }
 
 /*
@@ -596,7 +615,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 
 	SharedInvalBackendInit(false);
 
-	if (MyBackendId > MaxBackends || MyBackendId <= 0)
+	if (MyBackendId > GetMaxBackends() || 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..fd0977ab05 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;
@@ -455,6 +454,8 @@ extern AuxProcType MyAuxProcType;
 /* in utils/init/postinit.c */
 extern void pg_split_opts(char **argv, int *argcp, const char *optstr);
 extern void InitializeMaxBackends(void);
+extern int GetMaxBackends(void);
+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

