From ad1004aea268d329d4ad03a73fc817f51449aa5b Mon Sep 17 00:00:00 2001
From: Mike Palmiotto <mike.palmiotto@crunchydata.com>
Date: Sun, 29 Sep 2019 17:17:16 -0400
Subject: [PATCH 6/8] Add bgworker to process centralization

---
 src/backend/postmaster/bgworker.c           | 138 ++++++++-
 src/backend/postmaster/postmaster.c         | 316 +++++---------------
 src/include/miscadmin.h                     |   1 +
 src/include/postmaster/bgworker.h           |   4 +
 src/include/postmaster/bgworker_internals.h |   3 +-
 src/include/postmaster/fork_process.h       |   1 +
 src/include/postmaster/postmaster.h         |  53 ++++
 7 files changed, 269 insertions(+), 247 deletions(-)

diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index b66b517aca..8c60a13cd1 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -36,6 +36,7 @@
 #include "utils/ascii.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
+#include "utils/timestamp.h"
 
 /*
  * The postmaster's list of registered background workers, in private memory.
@@ -134,7 +135,110 @@ static const struct
 
 /* Private functions. */
 static bgworker_main_type LookupBackgroundWorkerFunction(const char *libraryname, const char *funcname);
+static bool assign_backendlist_entry(RegisteredBgWorker *rw);
+NON_EXEC_STATIC void BackgroundWorkerFailFork(int argc, char *argv[]);
+NON_EXEC_STATIC void BackgroundWorkerPostmasterMain(int argc, char *argv[]);
 
+/*
+ * Allocate the Backend struct for a connected background worker, but don't
+ * add it to the list of backends just yet.
+ *
+ * On failure, return false without changing any worker state.
+ *
+ * Some info from the Backend is copied into the passed rw.
+ */
+static bool
+assign_backendlist_entry(RegisteredBgWorker *rw)
+{
+	Backend    *bn;
+
+	/*
+	 * Compute the cancel key that will be assigned to this session. We
+	 * probably don't need cancel keys for background workers, but we'd better
+	 * have something random in the field to prevent unfriendly people from
+	 * sending cancels to them.
+	 */
+	if (!RandomCancelKey(&MyCancelKey))
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("could not generate random cancel key")));
+		return false;
+	}
+
+	bn = malloc(sizeof(Backend));
+	if (bn == NULL)
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
+		return false;
+	}
+
+	bn->cancel_key = MyCancelKey;
+	bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
+	bn->bkend_type = BACKEND_TYPE_BGWORKER;
+	bn->dead_end = false;
+	bn->bgworker_notify = false;
+
+	rw->rw_backend = bn;
+	rw->rw_child_slot = bn->child_slot;
+
+	return true;
+}
+
+/*
+ * PrepBgWorkerFork
+ *
+ *  Postmaster subroutine to prepare a bgworker subprocess.
+ *
+ *	Returns 0 on success or -1 on failure.
+ *
+ *	Note: if fail, we will be called again from the postmaster main loop.
+ */
+int
+PrepBgWorkerFork(ForkProcData *bgworker_fork)
+{
+	int				ac = 0;
+	RegisteredBgWorker *rw = CurrentBgWorker;
+
+	Assert(CurrentBgWorker);
+	Assert(rw->rw_pid == 0);
+
+	/*
+	 * Allocate and assign the Backend element.  Note we must do this before
+	 * forking, so that we can handle out of memory properly.
+	 *
+	 * Treat failure as though the worker had crashed.  That way, the
+	 * postmaster will wait a bit before attempting to start it again; if it
+	 * tried again right away, most likely it'd find itself repeating the
+	 * out-of-memory or fork failure condition.
+	 */
+	if (!assign_backendlist_entry(rw))
+	{
+		rw->rw_crashed_at = GetCurrentTimestamp();
+		return -1;
+	}
+
+	ereport(DEBUG1,
+			(errmsg("starting background worker process \"%s\"",
+					rw->rw_worker.bgw_name)));
+
+#ifdef EXEC_BACKEND
+	bgworker_fork->av[ac++] = pstrdup("postgres");
+	bgworker_fork->av[ac++] = psprintf("--forkbgworker=%d", rw->rw_shmem_slot);
+	bgworker_fork->av[ac++] = NULL;			/* filled in by postmaster_forkexec */
+	bgworker_fork->av[ac] = NULL;
+#endif
+	Assert(bgworker_fork->ac < lengthof(*bgworker_fork->av));
+	bgworker_fork->ac = ac;
+	bgworker_fork->type_desc = pstrdup("background worker");
+	bgworker_fork->child_main = BackgroundWorkerMain;
+	bgworker_fork->fail_main = BackgroundWorkerFailFork;
+	bgworker_fork->postmaster_main = BackgroundWorkerPostmasterMain;
+
+	return 0;
+}
 
 /*
  * Calculate shared memory needed.
@@ -698,7 +802,7 @@ bgworker_sigusr1_handler(SIGNAL_ARGS)
  * postmaster.
  */
 void
-StartBackgroundWorker(void)
+BackgroundWorkerMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
 {
 	sigjmp_buf	local_sigjmp_buf;
 	BackgroundWorker *worker = MyBgworkerEntry;
@@ -837,6 +941,38 @@ StartBackgroundWorker(void)
 	proc_exit(0);
 }
 
+NON_EXEC_STATIC void
+BackgroundWorkerFailFork(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
+{
+	RegisteredBgWorker *rw = CurrentBgWorker;
+
+	/* in postmaster, fork failed ... */
+	ereport(LOG,
+			(errmsg("could not fork worker process: %m")));
+
+	/* undo what assign_backendlist_entry did */
+	ReleasePostmasterChildSlot(rw->rw_child_slot);
+	rw->rw_child_slot = 0;
+	free(rw->rw_backend);
+	rw->rw_backend = NULL;
+	/* mark entry as crashed, so we'll try again later */
+	rw->rw_crashed_at = GetCurrentTimestamp();
+}
+
+NON_EXEC_STATIC void
+BackgroundWorkerPostmasterMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
+{
+	RegisteredBgWorker *rw = CurrentBgWorker;
+
+	rw->rw_pid = MyChildProcPid;
+	rw->rw_backend->pid = rw->rw_pid;
+	ReportBackgroundWorkerPID(rw);
+	/* add new worker to lists of backends */
+	dlist_push_head(&BackendList, &(rw->rw_backend->elem));
+#ifdef EXEC_BACKEND
+	ShmemBackendArrayAdd(rw->rw_backend);
+#endif
+}
 /*
  * Register a new static background worker.
  *
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 5d78db067f..8f862fcd64 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -136,62 +136,15 @@
 #include "storage/spin.h"
 #endif
 
-
-/*
- * Possible types of a backend. Beyond being the possible bkend_type values in
- * struct bkend, these are OR-able request flag bits for SignalSomeChildren()
- * and CountChildren().
- */
-#define BACKEND_TYPE_NORMAL		0x0001	/* normal backend */
-#define BACKEND_TYPE_AUTOVAC	0x0002	/* autovacuum worker process */
-#define BACKEND_TYPE_WALSND		0x0004	/* walsender process */
-#define BACKEND_TYPE_BGWORKER	0x0008	/* bgworker process */
-#define BACKEND_TYPE_ALL		0x000F	/* OR of all the above */
-
-#define BACKEND_TYPE_WORKER		(BACKEND_TYPE_AUTOVAC | BACKEND_TYPE_BGWORKER)
-
-/*
- * List of active backends (or child processes anyway; we don't actually
- * know whether a given child has become a backend or is still in the
- * authorization phase).  This is used mainly to keep track of how many
- * children we have and send them appropriate signals when necessary.
- *
- * "Special" children such as the startup, bgwriter and autovacuum launcher
- * tasks are not in this list.  Autovacuum worker and walsender are in it.
- * Also, "dead_end" children are in it: these are children launched just for
- * the purpose of sending a friendly rejection message to a would-be client.
- * We must track them because they are attached to shared memory, but we know
- * they will never become live backends.  dead_end children are not assigned a
- * PMChildSlot.
- *
- * Background workers are in this list, too.
- */
-typedef struct bkend
-{
-	pid_t		pid;			/* process id of backend */
-	int32		cancel_key;		/* cancel key for cancels for this backend */
-	int			child_slot;		/* PMChildSlot for this backend, if any */
-
-	/*
-	 * Flavor of backend or auxiliary process.  Note that BACKEND_TYPE_WALSND
-	 * backends initially announce themselves as BACKEND_TYPE_NORMAL, so if
-	 * bkend_type is normal, you should check for a recent transition.
-	 */
-	int			bkend_type;
-	bool		dead_end;		/* is it going to send an error and quit? */
-	bool		bgworker_notify;	/* gets bgworker start/stop notifications */
-	dlist_node	elem;			/* list link in BackendList */
-} Backend;
-
-static dlist_head BackendList = DLIST_STATIC_INIT(BackendList);
+dlist_head BackendList = DLIST_STATIC_INIT(BackendList);
 
 #ifdef EXEC_BACKEND
 static Backend *ShmemBackendArray;
 #endif
 
 BackgroundWorker *MyBgworkerEntry = NULL;
-
-
+RegisteredBgWorker *CurrentBgWorker = NULL;
+static int			child_errno;
 
 /* The socket number we are listening for connections on */
 int			PostPortNumber;
@@ -410,7 +363,6 @@ static void processCancelRequest(Port *port, void *pkt);
 static int	initMasks(fd_set *rmask);
 static void report_fork_failure_to_client(Port *port, int errnum);
 static CAC_state canAcceptConnections(void);
-static bool RandomCancelKey(int32 *cancel_key);
 static void signal_child(pid_t pid, int signal);
 static bool SignalSomeChildren(int signal, int targets);
 static void TerminateChildren(int signal);
@@ -421,7 +373,6 @@ static void shmemSetup(bool aux_process);
 #define SignalChildren(sig)			   SignalSomeChildren(sig, BACKEND_TYPE_ALL)
 
 static int	CountChildren(int target);
-static bool assign_backendlist_entry(RegisteredBgWorker *rw);
 static void maybe_start_bgworkers(void);
 static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
 static pid_t StartChildProcess(ForkProcType type);
@@ -538,10 +489,11 @@ static bool save_backend_variables(BackendParameters *param, Port *port,
 								   HANDLE childProcess, pid_t childPid);
 #endif
 
-static void ShmemBackendArrayAdd(Backend *bn);
 static void ShmemBackendArrayRemove(Backend *bn);
 #endif							/* EXEC_BACKEND */
 
+#define BGWORKER_LEN			15
+
 #define StartupDataBase()		StartChildProcess(StartupFork)
 #define StartBackgroundWriter() StartChildProcess(BgWriterFork)
 #define StartCheckpointer()		StartChildProcess(CheckpointerFork)
@@ -552,6 +504,7 @@ static void ShmemBackendArrayRemove(Backend *bn);
 #define pgstat_start()			StartChildProcess(PgstatCollectorFork)
 #define pgarch_start()			StartChildProcess(PgArchiverFork)
 #define SysLogger_Start()		StartChildProcess(SysLoggerFork)
+#define do_start_bgworker()		StartChildProcess(BgWorkerFork)
 
 /* Macros to check exit status of a child process */
 #define EXIT_STATUS_0(st)  ((st) == 0)
@@ -4967,7 +4920,7 @@ SubPostmasterMain(int argc, char *argv[])
 		strcmp(argv[1], "--forkavlauncher") == 0 ||
 		strcmp(argv[1], "--forkavworker") == 0 ||
 		strcmp(argv[1], "--forkboot") == 0 ||
-		strncmp(argv[1], "--forkbgworker=", 15) == 0)
+		strncmp(argv[1], "--forkbgworker=", BGWORKER_LEN) == 0)
 		PGSharedMemoryReAttach();
 	else
 		PGSharedMemoryNoReAttach();
@@ -5082,27 +5035,20 @@ SubPostmasterMain(int argc, char *argv[])
 		shmemSetup(true);
 		AutoVacWorkerMain(argc - 2, argv + 2);	/* does not return */
 	}
-	if (strncmp(argv[1], "--forkbgworker=", 15) == 0)
+	if (strncmp(argv[1], "--forkbgworker=", BGWORKER_LEN) == 0)
 	{
 		int			shmem_slot;
 
 		/* do this as early as possible; in particular, before InitProcess() */
 		IsBackgroundWorker = true;
 
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
-		InitProcess();
-
-		/* Attach process to shared data structures */
-		CreateSharedMemoryAndSemaphores();
+		shmemSetup(false);
 
 		/* Fetch MyBgworkerEntry from shared memory */
-		shmem_slot = atoi(argv[1] + 15);
+		shmem_slot = atoi(argv[1] + BGWORKER_LEN);
 		MyBgworkerEntry = BackgroundWorkerEntry(shmem_slot);
 
-		StartBackgroundWorker();
+		BackgroundWorkerMain(argc, argv);
 	}
 	if (strcmp(argv[1], "--forkarch") == 0)
 	{
@@ -5377,7 +5323,7 @@ StartupPacketTimeoutHandler(void)
 /*
  * Generate a random cancel key.
  */
-static bool
+bool
 RandomCancelKey(int32 *cancel_key)
 {
 	return pg_strong_random(cancel_key, sizeof(int32));
@@ -5467,6 +5413,14 @@ StartChildProcess(ForkProcType type)
 			break;
 		case PgArchiverFork:
 			PrepPgArchiverFork(fork_data);
+		case BgWorkerFork:
+			if (PrepBgWorkerFork(fork_data))
+			{
+				/* We failed to assign a backendlist entry
+				 * so tell postmaster to try again later.
+				 */
+				return -1;
+			}
 			break;
 		case SysLoggerFork:
 			if (!Logging_collector)
@@ -5490,6 +5444,9 @@ StartChildProcess(ForkProcType type)
 	pid = fork_process();
 #endif
 
+	/* some processes like backends and bgworkers need the pid */
+	MyChildProcPid = pid;
+
 	if (pid == 0)				/* child */
 	{
 #ifndef EXEC_BACKEND
@@ -5498,8 +5455,19 @@ StartChildProcess(ForkProcType type)
 		/* Release postmaster's working memory context */
 		if (type != SysLoggerFork)
 		{
-		/* Close the postmaster's sockets */
-		ClosePostmasterPorts(false);
+			/* Close the postmaster's sockets */
+			ClosePostmasterPorts(false);
+			if (type == BgWorkerFork)
+			{
+				/*
+				 * Before blowing away PostmasterContext, save this bgworker's
+				 * data where it can find it.
+				 */
+				MyBgworkerEntry = (BackgroundWorker *)
+					MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker));
+				memcpy(MyBgworkerEntry, &(CurrentBgWorker->rw_worker), sizeof(BackgroundWorker));
+
+			}
 
 		MemoryContextSwitchTo(TopMemoryContext);
 		MemoryContextDelete(PostmasterContext);
@@ -5521,21 +5489,41 @@ StartChildProcess(ForkProcType type)
 	else if (pid < 0)
 	{
 		/* in parent, fork failed */
-		int save_errno = errno;
+		child_errno = errno;
 
-		errno =  save_errno;
-		ereport(LOG,
-			(errmsg("could not fork %s process: %m", fork_data->type_desc)));
+				ereport(LOG,
+				(errmsg("could not fork %s process: %m", fork_data->type_desc)));
 
-		/*
-		 * fork failure is fatal during startup, but there's no need to choke
-		 * immediately if starting other child types fails.
-		 */
-		if (MyForkProcType == StartupFork)
-			ExitPostmaster(1);
+		switch (MyForkProcType)
+		{
+			/*
+			 * fork failure is fatal during startup, but there's no need to choke
+			 * immediately if starting other child types fails.
+			 */
+			case StartupFork:
+				ExitPostmaster(1);
+				break;
+			case BgWorkerFork:
+				fork_data->fail_main(fork_data->ac, fork_data->av);
+				return -1;
+			default:
+				break;
+		}
 
 		return 0;
 	}
+	else /* parent */
+	{
+		switch (MyForkProcType)
+		{
+			case SysLoggerFork:
+			case BgWorkerFork:
+				fork_data->postmaster_main(fork_data->ac, fork_data->av);
+				break;
+			default:
+				break;
+		}
+	}
 
 	/*
 	 * in parent, successful fork
@@ -5770,123 +5758,6 @@ BackgroundWorkerUnblockSignals(void)
 	PG_SETMASK(&UnBlockSig);
 }
 
-#ifdef EXEC_BACKEND
-static pid_t
-bgworker_forkexec(int shmem_slot)
-{
-	char	   *av[10];
-	int			ac = 0;
-	char		forkav[MAXPGPATH];
-
-	snprintf(forkav, MAXPGPATH, "--forkbgworker=%d", shmem_slot);
-
-	av[ac++] = "postgres";
-	av[ac++] = forkav;
-	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
-	av[ac] = NULL;
-
-	Assert(ac < lengthof(av));
-
-	return postmaster_forkexec(ac, av);
-}
-#endif
-
-/*
- * Start a new bgworker.
- * Starting time conditions must have been checked already.
- *
- * Returns true on success, false on failure.
- * In either case, update the RegisteredBgWorker's state appropriately.
- *
- * This code is heavily based on autovacuum.c, q.v.
- */
-static bool
-do_start_bgworker(RegisteredBgWorker *rw)
-{
-	pid_t		worker_pid;
-
-	Assert(rw->rw_pid == 0);
-
-	/*
-	 * Allocate and assign the Backend element.  Note we must do this before
-	 * forking, so that we can handle out of memory properly.
-	 *
-	 * Treat failure as though the worker had crashed.  That way, the
-	 * postmaster will wait a bit before attempting to start it again; if it
-	 * tried again right away, most likely it'd find itself repeating the
-	 * out-of-memory or fork failure condition.
-	 */
-	if (!assign_backendlist_entry(rw))
-	{
-		rw->rw_crashed_at = GetCurrentTimestamp();
-		return false;
-	}
-
-	ereport(DEBUG1,
-			(errmsg("starting background worker process \"%s\"",
-					rw->rw_worker.bgw_name)));
-
-#ifdef EXEC_BACKEND
-	switch ((worker_pid = bgworker_forkexec(rw->rw_shmem_slot)))
-#else
-	switch ((worker_pid = fork_process()))
-#endif
-	{
-		case -1:
-			/* in postmaster, fork failed ... */
-			ereport(LOG,
-					(errmsg("could not fork worker process: %m")));
-			/* undo what assign_backendlist_entry did */
-			ReleasePostmasterChildSlot(rw->rw_child_slot);
-			rw->rw_child_slot = 0;
-			free(rw->rw_backend);
-			rw->rw_backend = NULL;
-			/* mark entry as crashed, so we'll try again later */
-			rw->rw_crashed_at = GetCurrentTimestamp();
-			break;
-
-#ifndef EXEC_BACKEND
-		case 0:
-			/* in postmaster child ... */
-			InitPostmasterChild();
-
-			/* Close the postmaster's sockets */
-			ClosePostmasterPorts(false);
-
-			/*
-			 * Before blowing away PostmasterContext, save this bgworker's
-			 * data where it can find it.
-			 */
-			MyBgworkerEntry = (BackgroundWorker *)
-				MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker));
-			memcpy(MyBgworkerEntry, &rw->rw_worker, sizeof(BackgroundWorker));
-
-			/* Release postmaster's working memory context */
-			MemoryContextSwitchTo(TopMemoryContext);
-			MemoryContextDelete(PostmasterContext);
-			PostmasterContext = NULL;
-
-			StartBackgroundWorker();
-
-			exit(1);			/* should not get here */
-			break;
-#endif
-		default:
-			/* in postmaster, fork successful ... */
-			rw->rw_pid = worker_pid;
-			rw->rw_backend->pid = rw->rw_pid;
-			ReportBackgroundWorkerPID(rw);
-			/* add new worker to lists of backends */
-			dlist_push_head(&BackendList, &rw->rw_backend->elem);
-#ifdef EXEC_BACKEND
-			ShmemBackendArrayAdd(rw->rw_backend);
-#endif
-			return true;
-	}
-
-	return false;
-}
-
 /*
  * Does the current postmaster state require starting a worker with the
  * specified start_time?
@@ -5927,54 +5798,6 @@ bgworker_should_start_now(BgWorkerStartTime start_time)
 	return false;
 }
 
-/*
- * Allocate the Backend struct for a connected background worker, but don't
- * add it to the list of backends just yet.
- *
- * On failure, return false without changing any worker state.
- *
- * Some info from the Backend is copied into the passed rw.
- */
-static bool
-assign_backendlist_entry(RegisteredBgWorker *rw)
-{
-	Backend    *bn;
-
-	/*
-	 * Compute the cancel key that will be assigned to this session. We
-	 * probably don't need cancel keys for background workers, but we'd better
-	 * have something random in the field to prevent unfriendly people from
-	 * sending cancels to them.
-	 */
-	if (!RandomCancelKey(&MyCancelKey))
-	{
-		ereport(LOG,
-				(errcode(ERRCODE_INTERNAL_ERROR),
-				 errmsg("could not generate random cancel key")));
-		return false;
-	}
-
-	bn = malloc(sizeof(Backend));
-	if (bn == NULL)
-	{
-		ereport(LOG,
-				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("out of memory")));
-		return false;
-	}
-
-	bn->cancel_key = MyCancelKey;
-	bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
-	bn->bkend_type = BACKEND_TYPE_BGWORKER;
-	bn->dead_end = false;
-	bn->bgworker_notify = false;
-
-	rw->rw_backend = bn;
-	rw->rw_child_slot = bn->child_slot;
-
-	return true;
-}
-
 /*
  * If the time is right, start background worker(s).
  *
@@ -6079,7 +5902,8 @@ maybe_start_bgworkers(void)
 			 * crashed, but there's no need because the next run of this
 			 * function will do that.
 			 */
-			if (!do_start_bgworker(rw))
+			CurrentBgWorker = rw;
+			if (do_start_bgworker() <= 0)
 			{
 				StartWorkerNeeded = true;
 				return;
@@ -6202,6 +6026,7 @@ save_backend_variables(BackendParameters *param, Port *port,
 	param->max_safe_fds = max_safe_fds;
 
 	param->MaxBackends = MaxBackends;
+	param->proc_type = MyForkProcType;
 
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
@@ -6437,6 +6262,7 @@ restore_backend_variables(BackendParameters *param, Port *port)
 	max_safe_fds = param->max_safe_fds;
 
 	MaxBackends = param->MaxBackends;
+	MyForkProcType = param->proc_type;
 
 #ifdef WIN32
 	PostmasterHandle = param->PostmasterHandle;
@@ -6472,7 +6298,7 @@ ShmemBackendArrayAllocation(void)
 	memset(ShmemBackendArray, 0, size);
 }
 
-static void
+void
 ShmemBackendArrayAdd(Backend *bn)
 {
 	/* The array slot corresponding to my PMChildSlot should be free */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index bc6e03fbc7..da9c4319b9 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -163,6 +163,7 @@ extern PGDLLIMPORT int max_worker_processes;
 extern PGDLLIMPORT int max_parallel_workers;
 
 extern PGDLLIMPORT int MyProcPid;
+extern PGDLLIMPORT int MyChildProcPid;
 extern PGDLLIMPORT pg_time_t MyStartTime;
 extern PGDLLIMPORT TimestampTz MyStartTimestamp;
 extern PGDLLIMPORT struct Port *MyProcPort;
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index a8864946cb..363e03aca4 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -41,6 +41,8 @@
 #ifndef BGWORKER_H
 #define BGWORKER_H
 
+#include "postmaster/fork_process.h"
+
 /*---------------------------------------------------------------------
  * External module API.
  *---------------------------------------------------------------------
@@ -158,4 +160,6 @@ extern void BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, ui
 extern void BackgroundWorkerBlockSignals(void);
 extern void BackgroundWorkerUnblockSignals(void);
 
+extern int PrepBgWorkerFork(ForkProcData *bgworker_fork);
+
 #endif							/* BGWORKER_H */
diff --git a/src/include/postmaster/bgworker_internals.h b/src/include/postmaster/bgworker_internals.h
index 0809e0af53..b6d86b503e 100644
--- a/src/include/postmaster/bgworker_internals.h
+++ b/src/include/postmaster/bgworker_internals.h
@@ -43,6 +43,7 @@ typedef struct RegisteredBgWorker
 } RegisteredBgWorker;
 
 extern slist_head BackgroundWorkerList;
+extern RegisteredBgWorker *CurrentBgWorker;
 
 extern Size BackgroundWorkerShmemSize(void);
 extern void BackgroundWorkerShmemInit(void);
@@ -54,7 +55,7 @@ extern void BackgroundWorkerStopNotifications(pid_t pid);
 extern void ResetBackgroundWorkerCrashTimes(void);
 
 /* Function to start a background worker, called from postmaster.c */
-extern void StartBackgroundWorker(void) pg_attribute_noreturn();
+extern void BackgroundWorkerMain(int argc, char *argv[]);
 
 #ifdef EXEC_BACKEND
 extern BackgroundWorker *BackgroundWorkerEntry(int slotno);
diff --git a/src/include/postmaster/fork_process.h b/src/include/postmaster/fork_process.h
index 32e10e3629..b5d5457ce7 100644
--- a/src/include/postmaster/fork_process.h
+++ b/src/include/postmaster/fork_process.h
@@ -29,6 +29,7 @@ typedef enum
    PgstatCollectorFork,
    PgArchiverFork,
    SysLoggerFork,
+   BgWorkerFork,
 
    NUMFORKPROCTYPES			/* Must be last! */
 } ForkProcType;
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index b692d8be11..9962bdd4be 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -13,6 +13,41 @@
 #ifndef _POSTMASTER_H
 #define _POSTMASTER_H
 
+#include "lib/ilist.h"
+
+/*
+ * List of active backends (or child processes anyway; we don't actually
+ * know whether a given child has become a backend or is still in the
+ * authorization phase).  This is used mainly to keep track of how many
+ * children we have and send them appropriate signals when necessary.
+ *
+ * "Special" children such as the startup, bgwriter and autovacuum launcher
+ * tasks are not in this list.  Autovacuum worker and walsender are in it.
+ * Also, "dead_end" children are in it: these are children launched just for
+ * the purpose of sending a friendly rejection message to a would-be client.
+ * We must track them because they are attached to shared memory, but we know
+ * they will never become live backends.  dead_end children are not assigned a
+ * PMChildSlot.
+ *
+ * Background workers are in this list, too.
+ */
+typedef struct bkend
+{
+	pid_t		pid;			/* process id of backend */
+	int32		cancel_key;		/* cancel key for cancels for this backend */
+	int			child_slot;		/* PMChildSlot for this backend, if any */
+
+	/*
+	 * Flavor of backend or auxiliary process.  Note that BACKEND_TYPE_WALSND
+	 * backends initially announce themselves as BACKEND_TYPE_NORMAL, so if
+	 * bkend_type is normal, you should check for a recent transition.
+	 */
+	int			bkend_type;
+	bool		dead_end;		/* is it going to send an error and quit? */
+	bool		bgworker_notify;	/* gets bgworker start/stop notifications */
+	dlist_node	elem;			/* list link in BackendList */
+} Backend;
+
 /* GUC options */
 extern bool EnableSSL;
 extern int	ReservedBackends;
@@ -54,14 +89,19 @@ extern int	MaxLivePostmasterChildren(void);
 
 extern bool PostmasterMarkPIDForWorkerNotify(int);
 
+extern bool RandomCancelKey(int32 *cancel_key);
+
 #ifdef EXEC_BACKEND
 extern pid_t postmaster_forkexec(int argc, char *argv[]);
 extern void SubPostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
 
 extern Size ShmemBackendArraySize(void);
 extern void ShmemBackendArrayAllocation(void);
+extern void ShmemBackendArrayAdd(Backend *bn);
 #endif
 
+extern PGDLLIMPORT dlist_head BackendList;
+
 /*
  * Note: MAX_BACKENDS is limited to 2^18-1 because that's the width reserved
  * for buffer references in buf_internals.h.  This limitation could be lifted
@@ -74,4 +114,17 @@ extern void ShmemBackendArrayAllocation(void);
  */
 #define MAX_BACKENDS	0x3FFFF
 
+/*
+ * Possible types of a backend. Beyond being the possible bkend_type values in
+ * struct bkend, these are OR-able request flag bits for SignalSomeChildren()
+ * and CountChildren().
+ */
+#define BACKEND_TYPE_NORMAL		0x0001	/* normal backend */
+#define BACKEND_TYPE_AUTOVAC	0x0002	/* autovacuum worker process */
+#define BACKEND_TYPE_WALSND		0x0004	/* walsender process */
+#define BACKEND_TYPE_BGWORKER	0x0008	/* bgworker process */
+#define BACKEND_TYPE_ALL		0x000F	/* OR of all the above */
+
+#define BACKEND_TYPE_WORKER		(BACKEND_TYPE_AUTOVAC | BACKEND_TYPE_BGWORKER)
+
 #endif							/* _POSTMASTER_H */
-- 
2.23.0

