*** src/backend/access/transam/varsup.c	058cb2f268e956e37724f1d05d98946d1cc56ab4
--- src/backend/access/transam/varsup.c	5e3252795acfc8e04994336e4b432bdd0bc13238
*************** GetNewTransactionId(bool isSubXact)
*** 102,108 ****
  		if (IsUnderPostmaster && (xid % 65536) == 0)
  		{
  			msg = IMessageCreate(IMSGT_FORCE_VACUUM, 0);
! 			IMessageActivate(msg, GetAutovacuumLauncherId());
  		}
  
  		if (IsUnderPostmaster &&
--- 102,108 ----
  		if (IsUnderPostmaster && (xid % 65536) == 0)
  		{
  			msg = IMessageCreate(IMSGT_FORCE_VACUUM, 0);
! 			IMessageActivate(msg, GetCoordinatorId());
  		}
  
  		if (IsUnderPostmaster &&
*************** SetTransactionIdLimit(TransactionId olde
*** 348,354 ****
  		IsUnderPostmaster && !InRecovery)
  	{
  		msg = IMessageCreate(IMSGT_FORCE_VACUUM, 0);
! 		IMessageActivate(msg, GetAutovacuumLauncherId());
  	}
  
  	/* Give an immediate warning if past the wrap warn point */
--- 348,354 ----
  		IsUnderPostmaster && !InRecovery)
  	{
  		msg = IMessageCreate(IMSGT_FORCE_VACUUM, 0);
! 		IMessageActivate(msg, GetCoordinatorId());
  	}
  
  	/* Give an immediate warning if past the wrap warn point */
============================================================
*** src/backend/commands/vacuum.c	50cc1c597a82a6eb85c4ffdf83e07a0ede873646
--- src/backend/commands/vacuum.c	5cad7d65313d1dd67a70016906a2ee6d88043022
*************** vacuum(VacuumStmt *vacstmt, Oid relid, b
*** 126,132 ****
  	 * Send info about dead objects to the statistics collector, unless we are
  	 * in autovacuum --- autovacuum.c does this for itself.
  	 */
! 	if ((vacstmt->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
  		pgstat_vacuum_stat();
  
  	/*
--- 126,132 ----
  	 * Send info about dead objects to the statistics collector, unless we are
  	 * in autovacuum --- autovacuum.c does this for itself.
  	 */
! 	if ((vacstmt->options & VACOPT_VACUUM) && !IsBackgroundWorkerProcess())
  		pgstat_vacuum_stat();
  
  	/*
*************** vacuum(VacuumStmt *vacstmt, Oid relid, b
*** 182,188 ****
  	else
  	{
  		Assert(vacstmt->options & VACOPT_ANALYZE);
! 		if (IsAutoVacuumWorkerProcess())
  			use_own_xacts = true;
  		else if (in_outer_xact)
  			use_own_xacts = false;
--- 182,188 ----
  	else
  	{
  		Assert(vacstmt->options & VACOPT_ANALYZE);
! 		if (IsBackgroundWorkerProcess())
  			use_own_xacts = true;
  		else if (in_outer_xact)
  			use_own_xacts = false;
*************** vacuum(VacuumStmt *vacstmt, Oid relid, b
*** 278,284 ****
  		StartTransactionCommand();
  	}
  
! 	if ((vacstmt->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
  	{
  		/*
  		 * Update pg_database.datfrozenxid, and truncate pg_clog if possible.
--- 278,284 ----
  		StartTransactionCommand();
  	}
  
! 	if ((vacstmt->options & VACOPT_VACUUM) && !IsBackgroundWorkerProcess())
  	{
  		/*
  		 * Update pg_database.datfrozenxid, and truncate pg_clog if possible.
============================================================
*** src/backend/postmaster/postmaster.c	e6a455be46eee967cafa76cef125cedef4ba986f
--- src/backend/postmaster/postmaster.c	7c8ba1424aa374a2583c65fd912f563e94ab0a73
***************
*** 131,137 ****
   * 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 processes 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
--- 131,137 ----
   * 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 the coordinator
   * tasks are not in this list.	Autovacuum worker and walsender processes 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
*************** typedef struct bkend
*** 144,150 ****
  	pid_t		pid;			/* process id of backend */
  	long		cancel_key;		/* cancel key for cancels for this backend */
  	int			child_slot;		/* PMChildSlot for this backend, if any */
! 	bool		is_autovacuum;	/* is it an autovacuum process? */
  	bool		dead_end;		/* is it going to send an error and quit? */
  	Dlelem		elem;			/* list link in BackendList */
  } Backend;
--- 144,150 ----
  	pid_t		pid;			/* process id of backend */
  	long		cancel_key;		/* cancel key for cancels for this backend */
  	int			child_slot;		/* PMChildSlot for this backend, if any */
! 	bool		is_bgworker;	/* is it a background worker? */
  	bool		dead_end;		/* is it going to send an error and quit? */
  	Dlelem		elem;			/* list link in BackendList */
  } Backend;
*************** static pid_t StartupPID = 0,
*** 209,215 ****
  			BgWriterPID = 0,
  			WalWriterPID = 0,
  			WalReceiverPID = 0,
! 			AutoVacPID = 0,
  			PgArchPID = 0,
  			PgStatPID = 0,
  			SysLoggerPID = 0;
--- 209,215 ----
  			BgWriterPID = 0,
  			WalWriterPID = 0,
  			WalReceiverPID = 0,
! 			CoordinatorPID = 0,
  			PgArchPID = 0,
  			PgStatPID = 0,
  			SysLoggerPID = 0;
*************** bool		redirection_done = false;	/* stder
*** 296,302 ****
  
  
  /* the launcher needs to be signalled to communicate some condition */
! static volatile bool avlauncher_needs_signal = false;
  
  /*
   * State for assigning random salts and cancel keys.
--- 296,302 ----
  
  
  /* the launcher needs to be signalled to communicate some condition */
! static volatile bool coordinator_needs_signal = false;
  
  /*
   * State for assigning random salts and cancel keys.
*************** static bool SignalSomeChildren(int signa
*** 354,360 ****
  static bool SignalSomeChildren(int signal, int targets);
  
  #define SignalChildren(sig)			   SignalSomeChildren(sig, BACKEND_TYPE_ALL)
! #define SignalAutovacWorkers(sig)  SignalSomeChildren(sig, BACKEND_TYPE_AUTOVAC)
  
  /*
   * Possible types of a backend. These are OR-able request flag bits
--- 354,360 ----
  static bool SignalSomeChildren(int signal, int targets);
  
  #define SignalChildren(sig)			   SignalSomeChildren(sig, BACKEND_TYPE_ALL)
! #define SignalBackgroundWorkers(sig)  SignalSomeChildren(sig, BACKEND_TYPE_AUTOVAC)
  
  /*
   * Possible types of a backend. These are OR-able request flag bits
*************** static pid_t StartChildProcess(AuxProcTy
*** 368,374 ****
  static int	CountChildren(int target);
  static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
  static pid_t StartChildProcess(AuxProcType type);
! static void StartAutovacuumWorker(void);
  
  #ifdef EXEC_BACKEND
  
--- 368,374 ----
  static int	CountChildren(int target);
  static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
  static pid_t StartChildProcess(AuxProcType type);
! static void StartBgWorker(void);
  
  #ifdef EXEC_BACKEND
  
*************** ServerLoop(void)
*** 1460,1468 ****
  		if (WalWriterPID == 0 && pmState == PM_RUN)
  			WalWriterPID = StartWalWriter();
  
! 		/* If we have lost the autovacuum launcher, try to start a new one */
! 		if (AutoVacPID == 0 && pmState == PM_RUN)
! 			AutoVacPID = StartAutoVacLauncher();
  
  		/* If we have lost the archiver, try to start a new one */
  		if (XLogArchivingActive() && PgArchPID == 0 && pmState == PM_RUN)
--- 1460,1468 ----
  		if (WalWriterPID == 0 && pmState == PM_RUN)
  			WalWriterPID = StartWalWriter();
  
! 		/* If we have lost the coordinator, try to start a new one */
! 		if (CoordinatorPID == 0 && pmState == PM_RUN)
! 			CoordinatorPID = StartCoordinator();
  
  		/* If we have lost the archiver, try to start a new one */
  		if (XLogArchivingActive() && PgArchPID == 0 && pmState == PM_RUN)
*************** ServerLoop(void)
*** 1472,1483 ****
  		if (PgStatPID == 0 && pmState == PM_RUN)
  			PgStatPID = pgstat_start();
  
! 		/* If we need to signal the autovacuum launcher, do so now */
! 		if (avlauncher_needs_signal)
  		{
! 			avlauncher_needs_signal = false;
! 			if (AutoVacPID != 0)
! 				kill(AutoVacPID, SIGUSR2);
  		}
  
  		/*
--- 1472,1483 ----
  		if (PgStatPID == 0 && pmState == PM_RUN)
  			PgStatPID = pgstat_start();
  
! 		/* if we need to signal the coordinator, do so now */
! 		if (coordinator_needs_signal)
  		{
! 			coordinator_needs_signal = false;
! 			if (CoordinatorPID != 0)
! 				kill(CoordinatorPID, SIGUSR2);
  		}
  
  		/*
*************** SIGHUP_handler(SIGNAL_ARGS)
*** 2104,2111 ****
  			signal_child(WalWriterPID, SIGHUP);
  		if (WalReceiverPID != 0)
  			signal_child(WalReceiverPID, SIGHUP);
! 		if (AutoVacPID != 0)
! 			signal_child(AutoVacPID, SIGHUP);
  		if (PgArchPID != 0)
  			signal_child(PgArchPID, SIGHUP);
  		if (SysLoggerPID != 0)
--- 2104,2111 ----
  			signal_child(WalWriterPID, SIGHUP);
  		if (WalReceiverPID != 0)
  			signal_child(WalReceiverPID, SIGHUP);
! 		if (CoordinatorPID != 0)
! 			signal_child(CoordinatorPID, SIGHUP);
  		if (PgArchPID != 0)
  			signal_child(PgArchPID, SIGHUP);
  		if (SysLoggerPID != 0)
*************** pmdie(SIGNAL_ARGS)
*** 2164,2174 ****
  			if (pmState == PM_RUN || pmState == PM_RECOVERY ||
  				pmState == PM_HOT_STANDBY || pmState == PM_STARTUP)
  			{
! 				/* autovacuum workers are told to shut down immediately */
! 				SignalAutovacWorkers(SIGTERM);
! 				/* and the autovac launcher too */
! 				if (AutoVacPID != 0)
! 					signal_child(AutoVacPID, SIGTERM);
  				/* and the walwriter too */
  				if (WalWriterPID != 0)
  					signal_child(WalWriterPID, SIGTERM);
--- 2164,2174 ----
  			if (pmState == PM_RUN || pmState == PM_RECOVERY ||
  				pmState == PM_HOT_STANDBY || pmState == PM_STARTUP)
  			{
! 				/* background workers are told to shut down immediately */
! 				SignalBackgroundWorkers(SIGTERM);
! 				/* and the coordinator too */
! 				if (CoordinatorPID != 0)
! 					signal_child(CoordinatorPID, SIGTERM);
  				/* and the walwriter too */
  				if (WalWriterPID != 0)
  					signal_child(WalWriterPID, SIGTERM);
*************** pmdie(SIGNAL_ARGS)
*** 2225,2236 ****
  			{
  				ereport(LOG,
  						(errmsg("aborting any active transactions")));
! 				/* shut down all backends and autovac workers */
  				SignalSomeChildren(SIGTERM,
  								 BACKEND_TYPE_NORMAL | BACKEND_TYPE_AUTOVAC);
! 				/* and the autovac launcher too */
! 				if (AutoVacPID != 0)
! 					signal_child(AutoVacPID, SIGTERM);
  				/* and the walwriter too */
  				if (WalWriterPID != 0)
  					signal_child(WalWriterPID, SIGTERM);
--- 2225,2236 ----
  			{
  				ereport(LOG,
  						(errmsg("aborting any active transactions")));
! 				/* shut down all backends and background workers */
  				SignalSomeChildren(SIGTERM,
  								 BACKEND_TYPE_NORMAL | BACKEND_TYPE_AUTOVAC);
! 				/* and the coordinator too */
! 				if (CoordinatorPID != 0)
! 					signal_child(CoordinatorPID, SIGTERM);
  				/* and the walwriter too */
  				if (WalWriterPID != 0)
  					signal_child(WalWriterPID, SIGTERM);
*************** pmdie(SIGNAL_ARGS)
*** 2263,2270 ****
  				signal_child(WalWriterPID, SIGQUIT);
  			if (WalReceiverPID != 0)
  				signal_child(WalReceiverPID, SIGQUIT);
! 			if (AutoVacPID != 0)
! 				signal_child(AutoVacPID, SIGQUIT);
  			if (PgArchPID != 0)
  				signal_child(PgArchPID, SIGQUIT);
  			if (PgStatPID != 0)
--- 2263,2270 ----
  				signal_child(WalWriterPID, SIGQUIT);
  			if (WalReceiverPID != 0)
  				signal_child(WalReceiverPID, SIGQUIT);
! 			if (CoordinatorPID != 0)
! 				signal_child(CoordinatorPID, SIGQUIT);
  			if (PgArchPID != 0)
  				signal_child(PgArchPID, SIGQUIT);
  			if (PgStatPID != 0)
*************** reaper(SIGNAL_ARGS)
*** 2382,2389 ****
  			 */
  			if (WalWriterPID == 0)
  				WalWriterPID = StartWalWriter();
! 			if (AutoVacPID == 0)
! 				AutoVacPID = StartAutoVacLauncher();
  			if (XLogArchivingActive() && PgArchPID == 0)
  				PgArchPID = pgarch_start();
  			if (PgStatPID == 0)
--- 2382,2389 ----
  			 */
  			if (WalWriterPID == 0)
  				WalWriterPID = StartWalWriter();
! 			if (CoordinatorPID == 0)
! 				CoordinatorPID = StartCoordinator();
  			if (XLogArchivingActive() && PgArchPID == 0)
  				PgArchPID = pgarch_start();
  			if (PgStatPID == 0)
*************** reaper(SIGNAL_ARGS)
*** 2481,2497 ****
  		}
  
  		/*
! 		 * Was it the autovacuum launcher?	Normal exit can be ignored; we'll
! 		 * start a new one at the next iteration of the postmaster's main
  		 * loop, if necessary.	Any other exit condition is treated as a
  		 * crash.
  		 */
! 		if (pid == AutoVacPID)
  		{
! 			AutoVacPID = 0;
  			if (!EXIT_STATUS_0(exitstatus))
  				HandleChildCrash(pid, exitstatus,
! 								 _("autovacuum launcher process"));
  			continue;
  		}
  
--- 2481,2497 ----
  		}
  
  		/*
! 		 * Was it the coordinator?  Normal exit can be ignored; we'll start
! 		 * a new coordinator at the next iteration of the postmaster's main
  		 * loop, if necessary.	Any other exit condition is treated as a
  		 * crash.
  		 */
! 		if (pid == CoordinatorPID)
  		{
! 			CoordinatorPID = 0;
  			if (!EXIT_STATUS_0(exitstatus))
  				HandleChildCrash(pid, exitstatus,
! 								 _("coordinator process"));
  			continue;
  		}
  
*************** CleanupBackend(int pid,
*** 2615,2621 ****
  
  /*
   * HandleChildCrash -- cleanup after failed backend, bgwriter, walwriter,
!  * or autovacuum.
   *
   * The objectives here are to clean up our local state about the child
   * process, and to signal all other remaining children to quickdie.
--- 2615,2621 ----
  
  /*
   * HandleChildCrash -- cleanup after failed backend, bgwriter, walwriter,
!  * or coordinator.
   *
   * The objectives here are to clean up our local state about the child
   * process, and to signal all other remaining children to quickdie.
*************** HandleChildCrash(int pid, int exitstatus
*** 2732,2747 ****
  		signal_child(WalReceiverPID, (SendStop ? SIGSTOP : SIGQUIT));
  	}
  
! 	/* Take care of the autovacuum launcher too */
! 	if (pid == AutoVacPID)
! 		AutoVacPID = 0;
! 	else if (AutoVacPID != 0 && !FatalError)
  	{
  		ereport(DEBUG2,
  				(errmsg_internal("sending %s to process %d",
  								 (SendStop ? "SIGSTOP" : "SIGQUIT"),
! 								 (int) AutoVacPID)));
! 		signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT));
  	}
  
  	/*
--- 2732,2747 ----
  		signal_child(WalReceiverPID, (SendStop ? SIGSTOP : SIGQUIT));
  	}
  
! 	/* Take care of the coordinator too */
! 	if (pid == CoordinatorPID)
! 		CoordinatorPID = 0;
! 	else if (CoordinatorPID != 0 && !FatalError)
  	{
  		ereport(DEBUG2,
  				(errmsg_internal("sending %s to process %d",
  								 (SendStop ? "SIGSTOP" : "SIGQUIT"),
! 								 (int) CoordinatorPID)));
! 		signal_child(CoordinatorPID, (SendStop ? SIGSTOP : SIGQUIT));
  	}
  
  	/*
*************** PostmasterStateMachine(void)
*** 2887,2893 ****
  	{
  		/*
  		 * PM_WAIT_BACKENDS state ends when we have no regular backends
! 		 * (including autovac workers) and no walwriter or autovac launcher.
  		 * If we are doing crash recovery then we expect the bgwriter to exit
  		 * too, otherwise not.	The archiver, stats, and syslogger processes
  		 * are disregarded since they are not connected to shared memory; we
--- 2887,2893 ----
  	{
  		/*
  		 * PM_WAIT_BACKENDS state ends when we have no regular backends
! 		 * (including background workers) and no walwriter or coordinator.
  		 * If we are doing crash recovery then we expect the bgwriter to exit
  		 * too, otherwise not.	The archiver, stats, and syslogger processes
  		 * are disregarded since they are not connected to shared memory; we
*************** PostmasterStateMachine(void)
*** 2900,2906 ****
  			WalReceiverPID == 0 &&
  			(BgWriterPID == 0 || !FatalError) &&
  			WalWriterPID == 0 &&
! 			AutoVacPID == 0)
  		{
  			if (FatalError)
  			{
--- 2900,2906 ----
  			WalReceiverPID == 0 &&
  			(BgWriterPID == 0 || !FatalError) &&
  			WalWriterPID == 0 &&
! 			CoordinatorPID == 0)
  		{
  			if (FatalError)
  			{
*************** PostmasterStateMachine(void)
*** 2995,3001 ****
  			Assert(WalReceiverPID == 0);
  			Assert(BgWriterPID == 0);
  			Assert(WalWriterPID == 0);
! 			Assert(AutoVacPID == 0);
  			/* syslogger is not considered here */
  			pmState = PM_NO_CHILDREN;
  		}
--- 2995,3001 ----
  			Assert(WalReceiverPID == 0);
  			Assert(BgWriterPID == 0);
  			Assert(WalWriterPID == 0);
! 			Assert(CoordinatorPID == 0);
  			/* syslogger is not considered here */
  			pmState = PM_NO_CHILDREN;
  		}
*************** SignalSomeChildren(int signal, int targe
*** 3120,3128 ****
  
  		if (bp->dead_end)
  			continue;
! 		if (!(target & BACKEND_TYPE_NORMAL) && !bp->is_autovacuum)
  			continue;
! 		if (!(target & BACKEND_TYPE_AUTOVAC) && bp->is_autovacuum)
  			continue;
  		if (!(target & BACKEND_TYPE_WALSND) &&
  			IsPostmasterChildWalSender(bp->child_slot))
--- 3120,3128 ----
  
  		if (bp->dead_end)
  			continue;
! 		if (!(target & BACKEND_TYPE_NORMAL) && !bp->is_bgworker)
  			continue;
! 		if (!(target & BACKEND_TYPE_AUTOVAC) && bp->is_bgworker)
  			continue;
  		if (!(target & BACKEND_TYPE_WALSND) &&
  			IsPostmasterChildWalSender(bp->child_slot))
*************** SignalSomeChildren(int signal, int targe
*** 3142,3148 ****
   *
   * returns: STATUS_ERROR if the fork failed, STATUS_OK otherwise.
   *
!  * Note: if you change this code, also consider StartAutovacuumWorker.
   */
  static int
  BackendStartup(Port *port)
--- 3142,3148 ----
   *
   * returns: STATUS_ERROR if the fork failed, STATUS_OK otherwise.
   *
!  * Note: if you change this code, also consider StartBackgroundWorker.
   */
  static int
  BackendStartup(Port *port)
*************** BackendStartup(Port *port)
*** 3242,3248 ****
  	 * of backends.
  	 */
  	bn->pid = pid;
! 	bn->is_autovacuum = false;
  	DLInitElem(&bn->elem, bn);
  	DLAddHead(BackendList, &bn->elem);
  #ifdef EXEC_BACKEND
--- 3242,3248 ----
  	 * of backends.
  	 */
  	bn->pid = pid;
! 	bn->is_bgworker = false;
  	DLInitElem(&bn->elem, bn);
  	DLAddHead(BackendList, &bn->elem);
  #ifdef EXEC_BACKEND
*************** SubPostmasterMain(int argc, char *argv[]
*** 3931,3946 ****
  	 * same address the postmaster used.
  	 */
  	if (strcmp(argv[1], "--forkbackend") == 0 ||
! 		strcmp(argv[1], "--forkavlauncher") == 0 ||
! 		strcmp(argv[1], "--forkavworker") == 0 ||
  		strcmp(argv[1], "--forkboot") == 0)
  		PGSharedMemoryReAttach();
  
! 	/* autovacuum needs this set before calling InitProcess */
! 	if (strcmp(argv[1], "--forkavlauncher") == 0)
! 		AutovacuumLauncherIAm();
! 	if (strcmp(argv[1], "--forkavworker") == 0)
! 		AutovacuumWorkerIAm();
  
  	/*
  	 * Start our win32 signal implementation. This has to be done after we
--- 3931,3949 ----
  	 * same address the postmaster used.
  	 */
  	if (strcmp(argv[1], "--forkbackend") == 0 ||
! 		strcmp(argv[1], "--forkcoordinator") == 0 ||
! 		strcmp(argv[1], "--forkbgworker") == 0 ||
  		strcmp(argv[1], "--forkboot") == 0)
  		PGSharedMemoryReAttach();
  
! 	/*
! 	 * Background workers and the coordinator need this set before calling
! 	 * InitProcess.
! 	 */
! 	if (strcmp(argv[1], "--forkcoordinator") == 0)
! 		CoordinatorIAm();
! 	if (strcmp(argv[1], "--forkbgworker") == 0)
! 		BackgroundWorkerIAm();
  
  	/*
  	 * Start our win32 signal implementation. This has to be done after we
*************** SubPostmasterMain(int argc, char *argv[]
*** 4037,4043 ****
  		AuxiliaryProcessMain(argc - 2, argv + 2);
  		proc_exit(0);
  	}
! 	if (strcmp(argv[1], "--forkavlauncher") == 0)
  	{
  		/* Close the postmaster's sockets */
  		ClosePostmasterPorts(false);
--- 4040,4046 ----
  		AuxiliaryProcessMain(argc - 2, argv + 2);
  		proc_exit(0);
  	}
! 	if (strcmp(argv[1], "--forkcoordinator") == 0)
  	{
  		/* Close the postmaster's sockets */
  		ClosePostmasterPorts(false);
*************** SubPostmasterMain(int argc, char *argv[]
*** 4051,4060 ****
  		/* Attach process to shared data structures */
  		CreateSharedMemoryAndSemaphores(false, 0);
  
! 		AutoVacLauncherMain(argc - 2, argv + 2);
  		proc_exit(0);
  	}
! 	if (strcmp(argv[1], "--forkavworker") == 0)
  	{
  		/* Close the postmaster's sockets */
  		ClosePostmasterPorts(false);
--- 4054,4063 ----
  		/* Attach process to shared data structures */
  		CreateSharedMemoryAndSemaphores(false, 0);
  
! 		CoordinatorMain(argc - 2, argv + 2);
  		proc_exit(0);
  	}
! 	if (strcmp(argv[1], "--forkbgworker") == 0)
  	{
  		/* Close the postmaster's sockets */
  		ClosePostmasterPorts(false);
*************** SubPostmasterMain(int argc, char *argv[]
*** 4068,4074 ****
  		/* Attach process to shared data structures */
  		CreateSharedMemoryAndSemaphores(false, 0);
  
! 		AutoVacWorkerMain(argc - 2, argv + 2);
  		proc_exit(0);
  	}
  	if (strcmp(argv[1], "--forkarch") == 0)
--- 4071,4077 ----
  		/* Attach process to shared data structures */
  		CreateSharedMemoryAndSemaphores(false, 0);
  
! 		BackgroundWorkerMain(argc - 2, argv + 2);
  		proc_exit(0);
  	}
  	if (strcmp(argv[1], "--forkarch") == 0)
*************** sigusr1_handler(SIGNAL_ARGS)
*** 4190,4199 ****
  		signal_child(SysLoggerPID, SIGUSR1);
  	}
  
! 	if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER))
  	{
! 		/* The autovacuum launcher wants us to start a worker process. */
! 		StartAutovacuumWorker();
  	}
  
  	if (CheckPostmasterSignal(PMSIGNAL_START_WALRECEIVER) &&
--- 4193,4202 ----
  		signal_child(SysLoggerPID, SIGUSR1);
  	}
  
! 	if (CheckPostmasterSignal(PMSIGNAL_START_BGWORKER))
  	{
! 		/* The coordinator wants us to start a worker process. */
! 		StartBgWorker();
  	}
  
  	if (CheckPostmasterSignal(PMSIGNAL_START_WALRECEIVER) &&
*************** CountChildren(int target)
*** 4313,4321 ****
  
  		if (bp->dead_end)
  			continue;
! 		if (!(target & BACKEND_TYPE_NORMAL) && !bp->is_autovacuum)
  			continue;
! 		if (!(target & BACKEND_TYPE_AUTOVAC) && bp->is_autovacuum)
  			continue;
  		if (!(target & BACKEND_TYPE_WALSND) &&
  			IsPostmasterChildWalSender(bp->child_slot))
--- 4316,4324 ----
  
  		if (bp->dead_end)
  			continue;
! 		if (!(target & BACKEND_TYPE_NORMAL) && !bp->is_bgworker)
  			continue;
! 		if (!(target & BACKEND_TYPE_AUTOVAC) && bp->is_bgworker)
  			continue;
  		if (!(target & BACKEND_TYPE_WALSND) &&
  			IsPostmasterChildWalSender(bp->child_slot))
*************** StartChildProcess(AuxProcType type)
*** 4431,4438 ****
  }
  
  /*
!  * StartAutovacuumWorker
!  *		Start an autovac worker process.
   *
   * This function is here because it enters the resulting PID into the
   * postmaster's private backends list.
--- 4434,4441 ----
  }
  
  /*
!  * StartBgWorker
!  *		Start a background worker process.
   *
   * This function is here because it enters the resulting PID into the
   * postmaster's private backends list.
*************** static void
*** 4440,4453 ****
   * NB -- this code very roughly matches BackendStartup.
   */
  static void
! StartAutovacuumWorker(void)
  {
  	Backend    *bn;
  
  	/*
  	 * If not in condition to run a process, don't try, but handle it like a
  	 * fork failure.  This does not normally happen, since the signal is only
! 	 * supposed to be sent by autovacuum launcher when it's OK to do it, but
  	 * we have to check to avoid race-condition problems during DB state
  	 * changes.
  	 */
--- 4443,4456 ----
   * NB -- this code very roughly matches BackendStartup.
   */
  static void
! StartBgWorker(void)
  {
  	Backend    *bn;
  
  	/*
  	 * If not in condition to run a process, don't try, but handle it like a
  	 * fork failure.  This does not normally happen, since the signal is only
! 	 * supposed to be sent by the coordinator when it's OK to do it, but
  	 * we have to check to avoid race-condition problems during DB state
  	 * changes.
  	 */
*************** StartAutovacuumWorker(void)
*** 4458,4478 ****
  		{
  			/*
  			 * Compute the cancel key that will be assigned to this session.
! 			 * We probably don't need cancel keys for autovac workers, but
  			 * we'd better have something random in the field to prevent
  			 * unfriendly people from sending cancels to them.
  			 */
  			MyCancelKey = PostmasterRandom();
  			bn->cancel_key = MyCancelKey;
  
! 			/* Autovac workers are not dead_end and need a child slot */
  			bn->dead_end = false;
  			bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
  
! 			bn->pid = StartAutoVacWorker();
  			if (bn->pid > 0)
  			{
! 				bn->is_autovacuum = true;
  				DLInitElem(&bn->elem, bn);
  				DLAddHead(BackendList, &bn->elem);
  #ifdef EXEC_BACKEND
--- 4461,4481 ----
  		{
  			/*
  			 * 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.
  			 */
  			MyCancelKey = PostmasterRandom();
  			bn->cancel_key = MyCancelKey;
  
! 			/* Background workers are not dead_end and need a child slot */
  			bn->dead_end = false;
  			bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
  
! 			bn->pid = StartBackgroundWorker();
  			if (bn->pid > 0)
  			{
! 				bn->is_bgworker = true;
  				DLInitElem(&bn->elem, bn);
  				DLAddHead(BackendList, &bn->elem);
  #ifdef EXEC_BACKEND
*************** StartAutovacuumWorker(void)
*** 4484,4490 ****
  
  			/*
  			 * fork failed, fall through to report -- actual error message was
! 			 * logged by StartAutoVacWorker
  			 */
  			(void) ReleasePostmasterChildSlot(bn->child_slot);
  			free(bn);
--- 4487,4493 ----
  
  			/*
  			 * fork failed, fall through to report -- actual error message was
! 			 * logged by StartBackgroundWorker
  			 */
  			(void) ReleasePostmasterChildSlot(bn->child_slot);
  			free(bn);
*************** StartAutovacuumWorker(void)
*** 4501,4508 ****
  	 * avoid a ping-pong signalling in quick succession between coordinator
  	 * and postmaster in case things get ugly.
  	 */
! 	if (AutoVacPID != 0)
! 		avlauncher_needs_signal = true;
  }
  
  /*
--- 4504,4511 ----
  	 * avoid a ping-pong signalling in quick succession between coordinator
  	 * and postmaster in case things get ugly.
  	 */
! 	if (CoordinatorPID != 0)
! 		coordinator_needs_signal = true;
  }
  
  /*
============================================================
*** src/backend/storage/ipc/ipc.c	304a8cf3658db251480c2b4517206c21b5bac40d
--- src/backend/storage/ipc/ipc.c	af20d2d3315ef31aa41441a3e77b8b975ee1a890
*************** proc_exit(int code)
*** 121,127 ****
  		 */
  		char		gprofDirName[32];
  
! 		if (IsAutoVacuumWorkerProcess())
  			snprintf(gprofDirName, 32, "gprof/avworker");
  		else
  			snprintf(gprofDirName, 32, "gprof/%d", (int) getpid());
--- 121,127 ----
  		 */
  		char		gprofDirName[32];
  
! 		if (IsBackgroundWorkerProcess())
  			snprintf(gprofDirName, 32, "gprof/avworker");
  		else
  			snprintf(gprofDirName, 32, "gprof/%d", (int) getpid());
============================================================
*** src/backend/storage/ipc/ipci.c	1e5026e411d3728825b8e18dc01a9a0ec9ce974d
--- src/backend/storage/ipc/ipci.c	7e32569fd32941d7c26800f3077b0ff94e210f9e
*************** CreateSharedMemoryAndSemaphores(bool mak
*** 120,126 ****
  		size = add_size(size, PMSignalShmemSize());
  		size = add_size(size, ProcSignalShmemSize());
  		size = add_size(size, BgWriterShmemSize());
! 		size = add_size(size, AutoVacuumShmemSize());
  		size = add_size(size, WalSndShmemSize());
  		size = add_size(size, WalRcvShmemSize());
  		size = add_size(size, BTreeShmemSize());
--- 120,126 ----
  		size = add_size(size, PMSignalShmemSize());
  		size = add_size(size, ProcSignalShmemSize());
  		size = add_size(size, BgWriterShmemSize());
! 		size = add_size(size, CoordinatorShmemSize());
  		size = add_size(size, WalSndShmemSize());
  		size = add_size(size, WalRcvShmemSize());
  		size = add_size(size, BTreeShmemSize());
*************** CreateSharedMemoryAndSemaphores(bool mak
*** 226,232 ****
  	PMSignalShmemInit();
  	ProcSignalShmemInit();
  	BgWriterShmemInit();
! 	AutoVacuumShmemInit();
  	WalSndShmemInit();
  	WalRcvShmemInit();
  
--- 226,232 ----
  	PMSignalShmemInit();
  	ProcSignalShmemInit();
  	BgWriterShmemInit();
! 	CoordinatorShmemInit();
  	WalSndShmemInit();
  	WalRcvShmemInit();
  
============================================================
*** src/backend/storage/lmgr/proc.c	bbd84c0cdc85587bd4e04f1b69ead6ea11bb08dd
--- src/backend/storage/lmgr/proc.c	227fffe14ecf0833f313fc7eba58a45e968210fe
*************** ProcGlobalSemas(void)
*** 143,149 ****
   *	  running out when trying to start another backend is a common failure.
   *	  So, now we grab enough semaphores to support the desired max number
   *	  of backends immediately at initialization --- if the sysadmin has set
!  *	  MaxConnections or autovacuum_max_workers higher than his kernel will
   *	  support, he'll find out sooner rather than later.
   *
   *	  Another reason for creating semaphores here is that the semaphore
--- 143,149 ----
   *	  running out when trying to start another backend is a common failure.
   *	  So, now we grab enough semaphores to support the desired max number
   *	  of backends immediately at initialization --- if the sysadmin has set
!  *	  MaxConnections or max_background_workers higher than his kernel will
   *	  support, he'll find out sooner rather than later.
   *
   *	  Another reason for creating semaphores here is that the semaphore
*************** InitProcGlobal(void)
*** 204,216 ****
  	 *
  	 * Note: the "+1" here accounts for the autovac launcher
  	 */
! 	procs = (PGPROC *) ShmemAlloc((autovacuum_max_workers + 1) * sizeof(PGPROC));
  	if (!procs)
  		ereport(FATAL,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of shared memory")));
! 	MemSet(procs, 0, (autovacuum_max_workers + 1) * sizeof(PGPROC));
! 	for (i = 0; i < autovacuum_max_workers + 1; i++)
  	{
  		PGSemaphoreCreate(&(procs[i].sem));
  		procs[i].links.next = (SHM_QUEUE *) ProcGlobal->autovacFreeProcs;
--- 204,216 ----
  	 *
  	 * Note: the "+1" here accounts for the autovac launcher
  	 */
! 	procs = (PGPROC *) ShmemAlloc((max_background_workers + 1) * sizeof(PGPROC));
  	if (!procs)
  		ereport(FATAL,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of shared memory")));
! 	MemSet(procs, 0, (max_background_workers + 1) * sizeof(PGPROC));
! 	for (i = 0; i < max_background_workers + 1; i++)
  	{
  		PGSemaphoreCreate(&(procs[i].sem));
  		procs[i].links.next = (SHM_QUEUE *) ProcGlobal->autovacFreeProcs;
*************** InitProcess(void)
*** 263,276 ****
  
  	set_spins_per_delay(procglobal->spins_per_delay);
  
! 	if (IsAutoVacuumLauncherProcess() || IsAutoVacuumWorkerProcess())
  		MyProc = procglobal->autovacFreeProcs;
  	else
  		MyProc = procglobal->freeProcs;
  
  	if (MyProc != NULL)
  	{
! 		if (IsAutoVacuumLauncherProcess() || IsAutoVacuumWorkerProcess())
  			procglobal->autovacFreeProcs = (PGPROC *) MyProc->links.next;
  		else
  			procglobal->freeProcs = (PGPROC *) MyProc->links.next;
--- 263,276 ----
  
  	set_spins_per_delay(procglobal->spins_per_delay);
  
! 	if (IsCoordinatorProcess() || IsBackgroundWorkerProcess())
  		MyProc = procglobal->autovacFreeProcs;
  	else
  		MyProc = procglobal->freeProcs;
  
  	if (MyProc != NULL)
  	{
! 		if (IsCoordinatorProcess() || IsBackgroundWorkerProcess())
  			procglobal->autovacFreeProcs = (PGPROC *) MyProc->links.next;
  		else
  			procglobal->freeProcs = (PGPROC *) MyProc->links.next;
*************** InitProcess(void)
*** 296,302 ****
  	 * cleaning up.  (XXX autovac launcher currently doesn't participate in
  	 * this; it probably should.)
  	 */
! 	if (IsUnderPostmaster && !IsAutoVacuumLauncherProcess())
  	{
  		if (am_walsender)
  			MarkPostmasterChildWalSender();
--- 296,302 ----
  	 * cleaning up.  (XXX autovac launcher currently doesn't participate in
  	 * this; it probably should.)
  	 */
! 	if (IsUnderPostmaster && !IsCoordinatorProcess())
  	{
  		if (am_walsender)
  			MarkPostmasterChildWalSender();
*************** InitProcess(void)
*** 321,327 ****
  	MyProc->inCommit = false;
  	MyProc->vacuumFlags = 0;
  	/* NB -- autovac launcher intentionally does not set IS_AUTOVACUUM */
! 	if (IsAutoVacuumWorkerProcess())
  		MyProc->vacuumFlags |= PROC_IS_AUTOVACUUM;
  	MyProc->lwWaiting = false;
  	MyProc->lwExclusive = false;
--- 321,327 ----
  	MyProc->inCommit = false;
  	MyProc->vacuumFlags = 0;
  	/* NB -- autovac launcher intentionally does not set IS_AUTOVACUUM */
! 	if (IsBackgroundWorkerProcess())
  		MyProc->vacuumFlags |= PROC_IS_AUTOVACUUM;
  	MyProc->lwWaiting = false;
  	MyProc->lwExclusive = false;
*************** ProcKill(int code, Datum arg)
*** 682,688 ****
  	SpinLockAcquire(ProcStructLock);
  
  	/* Return PGPROC structure (and semaphore) to appropriate freelist */
! 	if (IsAutoVacuumLauncherProcess() || IsAutoVacuumWorkerProcess())
  	{
  		MyProc->links.next = (SHM_QUEUE *) procglobal->autovacFreeProcs;
  		procglobal->autovacFreeProcs = MyProc;
--- 682,688 ----
  	SpinLockAcquire(ProcStructLock);
  
  	/* Return PGPROC structure (and semaphore) to appropriate freelist */
! 	if (IsCoordinatorProcess() || IsBackgroundWorkerProcess())
  	{
  		MyProc->links.next = (SHM_QUEUE *) procglobal->autovacFreeProcs;
  		procglobal->autovacFreeProcs = MyProc;
*************** ProcKill(int code, Datum arg)
*** 706,712 ****
  	 * way, so tell the postmaster we've cleaned up acceptably well. (XXX
  	 * autovac launcher should be included here someday)
  	 */
! 	if (IsUnderPostmaster && !IsAutoVacuumLauncherProcess())
  		MarkPostmasterChildInactive();
  }
  
--- 706,712 ----
  	 * way, so tell the postmaster we've cleaned up acceptably well. (XXX
  	 * autovac launcher should be included here someday)
  	 */
! 	if (IsUnderPostmaster && !IsCoordinatorProcess())
  		MarkPostmasterChildInactive();
  }
  
============================================================
*** src/backend/tcop/postgres.c	778b93a7067312d73d4c68561d8fa23476c0a292
--- src/backend/tcop/postgres.c	bbc0e7db5e6aae98d7077b6321089f4262547ca9
*************** ProcessInterrupts(void)
*** 2891,2900 ****
  		/* As in quickdie, don't risk sending to client during auth */
  		if (ClientAuthInProgress && whereToSendOutput == DestRemote)
  			whereToSendOutput = DestNone;
! 		if (IsAutoVacuumWorkerProcess())
  			ereport(FATAL,
  					(errcode(ERRCODE_ADMIN_SHUTDOWN),
! 					 errmsg("terminating autovacuum process due to administrator command")));
  		else if (RecoveryConflictPending && RecoveryConflictRetryable)
  			ereport(FATAL,
  					(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
--- 2891,2900 ----
  		/* As in quickdie, don't risk sending to client during auth */
  		if (ClientAuthInProgress && whereToSendOutput == DestRemote)
  			whereToSendOutput = DestNone;
! 		if (IsBackgroundWorkerProcess())
  			ereport(FATAL,
  					(errcode(ERRCODE_ADMIN_SHUTDOWN),
! 					 errmsg("terminating background worker process due to administrator command")));
  		else if (RecoveryConflictPending && RecoveryConflictRetryable)
  			ereport(FATAL,
  					(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
*************** ProcessInterrupts(void)
*** 2934,2940 ****
  					(errcode(ERRCODE_QUERY_CANCELED),
  					 errmsg("canceling statement due to statement timeout")));
  		}
! 		if (IsAutoVacuumWorkerProcess())
  		{
  			/* FIXME: make sure this works fine for all background jobs */
  			ImmediateInterruptOK = false;		/* not idle anymore */
--- 2934,2940 ----
  					(errcode(ERRCODE_QUERY_CANCELED),
  					 errmsg("canceling statement due to statement timeout")));
  		}
! 		if (IsBackgroundWorkerProcess())
  		{
  			/* FIXME: make sure this works fine for all background jobs */
  			ImmediateInterruptOK = false;		/* not idle anymore */
============================================================
*** src/backend/utils/init/globals.c	626dcd5939ff8882d155015f0aecae78e2945728
--- src/backend/utils/init/globals.c	941f73a44a2a558c326344246d35740bf320fef8
*************** int			maintenance_work_mem = 16384;
*** 101,107 ****
  
  /*
   * Primary determinants of sizes of shared-memory structures.  MaxBackends is
!  * MaxConnections + autovacuum_max_workers + 1 (it is computed by the GUC
   * assign hooks for those variables):
   */
  int			NBuffers = 1000;
--- 101,107 ----
  
  /*
   * Primary determinants of sizes of shared-memory structures.  MaxBackends is
!  * MaxConnections + max_background_workers + 1 (it is computed by the GUC
   * assign hooks for those variables):
   */
  int			NBuffers = 1000;
============================================================
*** src/backend/utils/init/miscinit.c	264963c6cfa0b082b09d6bba8b8d7df6c0f99eb5
--- src/backend/utils/init/miscinit.c	d3022f8ecf3b3b7084e064ac2661d1ff7ec1c560
*************** InitializeSessionUserIdStandalone(void)
*** 481,487 ****
  	 * This function should only be called in single-user mode and in
  	 * autovacuum workers.
  	 */
! 	AssertState(!IsUnderPostmaster || IsAutoVacuumWorkerProcess());
  
  	/* call only once */
  	AssertState(!OidIsValid(AuthenticatedUserId));
--- 481,487 ----
  	 * This function should only be called in single-user mode and in
  	 * autovacuum workers.
  	 */
! 	AssertState(!IsUnderPostmaster || IsBackgroundWorkerProcess());
  
  	/* call only once */
  	AssertState(!OidIsValid(AuthenticatedUserId));
============================================================
*** src/backend/utils/init/postinit.c	97b5d8e74b7361ae6f1776c60bc1f1977c2c31bc
--- src/backend/utils/init/postinit.c	7516795aa54c52d299b38a133329aa1ac8449a10
*************** CheckMyDatabase(const char *name, bool a
*** 281,287 ****
  	 *
  	 * We do not enforce them for autovacuum worker processes either.
  	 */
! 	if (IsUnderPostmaster && !IsAutoVacuumWorkerProcess())
  	{
  		/*
  		 * Check that the database is currently allowing connections.
--- 281,287 ----
  	 *
  	 * We do not enforce them for autovacuum worker processes either.
  	 */
! 	if (IsUnderPostmaster && !IsBackgroundWorkerProcess())
  	{
  		/*
  		 * Check that the database is currently allowing connections.
*************** InitPostgres(const char *in_dbname, Oid 
*** 569,575 ****
  	on_shmem_exit(ShutdownPostgres, 0);
  
  	/* The autovacuum launcher is done here */
! 	if (IsAutoVacuumLauncherProcess())
  		return;
  
  	/*
--- 569,575 ----
  	on_shmem_exit(ShutdownPostgres, 0);
  
  	/* The autovacuum launcher is done here */
! 	if (IsCoordinatorProcess())
  		return;
  
  	/*
*************** InitPostgres(const char *in_dbname, Oid 
*** 593,599 ****
  	 * In standalone mode and in autovacuum worker processes, we use a fixed
  	 * ID, otherwise we figure it out from the authenticated user name.
  	 */
! 	if (bootstrap || IsAutoVacuumWorkerProcess())
  	{
  		InitializeSessionUserIdStandalone();
  		am_superuser = true;
--- 593,599 ----
  	 * In standalone mode and in autovacuum worker processes, we use a fixed
  	 * ID, otherwise we figure it out from the authenticated user name.
  	 */
! 	if (bootstrap || IsBackgroundWorkerProcess())
  	{
  		InitializeSessionUserIdStandalone();
  		am_superuser = true;
============================================================
*** src/backend/access/transam/xlog.c	35715aec89ea13b429cffeb513fd439d1138cf0a
--- src/backend/access/transam/xlog.c	769d4de171e9c891a1cfb4516ff7a3e88f66033e
*************** CheckRequiredParameterValues(void)
*** 5660,5666 ****
  					(errmsg("hot standby is not possible because wal_level was not set to \"hot_standby\" on the master server"),
  					 errhint("Either set wal_level to \"hot_standby\" on the master, or turn off hot_standby here.")));
  
! 		/* We ignore autovacuum_max_workers when we make this test. */
  		RecoveryRequiresIntParameter("max_connections",
  									 MaxConnections,
  									 ControlFile->MaxConnections);
--- 5660,5666 ----
  					(errmsg("hot standby is not possible because wal_level was not set to \"hot_standby\" on the master server"),
  					 errhint("Either set wal_level to \"hot_standby\" on the master, or turn off hot_standby here.")));
  
! 		/* We ignore max__background_workers when we make this test. */
  		RecoveryRequiresIntParameter("max_connections",
  									 MaxConnections,
  									 ControlFile->MaxConnections);
============================================================
*** src/backend/commands/analyze.c	106a0d69fd64ba23ac9cb7d022ed85673515b8a5
--- src/backend/commands/analyze.c	618166236ea3b1f37c763478c0000bf14654717c
*************** do_analyze_rel(Relation onerel, VacuumSt
*** 310,316 ****
  	save_nestlevel = NewGUCNestLevel();
  
  	/* measure elapsed time iff autovacuum logging requires it */
! 	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
  	{
  		pg_rusage_init(&ru0);
  		if (Log_autovacuum_min_duration > 0)
--- 310,316 ----
  	save_nestlevel = NewGUCNestLevel();
  
  	/* measure elapsed time iff autovacuum logging requires it */
! 	if (IsBackgroundWorkerProcess() && Log_autovacuum_min_duration >= 0)
  	{
  		pg_rusage_init(&ru0);
  		if (Log_autovacuum_min_duration > 0)
*************** cleanup:
*** 612,618 ****
  	vac_close_indexes(nindexes, Irel, NoLock);
  
  	/* Log the action if appropriate */
! 	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
  	{
  		if (Log_autovacuum_min_duration == 0 ||
  			TimestampDifferenceExceeds(starttime, GetCurrentTimestamp(),
--- 612,618 ----
  	vac_close_indexes(nindexes, Irel, NoLock);
  
  	/* Log the action if appropriate */
! 	if (IsBackgroundWorkerProcess() && Log_autovacuum_min_duration >= 0)
  	{
  		if (Log_autovacuum_min_duration == 0 ||
  			TimestampDifferenceExceeds(starttime, GetCurrentTimestamp(),
============================================================
*** src/backend/utils/misc/guc.c	b1a0bdd9b727094182159680278763a09f3ded9c
--- src/backend/utils/misc/guc.c	194be259610536302033f04f41fcd59e83620e77
*************** static bool assign_maxconnections(int ne
*** 170,176 ****
  static const char *show_tcp_keepalives_interval(void);
  static const char *show_tcp_keepalives_count(void);
  static bool assign_maxconnections(int newval, bool doit, GucSource source);
! static bool assign_autovacuum_max_workers(int newval, bool doit, GucSource source);
  static bool assign_effective_io_concurrency(int newval, bool doit, GucSource source);
  static const char *assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source);
  static const char *assign_application_name(const char *newval, bool doit, GucSource source);
--- 170,176 ----
  static const char *show_tcp_keepalives_interval(void);
  static const char *show_tcp_keepalives_count(void);
  static bool assign_maxconnections(int newval, bool doit, GucSource source);
! static bool assign_max_background_workers(int newval, bool doit, GucSource source);
  static bool assign_effective_io_concurrency(int newval, bool doit, GucSource source);
  static const char *assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source);
  static const char *assign_application_name(const char *newval, bool doit, GucSource source);
*************** static struct config_int ConfigureNamesI
*** 1397,1403 ****
  	 * Note: MaxBackends is limited to INT_MAX/4 because some places compute
  	 * 4*MaxBackends without any overflow check.  This check is made in
  	 * assign_maxconnections, since MaxBackends is computed as MaxConnections
! 	 * plus autovacuum_max_workers plus one (for the autovacuum launcher).
  	 *
  	 * Likewise we have to limit NBuffers to INT_MAX/2.
  	 *
--- 1397,1403 ----
  	 * Note: MaxBackends is limited to INT_MAX/4 because some places compute
  	 * 4*MaxBackends without any overflow check.  This check is made in
  	 * assign_maxconnections, since MaxBackends is computed as MaxConnections
! 	 * plus max_background_workers plus one (for the autovacuum launcher).
  	 *
  	 * Likewise we have to limit NBuffers to INT_MAX/2.
  	 *
*************** static struct config_int ConfigureNamesI
*** 1994,2005 ****
  	},
  	{
  		/* see max_connections */
! 		{"autovacuum_max_workers", PGC_POSTMASTER, AUTOVACUUM,
  			gettext_noop("Sets the maximum number of simultaneously running autovacuum worker processes."),
  			NULL
  		},
! 		&autovacuum_max_workers,
! 		3, 1, INT_MAX / 4, assign_autovacuum_max_workers, NULL
  	},
  
  	{
--- 1994,2005 ----
  	},
  	{
  		/* see max_connections */
! 		{"max_background_workers", PGC_POSTMASTER, AUTOVACUUM,
  			gettext_noop("Sets the maximum number of simultaneously running autovacuum worker processes."),
  			NULL
  		},
! 		&max_background_workers,
! 		3, 1, INT_MAX / 4, assign_max_background_workers, NULL
  	},
  
  	{
*************** assign_maxconnections(int newval, bool d
*** 7972,7988 ****
  static bool
  assign_maxconnections(int newval, bool doit, GucSource source)
  {
! 	if (newval + autovacuum_max_workers + 1 > INT_MAX / 4)
  		return false;
  
  	if (doit)
! 		MaxBackends = newval + autovacuum_max_workers + 1;
  
  	return true;
  }
  
  static bool
! assign_autovacuum_max_workers(int newval, bool doit, GucSource source)
  {
  	if (MaxConnections + newval + 1 > INT_MAX / 4)
  		return false;
--- 7972,7988 ----
  static bool
  assign_maxconnections(int newval, bool doit, GucSource source)
  {
! 	if (newval + max_background_workers + 1 > INT_MAX / 4)
  		return false;
  
  	if (doit)
! 		MaxBackends = newval + max_background_workers + 1;
  
  	return true;
  }
  
  static bool
! assign_max_background_workers(int newval, bool doit, GucSource source)
  {
  	if (MaxConnections + newval + 1 > INT_MAX / 4)
  		return false;
============================================================
*** src/backend/utils/misc/postgresql.conf.sample	14a99f6b70482f481ccd82dad86e31a9421c9336
--- src/backend/utils/misc/postgresql.conf.sample	d9acfb47e04263a3caae6ec5e5c04316f3a7f176
***************
*** 404,413 ****
  
  
  #------------------------------------------------------------------------------
! # BACKGROUND WORKER PARAMETERS (including autovacuum)
  #------------------------------------------------------------------------------
  
! #autovacuum_max_workers = 3		# max number of background helper
  			  		# subprocesses (for all databases)
  
  #min_spare_background_helpers = 0	# min spare workers to prefork and
--- 404,413 ----
  
  
  #------------------------------------------------------------------------------
! # BACKGROUND WORKER PARAMETERS (includes autovacuum)
  #------------------------------------------------------------------------------
  
! #max_background_workers = 3		# max number of background helper
  			  		# subprocesses (for all databases)
  
  #min_spare_background_helpers = 0	# min spare workers to prefork and
============================================================
*** src/backend/postmaster/pgstat.c	cfd69500a4ad774f69f30ab3a759d090fbb09e6a
--- src/backend/postmaster/pgstat.c	6f82a2553021742674644dccbb7072db58aa3711
*************** pgstat_report_vacuum(Oid tableoid, bool 
*** 1256,1262 ****
  	msg.m_databaseid = shared ? InvalidOid : MyDatabaseId;
  	msg.m_tableoid = tableoid;
  	msg.m_adopt_counts = adopt_counts;
! 	msg.m_autovacuum = IsAutoVacuumWorkerProcess();
  	msg.m_vacuumtime = GetCurrentTimestamp();
  	msg.m_tuples = tuples;
  	pgstat_send(&msg, sizeof(msg));
--- 1256,1262 ----
  	msg.m_databaseid = shared ? InvalidOid : MyDatabaseId;
  	msg.m_tableoid = tableoid;
  	msg.m_adopt_counts = adopt_counts;
! 	msg.m_bgworker = IsBackgroundWorkerProcess();
  	msg.m_vacuumtime = GetCurrentTimestamp();
  	msg.m_tuples = tuples;
  	pgstat_send(&msg, sizeof(msg));
*************** pgstat_report_analyze(Relation rel, bool
*** 1307,1313 ****
  	msg.m_databaseid = rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId;
  	msg.m_tableoid = RelationGetRelid(rel);
  	msg.m_adopt_counts = adopt_counts;
! 	msg.m_autovacuum = IsAutoVacuumWorkerProcess();
  	msg.m_analyzetime = GetCurrentTimestamp();
  	msg.m_live_tuples = livetuples;
  	msg.m_dead_tuples = deadtuples;
--- 1307,1313 ----
  	msg.m_databaseid = rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId;
  	msg.m_tableoid = RelationGetRelid(rel);
  	msg.m_adopt_counts = adopt_counts;
! 	msg.m_bgworker = IsBackgroundWorkerProcess();
  	msg.m_analyzetime = GetCurrentTimestamp();
  	msg.m_live_tuples = livetuples;
  	msg.m_dead_tuples = deadtuples;
*************** backend_read_statsfile(void)
*** 3669,3675 ****
  	 * PGSTAT_STAT_INTERVAL; and we don't want to lie to the collector about
  	 * what our cutoff time really is.
  	 */
! 	if (IsAutoVacuumWorkerProcess())
  		min_ts = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
  											 -PGSTAT_RETRY_DELAY);
  	else
--- 3669,3675 ----
  	 * PGSTAT_STAT_INTERVAL; and we don't want to lie to the collector about
  	 * what our cutoff time really is.
  	 */
! 	if (IsBackgroundWorkerProcess())
  		min_ts = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
  											 -PGSTAT_RETRY_DELAY);
  	else
*************** backend_read_statsfile(void)
*** 3700,3706 ****
  		elog(WARNING, "pgstat wait timeout");
  
  	/* Autovacuum launcher wants stats about all databases */
! 	if (IsAutoVacuumLauncherProcess())
  		pgStatDBHash = pgstat_read_statsfile(InvalidOid, false);
  	else
  		pgStatDBHash = pgstat_read_statsfile(MyDatabaseId, false);
--- 3700,3706 ----
  		elog(WARNING, "pgstat wait timeout");
  
  	/* Autovacuum launcher wants stats about all databases */
! 	if (IsCoordinatorProcess())
  		pgStatDBHash = pgstat_read_statsfile(InvalidOid, false);
  	else
  		pgStatDBHash = pgstat_read_statsfile(MyDatabaseId, false);
*************** pgstat_recv_vacuum(PgStat_MsgVacuum *msg
*** 4070,4076 ****
  	/* Resetting dead_tuples to 0 is an approximation ... */
  	tabentry->n_dead_tuples = 0;
  
! 	if (msg->m_autovacuum)
  		tabentry->autovac_vacuum_timestamp = msg->m_vacuumtime;
  	else
  		tabentry->vacuum_timestamp = msg->m_vacuumtime;
--- 4070,4076 ----
  	/* Resetting dead_tuples to 0 is an approximation ... */
  	tabentry->n_dead_tuples = 0;
  
! 	if (msg->m_bgworker)
  		tabentry->autovac_vacuum_timestamp = msg->m_vacuumtime;
  	else
  		tabentry->vacuum_timestamp = msg->m_vacuumtime;
*************** pgstat_recv_analyze(PgStat_MsgAnalyze *m
*** 4107,4113 ****
  	 */
  	tabentry->changes_since_analyze = 0;
  
! 	if (msg->m_autovacuum)
  		tabentry->autovac_analyze_timestamp = msg->m_analyzetime;
  	else
  		tabentry->analyze_timestamp = msg->m_analyzetime;
--- 4107,4113 ----
  	 */
  	tabentry->changes_since_analyze = 0;
  
! 	if (msg->m_bgworker)
  		tabentry->autovac_analyze_timestamp = msg->m_analyzetime;
  	else
  		tabentry->analyze_timestamp = msg->m_analyzetime;
============================================================
*** src/include/pgstat.h	3c935ec633909c2864ad4a79c7a8c830289150d9
--- src/include/pgstat.h	eb6b09e66466b9220aad3854e7877d7709b11ffd
*************** typedef struct PgStat_MsgVacuum
*** 322,328 ****
  	Oid			m_databaseid;
  	Oid			m_tableoid;
  	bool		m_adopt_counts;
! 	bool		m_autovacuum;
  	TimestampTz m_vacuumtime;
  	PgStat_Counter m_tuples;
  } PgStat_MsgVacuum;
--- 322,328 ----
  	Oid			m_databaseid;
  	Oid			m_tableoid;
  	bool		m_adopt_counts;
! 	bool		m_bgworker;
  	TimestampTz m_vacuumtime;
  	PgStat_Counter m_tuples;
  } PgStat_MsgVacuum;
*************** typedef struct PgStat_MsgAnalyze
*** 339,345 ****
  	Oid			m_databaseid;
  	Oid			m_tableoid;
  	bool		m_adopt_counts;
! 	bool		m_autovacuum;
  	TimestampTz m_analyzetime;
  	PgStat_Counter m_live_tuples;
  	PgStat_Counter m_dead_tuples;
--- 339,345 ----
  	Oid			m_databaseid;
  	Oid			m_tableoid;
  	bool		m_adopt_counts;
! 	bool		m_bgworker;
  	TimestampTz m_analyzetime;
  	PgStat_Counter m_live_tuples;
  	PgStat_Counter m_dead_tuples;
============================================================
*** src/backend/commands/vacuumlazy.c	2a1a3d68a6bdf7d0a558a03d9cec90c586beae3e
--- src/backend/commands/vacuumlazy.c	ee705c606001471649559bc45d1676b7c943c3a2
*************** lazy_vacuum_rel(Relation onerel, VacuumS
*** 157,163 ****
  	pg_rusage_init(&ru0);
  
  	/* measure elapsed time iff autovacuum logging requires it */
! 	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration > 0)
  		starttime = GetCurrentTimestamp();
  
  	if (vacstmt->options & VACOPT_VERBOSE)
--- 157,163 ----
  	pg_rusage_init(&ru0);
  
  	/* measure elapsed time iff autovacuum logging requires it */
! 	if (IsBackgroundWorkerProcess() && Log_autovacuum_min_duration > 0)
  		starttime = GetCurrentTimestamp();
  
  	if (vacstmt->options & VACOPT_VERBOSE)
*************** lazy_vacuum_rel(Relation onerel, VacuumS
*** 225,231 ****
  						 vacrelstats->rel_tuples);
  
  	/* and log the action if appropriate */
! 	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
  	{
  		if (Log_autovacuum_min_duration == 0 ||
  			TimestampDifferenceExceeds(starttime, GetCurrentTimestamp(),
--- 225,231 ----
  						 vacrelstats->rel_tuples);
  
  	/* and log the action if appropriate */
! 	if (IsBackgroundWorkerProcess() && Log_autovacuum_min_duration >= 0)
  	{
  		if (Log_autovacuum_min_duration == 0 ||
  			TimestampDifferenceExceeds(starttime, GetCurrentTimestamp(),
============================================================
*** src/include/storage/lwlock.h	2aea3a2fcddca3a9f9eea62f65d08930d221beb5
--- src/include/storage/lwlock.h	961fe3aa8f73e8c722383e33513426a6c5f83ba4
*************** typedef enum LWLockId
*** 64,71 ****
  	TablespaceCreateLock,
  	BtreeVacuumLock,
  	AddinShmemInitLock,
! 	AutovacuumLock,
! 	AutovacuumScheduleLock,
  	CoordinatorDatabasesLock,
  	SyncScanLock,
  	RelationMappingLock,
--- 64,71 ----
  	TablespaceCreateLock,
  	BtreeVacuumLock,
  	AddinShmemInitLock,
! 	WorkerInfoLock,
! 	WorkerScheduleLock,
  	CoordinatorDatabasesLock,
  	SyncScanLock,
  	RelationMappingLock,
============================================================
*** src/include/storage/pmsignal.h	c8113d31e2e76fc64ad82209ff2fb8aea91f68b3
--- src/include/storage/pmsignal.h	0d704becf5cfcbacf1d614e025f3622d76b489fc
*************** typedef enum
*** 26,32 ****
  	PMSIGNAL_BEGIN_HOT_STANDBY, /* begin Hot Standby */
  	PMSIGNAL_WAKEN_ARCHIVER,	/* send a NOTIFY signal to xlog archiver */
  	PMSIGNAL_ROTATE_LOGFILE,	/* send SIGUSR1 to syslogger to rotate logfile */
! 	PMSIGNAL_START_AUTOVAC_WORKER,		/* start an autovacuum worker */
  	PMSIGNAL_START_WALRECEIVER, /* start a walreceiver */
  
  	NUM_PMSIGNALS				/* Must be last value of enum! */
--- 26,32 ----
  	PMSIGNAL_BEGIN_HOT_STANDBY, /* begin Hot Standby */
  	PMSIGNAL_WAKEN_ARCHIVER,	/* send a NOTIFY signal to xlog archiver */
  	PMSIGNAL_ROTATE_LOGFILE,	/* send SIGUSR1 to syslogger to rotate logfile */
! 	PMSIGNAL_START_BGWORKER,	/* start a background worker process */
  	PMSIGNAL_START_WALRECEIVER, /* start a walreceiver */
  
  	NUM_PMSIGNALS				/* Must be last value of enum! */
============================================================
*** src/backend/postmaster/autovacuum.c	ca02f9e08a1dbe34ff8049c0d51cc76594fb16a0
--- src/backend/postmaster/autovacuum.c	9f0dd25e08c15c2cf3989e24996554fa94c99f08
*************** bool		autovacuum_enabled = false;
*** 105,111 ****
   * GUC parameters
   */
  bool		autovacuum_enabled = false;
! int			autovacuum_max_workers;
  int			min_spare_background_workers;
  int			max_spare_background_workers;
  int			autovacuum_naptime;
--- 105,111 ----
   * GUC parameters
   */
  bool		autovacuum_enabled = false;
! int			max_background_workers;
  int			min_spare_background_workers;
  int			max_spare_background_workers;
  int			autovacuum_naptime;
*************** int			Log_autovacuum_min_duration = -1;
*** 126,134 ****
  /* the minimum allowed time between two awakenings of the launcher */
  #define MIN_AUTOVAC_SLEEPTIME 100.0		/* milliseconds */
  
! /* Flags to tell if we are in an autovacuum process */
! static bool am_autovacuum_launcher = false;
! static bool am_autovacuum_worker = false;
  
  /* Flags set by signal handlers */
  static volatile sig_atomic_t got_SIGHUP = false;
--- 126,134 ----
  /* the minimum allowed time between two awakenings of the launcher */
  #define MIN_AUTOVAC_SLEEPTIME 100.0		/* milliseconds */
  
! /* Flags to tell if we are in a coordinator or background worker process */
! static bool am_coordinator = false;
! static bool am_background_worker = false;
  
  /* Flags set by signal handlers */
  static volatile sig_atomic_t got_SIGHUP = false;
*************** static int	default_freeze_table_age;
*** 143,150 ****
  static int	default_freeze_table_age;
  
  /* Memory contexts for long-lived data */
! static MemoryContext AutovacLauncherMemCxt;
! static MemoryContext AutovacWorkerMemCxt;
  
  /* job handling routines */
  static void bgworker_job_initialize(worker_state new_state);
--- 143,150 ----
  static int	default_freeze_table_age;
  
  /* Memory contexts for long-lived data */
! static MemoryContext CoordinatorMemCxt;
! static MemoryContext BgWorkerMemCxt;
  
  /* job handling routines */
  static void bgworker_job_initialize(worker_state new_state);
*************** typedef struct autovac_table
*** 199,226 ****
  } autovac_table;
  
  /*-------------
!  * The main autovacuum shmem struct.  On shared memory we store this main
!  * struct and the array of WorkerInfo structs.	This struct keeps:
   *
!  * av_launcherid	the BackendId of the autovacuum launcher
!  * av_freeWorkers	the WorkerInfo freelist
!  * av_runningWorkers the WorkerInfo non-free queue
!  * av_startingWorker pointer to WorkerInfo currently being started (cleared by
!  *					the worker itself as soon as it's up and running)
   *
!  * This struct is protected by AutovacuumLock, except for parts of the worker
   * list (see above).
   *-------------
   */
  typedef struct
  {
! 	BackendId	av_launcherid;
! 	WorkerInfo	av_freeWorkers;
! 	SHM_QUEUE	av_runningWorkers;
! 	WorkerInfo	av_startingWorker;
! } AutoVacuumShmemStruct;
  
! static AutoVacuumShmemStruct *AutoVacuumShmem;
  
  /*
   * Table of databases with at least one connected worker, resides in shared
--- 199,227 ----
  } autovac_table;
  
  /*-------------
!  * The main background worker shmem struct.  On shared memory we store this
!  * main struct and the array of WorkerInfo structs.	This struct keeps:
   *
!  * co_coordinatorid    the BackendId of the coordinator
!  * co_freeWorkers	   the WorkerInfo freelist
!  * co_runningWorkers   the WorkerInfo non-free queue
!  * co_startingWorker   pointer to WorkerInfo currently being started
!  *                      (cleared by the worker itself as soon as it's up and
!  *                      running)
   *
!  * This struct is protected by WorkerInfoLock, except for parts of the worker
   * list (see above).
   *-------------
   */
  typedef struct
  {
! 	BackendId	co_coordinatorid;
! 	WorkerInfo	co_freeWorkers;
! 	SHM_QUEUE	co_runningWorkers;
! 	WorkerInfo	co_startingWorker;
! } CoordinatorShmemStruct;
  
! static CoordinatorShmemStruct *CoordinatorShmem;
  
  /*
   * Table of databases with at least one connected worker, resides in shared
*************** static WorkerInfo terminatable_worker = 
*** 237,248 ****
  static WorkerInfo terminatable_worker = NULL;
  
  #ifdef EXEC_BACKEND
! static pid_t avlauncher_forkexec(void);
! static pid_t avworker_forkexec(void);
  #endif
! NON_EXEC_STATIC void AutoVacWorkerMain(int argc, char *argv[]);
  static void handle_imessage(IMessage *msg);
! NON_EXEC_STATIC void AutoVacLauncherMain(int argc, char *argv[]);
  
  static void init_co_database(co_database *codb);
  static co_database *get_co_database(Oid dboid);
--- 238,249 ----
  static WorkerInfo terminatable_worker = NULL;
  
  #ifdef EXEC_BACKEND
! static pid_t coordinator_forkexec(void);
! static pid_t bgworker_forkexec(void);
  #endif
! NON_EXEC_STATIC void BackgroundWorkerMain(int argc, char *argv[]);
  static void handle_imessage(IMessage *msg);
! NON_EXEC_STATIC void CoordinatorMain(int argc, char *argv[]);
  
  static void init_co_database(co_database *codb);
  static co_database *get_co_database(Oid dboid);
*************** static Oid autovacuum_select_database(vo
*** 263,269 ****
  
  static void do_start_worker(Oid dboid);
  static Oid autovacuum_select_database(void);
! static void launcher_determine_sleep(bool can_launch, bool recursing,
  						 struct timespec *nap);
  static void autovacuum_update_timing(Oid dbid, TimestampTz now);
  static List *get_database_list(void);
--- 264,270 ----
  
  static void do_start_worker(Oid dboid);
  static Oid autovacuum_select_database(void);
! static void coordinator_determine_sleep(bool can_launch, bool recursing,
  						 struct timespec *nap);
  static void autovacuum_update_timing(Oid dbid, TimestampTz now);
  static List *get_database_list(void);
*************** decode_worker_state(worker_state state)
*** 312,334 ****
  
  
  /********************************************************************
!  *					  AUTOVACUUM LAUNCHER CODE
   ********************************************************************/
  
  #ifdef EXEC_BACKEND
  /*
!  * forkexec routine for the autovacuum launcher process.
   *
   * Format up the arglist, then fork and exec.
   */
  static pid_t
! avlauncher_forkexec(void)
  {
  	char	   *av[10];
  	int			ac = 0;
  
  	av[ac++] = "postgres";
! 	av[ac++] = "--forkavlauncher";
  	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
  	av[ac] = NULL;
  
--- 313,335 ----
  
  
  /********************************************************************
!  *					      COORDINATOR CODE
   ********************************************************************/
  
  #ifdef EXEC_BACKEND
  /*
!  * forkexec routine for the coordinator process.
   *
   * Format up the arglist, then fork and exec.
   */
  static pid_t
! coordinator_forkexec(void)
  {
  	char	   *av[10];
  	int			ac = 0;
  
  	av[ac++] = "postgres";
! 	av[ac++] = "--forkcoordinator";
  	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
  	av[ac] = NULL;
  
*************** void
*** 341,370 ****
   * We need this set from the outside, before InitProcess is called
   */
  void
! AutovacuumLauncherIAm(void)
  {
! 	am_autovacuum_launcher = true;
  }
  #endif
  
  /*
!  * Main entry point for autovacuum launcher process, to be called from the
!  * postmaster.
   */
  int
! StartAutoVacLauncher(void)
  {
! 	pid_t		AutoVacPID;
  
  #ifdef EXEC_BACKEND
! 	switch ((AutoVacPID = avlauncher_forkexec()))
  #else
! 	switch ((AutoVacPID = fork_process()))
  #endif
  	{
  		case -1:
  			ereport(LOG,
! 				 (errmsg("could not fork autovacuum launcher process: %m")));
  			return 0;
  
  #ifndef EXEC_BACKEND
--- 342,370 ----
   * We need this set from the outside, before InitProcess is called
   */
  void
! CoordinatorIAm(void)
  {
! 	am_coordinator = true;
  }
  #endif
  
  /*
!  * Main entry point for coordinator process, to be called from the postmaster.
   */
  int
! StartCoordinator(void)
  {
! 	pid_t		CoordinatorPID;
  
  #ifdef EXEC_BACKEND
! 	switch ((CoordinatorPID = coordinator_forkexec()))
  #else
! 	switch ((CoordinatorPID = fork_process()))
  #endif
  	{
  		case -1:
  			ereport(LOG,
! 				 (errmsg("could not fork the coordinator process: %m")));
  			return 0;
  
  #ifndef EXEC_BACKEND
*************** StartAutoVacLauncher(void)
*** 376,386 ****
  			/* Lose the postmaster's on-exit routines */
  			on_exit_reset();
  
! 			AutoVacLauncherMain(0, NULL);
  			break;
  #endif
  		default:
! 			return (int) AutoVacPID;
  	}
  
  	/* shouldn't get here */
--- 376,386 ----
  			/* Lose the postmaster's on-exit routines */
  			on_exit_reset();
  
! 			CoordinatorMain(0, NULL);
  			break;
  #endif
  		default:
! 			return (int) CoordinatorPID;
  	}
  
  	/* shouldn't get here */
*************** populate_co_databases()
*** 581,590 ****
  }
  
  /*
!  * Main loop for the autovacuum launcher process.
   */
  NON_EXEC_STATIC void
! AutoVacLauncherMain(int argc, char *argv[])
  {
  	sigjmp_buf	local_sigjmp_buf;
  	IMessage   *msg = NULL;
--- 581,590 ----
  }
  
  /*
!  * Main loop for the coordinator process.
   */
  NON_EXEC_STATIC void
! CoordinatorMain(int argc, char *argv[])
  {
  	sigjmp_buf	local_sigjmp_buf;
  	IMessage   *msg = NULL;
*************** AutoVacLauncherMain(int argc, char *argv
*** 592,598 ****
  
  	/* we are a postmaster subprocess now */
  	IsUnderPostmaster = true;
! 	am_autovacuum_launcher = true;
  
  	/* reset MyProcPid */
  	MyProcPid = getpid();
--- 592,598 ----
  
  	/* we are a postmaster subprocess now */
  	IsUnderPostmaster = true;
! 	am_coordinator = true;
  
  	/* reset MyProcPid */
  	MyProcPid = getpid();
*************** AutoVacLauncherMain(int argc, char *argv
*** 601,610 ****
  	MyStartTime = time(NULL);
  
  	/* Identify myself via ps */
! 	init_ps_display("autovacuum launcher process", "", "", "");
  
  	ereport(LOG,
! 			(errmsg("autovacuum launcher started")));
  
  	if (PostAuthDelay)
  		pg_usleep(PostAuthDelay * 1000000L);
--- 601,610 ----
  	MyStartTime = time(NULL);
  
  	/* Identify myself via ps */
! 	init_ps_display("coordinator process", "", "", "");
  
  	ereport(LOG,
! 			(errmsg("coordinator started")));
  
  	if (PostAuthDelay)
  		pg_usleep(PostAuthDelay * 1000000L);
*************** AutoVacLauncherMain(int argc, char *argv
*** 613,620 ****
  
  	/*
  	 * If possible, make this process a group leader, so that the postmaster
! 	 * can signal any child processes too.	(autovacuum probably never has any
! 	 * child processes, but for consistency we make all postmaster child
  	 * processes do this.)
  	 */
  #ifdef HAVE_SETSID
--- 613,620 ----
  
  	/*
  	 * If possible, make this process a group leader, so that the postmaster
! 	 * can signal any child processes too.	(coordinator probably never has
! 	 * any child processes, but for consistency we make all postmaster child
  	 * processes do this.)
  	 */
  #ifdef HAVE_SETSID
*************** AutoVacLauncherMain(int argc, char *argv
*** 662,673 ****
  	 * that we can reset the context during error recovery and thereby avoid
  	 * possible memory leaks.
  	 */
! 	AutovacLauncherMemCxt = AllocSetContextCreate(TopMemoryContext,
! 												  "Autovacuum Launcher",
! 												  ALLOCSET_DEFAULT_MINSIZE,
! 												  ALLOCSET_DEFAULT_INITSIZE,
! 												  ALLOCSET_DEFAULT_MAXSIZE);
! 	MemoryContextSwitchTo(AutovacLauncherMemCxt);
  
  	/*
  	 * If an exception is encountered, processing resumes here.
--- 662,673 ----
  	 * that we can reset the context during error recovery and thereby avoid
  	 * possible memory leaks.
  	 */
! 	CoordinatorMemCxt = AllocSetContextCreate(TopMemoryContext,
! 											  "Coordinator",
! 											  ALLOCSET_DEFAULT_MINSIZE,
! 											  ALLOCSET_DEFAULT_INITSIZE,
! 											  ALLOCSET_DEFAULT_MAXSIZE);
! 	MemoryContextSwitchTo(CoordinatorMemCxt);
  
  	/*
  	 * If an exception is encountered, processing resumes here.
*************** AutoVacLauncherMain(int argc, char *argv
*** 697,707 ****
  		 * Now return to normal top-level context and clear ErrorContext for
  		 * next time.
  		 */
! 		MemoryContextSwitchTo(AutovacLauncherMemCxt);
  		FlushErrorState();
  
  		/* Flush any leaked data in the top-level context */
! 		MemoryContextResetAndDeleteChildren(AutovacLauncherMemCxt);
  
  		/* don't leave dangling pointers to freed memory */
  		DatabaseListCxt = NULL;
--- 697,707 ----
  		 * Now return to normal top-level context and clear ErrorContext for
  		 * next time.
  		 */
! 		MemoryContextSwitchTo(CoordinatorMemCxt);
  		FlushErrorState();
  
  		/* Flush any leaked data in the top-level context */
! 		MemoryContextResetAndDeleteChildren(CoordinatorMemCxt);
  
  		/* don't leave dangling pointers to freed memory */
  		DatabaseListCxt = NULL;
*************** AutoVacLauncherMain(int argc, char *argv
*** 729,735 ****
  	/* must unblock signals before calling rebuild_database_list */
  	PG_SETMASK(&UnBlockSig);
  
! 	AutoVacuumShmem->av_launcherid = MyBackendId;
  
  	/*
  	 * Initial population of the database list from pg_database
--- 729,735 ----
  	/* must unblock signals before calling rebuild_database_list */
  	PG_SETMASK(&UnBlockSig);
  
! 	CoordinatorShmem->co_coordinatorid = MyBackendId;
  
  	/*
  	 * Initial population of the database list from pg_database
*************** AutoVacLauncherMain(int argc, char *argv
*** 761,768 ****
  		if (!PostmasterIsAlive(true))
  			proc_exit(1);
  
! 		can_launch = (AutoVacuumShmem->av_freeWorkers != NULL);
! 		launcher_determine_sleep(can_launch, false, &nap);
  
  		/* Initialize variables for listening on sockets */ 
  		FD_ZERO(&socks);
--- 761,768 ----
  		if (!PostmasterIsAlive(true))
  			proc_exit(1);
  
! 		can_launch = (CoordinatorShmem->co_freeWorkers != NULL);
! 		coordinator_determine_sleep(can_launch, false, &nap);
  
  		/* Initialize variables for listening on sockets */ 
  		FD_ZERO(&socks);
*************** AutoVacLauncherMain(int argc, char *argv
*** 847,855 ****
  			ProcessConfigFile(PGC_SIGHUP);
  
  			/* rebalance in case the default cost parameters changed */
! 			LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  			autovac_balance_cost();
! 			LWLockRelease(AutovacuumLock);
  
  			/* rebuild the list in case the naptime changed */
  			rebuild_database_list(InvalidOid);
--- 847,855 ----
  			ProcessConfigFile(PGC_SIGHUP);
  
  			/* rebalance in case the default cost parameters changed */
! 			LWLockAcquire(WorkerInfoLock, LW_EXCLUSIVE);
  			autovac_balance_cost();
! 			LWLockRelease(WorkerInfoLock);
  
  			/* rebuild the list in case the naptime changed */
  			rebuild_database_list(InvalidOid);
*************** AutoVacLauncherMain(int argc, char *argv
*** 885,891 ****
  				 *        is currently unable to fork new workers.
  				 */
  				pg_usleep(1000000L);	/* 1s */
! 				SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER);
  				continue;
  			}
  		}
--- 885,891 ----
  				 *        is currently unable to fork new workers.
  				 */
  				pg_usleep(1000000L);	/* 1s */
! 				SendPostmasterSignal(PMSIGNAL_START_BGWORKER);
  				continue;
  			}
  		}
*************** AutoVacLauncherMain(int argc, char *argv
*** 897,906 ****
  
  		/* handle pending imessages */
  		while ((msg = IMessageCheck()) != NULL)
- 		{
  			handle_imessage(msg);
- 			msg = IMessageCheck();
- 		}
  
  		current_time  = GetCurrentTimestamp();
  		can_launch = CoordinatorCanLaunchWorker(current_time);
--- 897,903 ----
*************** AutoVacLauncherMain(int argc, char *argv
*** 915,924 ****
  		manage_workers(can_launch);
  	}
  
! 	/* Normal exit from the autovac launcher is here */
  	ereport(LOG,
! 			(errmsg("autovacuum launcher shutting down")));
! 	AutoVacuumShmem->av_launcherid = InvalidBackendId;
  
  	proc_exit(0);				/* done */
  }
--- 912,921 ----
  		manage_workers(can_launch);
  	}
  
! 	/* Normal exit from the coordinator is here */
  	ereport(LOG,
! 			(errmsg("coordinator shutting down")));
! 	CoordinatorShmem->co_coordinatorid = InvalidBackendId;
  
  	proc_exit(0);				/* done */
  }
*************** handle_imessage(IMessage *msg)
*** 991,999 ****
  			 * removed, so there's probably no point in rebalancing here.
  			 * So: FIXME.
  			 */
! 			LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  			autovac_balance_cost();
! 			LWLockRelease(AutovacuumLock);
  
  			break;
  
--- 988,996 ----
  			 * removed, so there's probably no point in rebalancing here.
  			 * So: FIXME.
  			 */
! 			LWLockAcquire(WorkerInfoLock, LW_EXCLUSIVE);
  			autovac_balance_cost();
! 			LWLockRelease(WorkerInfoLock);
  
  			break;
  
*************** can_deliver_cached_job(co_database *codb
*** 1067,1073 ****
   *
   * Starts background workers for databases which have at least one cached
   * job or which have less than min_background_workers connected. Within the
!  * same loop, the autovacuum_max_workers is checked and terminates a worker
   * accordingly.
   * 
   * Note that at max one worker can be requested to start or stop per
--- 1064,1070 ----
   *
   * Starts background workers for databases which have at least one cached
   * job or which have less than min_background_workers connected. Within the
!  * same loop, the max_background_workers is checked and terminates a worker
   * accordingly.
   * 
   * Note that at max one worker can be requested to start or stop per
*************** manage_workers(bool can_launch)
*** 1085,1093 ****
  	int                     idle_workers_required;
  	int                     job_workers_required;
  
! 	LWLockAcquire(AutovacuumLock, LW_SHARED);
! 	worker_slots_available = (AutoVacuumShmem->av_freeWorkers != NULL);
! 	LWLockRelease(AutovacuumLock);
  
  	/*
  	 * Terminate an unneeded worker that has been fetched from the list of
--- 1082,1090 ----
  	int                     idle_workers_required;
  	int                     job_workers_required;
  
! 	LWLockAcquire(WorkerInfoLock, LW_SHARED);
! 	worker_slots_available = (CoordinatorShmem->co_freeWorkers != NULL);
! 	LWLockRelease(WorkerInfoLock);
  
  	/*
  	 * Terminate an unneeded worker that has been fetched from the list of
*************** CoordinatorCanLaunchWorker(TimestampTz c
*** 1213,1226 ****
  		 * other worker failed while starting up.
  		 */
  
! 		LWLockAcquire(AutovacuumLock, LW_SHARED);
  
! 		can_launch = (AutoVacuumShmem->av_freeWorkers != NULL);
  
! 		if (AutoVacuumShmem->av_startingWorker != NULL)
  		{
  			int			waittime;
! 			WorkerInfo	worker = AutoVacuumShmem->av_startingWorker;
  
  #ifdef COORDINATOR_DEBUG
  			elog(DEBUG5, "Coordinator: another worker is starting...");
--- 1210,1223 ----
  		 * other worker failed while starting up.
  		 */
  
! 		LWLockAcquire(WorkerInfoLock, LW_SHARED);
  
! 		can_launch = (CoordinatorShmem->co_freeWorkers != NULL);
  
! 		if (CoordinatorShmem->co_startingWorker != NULL)
  		{
  			int			waittime;
! 			WorkerInfo	worker = CoordinatorShmem->co_startingWorker;
  
  #ifdef COORDINATOR_DEBUG
  			elog(DEBUG5, "Coordinator: another worker is starting...");
*************** CoordinatorCanLaunchWorker(TimestampTz c
*** 1237,1243 ****
  			 * pointer before trying to connect.  Problems detected by the
  			 * postmaster (like fork() failure) are also reported and handled
  			 * differently.  The only problems that may cause this code to
! 			 * fire are errors in the earlier sections of AutoVacWorkerMain,
  			 * before the worker removes the WorkerInfo from the
  			 * startingWorker pointer.
  			 */
--- 1234,1240 ----
  			 * pointer before trying to connect.  Problems detected by the
  			 * postmaster (like fork() failure) are also reported and handled
  			 * differently.  The only problems that may cause this code to
! 			 * fire are errors in the earlier sections of BackgroundWorkerMain,
  			 * before the worker removes the WorkerInfo from the
  			 * startingWorker pointer.
  			 */
*************** CoordinatorCanLaunchWorker(TimestampTz c
*** 1245,1252 ****
  			if (TimestampDifferenceExceeds(worker->wi_launchtime, current_time,
  										   waittime))
  			{
! 				LWLockRelease(AutovacuumLock);
! 				LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  
  				/*
  				 * No other process can put a worker in starting mode, so if
--- 1242,1249 ----
  			if (TimestampDifferenceExceeds(worker->wi_launchtime, current_time,
  										   waittime))
  			{
! 				LWLockRelease(WorkerInfoLock);
! 				LWLockAcquire(WorkerInfoLock, LW_EXCLUSIVE);
  
  				/*
  				 * No other process can put a worker in starting mode, so if
*************** CoordinatorCanLaunchWorker(TimestampTz c
*** 1254,1276 ****
  				 * we assume it's the same one we saw above (so we don't
  				 * recheck the launch time).
  				 */
! 				if (AutoVacuumShmem->av_startingWorker != NULL)
  				{
! 					worker = AutoVacuumShmem->av_startingWorker;
  					worker->wi_dboid = InvalidOid;
  					worker->wi_tableoid = InvalidOid;
  					worker->wi_backend_id = InvalidBackendId;
  					worker->wi_launchtime = 0;
! 					worker->wi_links.next = (SHM_QUEUE *) AutoVacuumShmem->av_freeWorkers;
! 					AutoVacuumShmem->av_freeWorkers = worker;
! 					AutoVacuumShmem->av_startingWorker = NULL;
  					elog(WARNING, "worker took too long to start; cancelled");
  				}
  			}
  			else
  				can_launch = false;
  		}
! 		LWLockRelease(AutovacuumLock);	/* either shared or exclusive */
  	}
  
  	return can_launch;
--- 1251,1273 ----
  				 * we assume it's the same one we saw above (so we don't
  				 * recheck the launch time).
  				 */
! 				if (CoordinatorShmem->co_startingWorker != NULL)
  				{
! 					worker = CoordinatorShmem->co_startingWorker;
  					worker->wi_dboid = InvalidOid;
  					worker->wi_tableoid = InvalidOid;
  					worker->wi_backend_id = InvalidBackendId;
  					worker->wi_launchtime = 0;
! 					worker->wi_links.next = (SHM_QUEUE *) CoordinatorShmem->co_freeWorkers;
! 					CoordinatorShmem->co_freeWorkers = worker;
! 					CoordinatorShmem->co_startingWorker = NULL;
  					elog(WARNING, "worker took too long to start; cancelled");
  				}
  			}
  			else
  				can_launch = false;
  		}
! 		LWLockRelease(WorkerInfoLock);	/* either shared or exclusive */
  	}
  
  	return can_launch;
*************** static void
*** 1345,1351 ****
   * cause a long sleep, which will be interrupted when a worker exits.
   */
  static void
! launcher_determine_sleep(bool can_launch, bool recursing, struct timespec *nap)
  {
  	Dlelem	   *elem;
  
--- 1342,1348 ----
   * cause a long sleep, which will be interrupted when a worker exits.
   */
  static void
! coordinator_determine_sleep(bool can_launch, bool recursing, struct timespec *nap)
  {
  	Dlelem	   *elem;
  
*************** launcher_determine_sleep(bool can_launch
*** 1394,1400 ****
  	if (nap->tv_sec == 0 && nap->tv_nsec == 0 && !recursing)
  	{
  		rebuild_database_list(InvalidOid);
! 		launcher_determine_sleep(can_launch, true, nap);
  		return;
  	}
  
--- 1391,1397 ----
  	if (nap->tv_sec == 0 && nap->tv_nsec == 0 && !recursing)
  	{
  		rebuild_database_list(InvalidOid);
! 		coordinator_determine_sleep(can_launch, true, nap);
  		return;
  	}
  
*************** rebuild_database_list(Oid newdb)
*** 1435,1441 ****
  	/* use fresh stats */
  	autovac_refresh_stats();
  
! 	newcxt = AllocSetContextCreate(AutovacLauncherMemCxt,
  								   "AV dblist",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_INITSIZE,
--- 1432,1438 ----
  	/* use fresh stats */
  	autovac_refresh_stats();
  
! 	newcxt = AllocSetContextCreate(CoordinatorMemCxt,
  								   "AV dblist",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_INITSIZE,
*************** rebuild_database_list(Oid newdb)
*** 1576,1582 ****
  		/*
  		 * Determine the time interval between databases in the schedule. If
  		 * we see that the configured naptime would take us to sleep times
! 		 * lower than our min sleep time (which launcher_determine_sleep is
  		 * coded not to allow), silently use a larger naptime (but don't touch
  		 * the GUC variable).
  		 */
--- 1573,1579 ----
  		/*
  		 * Determine the time interval between databases in the schedule. If
  		 * we see that the configured naptime would take us to sleep times
! 		 * lower than our min sleep time (which coordinator_determine_sleep is
  		 * coded not to allow), silently use a larger naptime (but don't touch
  		 * the GUC variable).
  		 */
*************** db_comparator(const void *a, const void 
*** 1626,1632 ****
  /*
   * do_start_worker
   *
!  * Bare-bones procedure for starting an autovacuum worker from the
   * coordinator. It sets up shared memory stuff and signals the postmaster to
   * start a worker.
   */
--- 1623,1629 ----
  /*
   * do_start_worker
   *
!  * Bare-bones procedure for starting a background worker from the
   * coordinator. It sets up shared memory stuff and signals the postmaster to
   * start a worker.
   */
*************** do_start_worker(Oid dboid)
*** 1641,1668 ****
  	elog(DEBUG3, "Coordinator: requesting worker for database %d.", dboid);
  #endif
  
! 	LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  
  	/*
  	 * Get a worker entry from the freelist.  We checked above, so there
  	 * really should be a free slot -- complain very loudly if there
  	 * isn't.
  	 */
! 	worker = AutoVacuumShmem->av_freeWorkers;
  	if (worker == NULL)
  		elog(FATAL, "no free worker found");
  
! 	AutoVacuumShmem->av_freeWorkers = (WorkerInfo) worker->wi_links.next;
  
  	worker->wi_dboid = dboid;
  	worker->wi_backend_id = InvalidBackendId;
  	worker->wi_launchtime = GetCurrentTimestamp();
  
! 	AutoVacuumShmem->av_startingWorker = worker;
  
! 	LWLockRelease(AutovacuumLock);
  
! 	SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER);
  }
  
  /*
--- 1638,1665 ----
  	elog(DEBUG3, "Coordinator: requesting worker for database %d.", dboid);
  #endif
  
! 	LWLockAcquire(WorkerInfoLock, LW_EXCLUSIVE);
  
  	/*
  	 * Get a worker entry from the freelist.  We checked above, so there
  	 * really should be a free slot -- complain very loudly if there
  	 * isn't.
  	 */
! 	worker = CoordinatorShmem->co_freeWorkers;
  	if (worker == NULL)
  		elog(FATAL, "no free worker found");
  
! 	CoordinatorShmem->co_freeWorkers = (WorkerInfo) worker->wi_links.next;
  
  	worker->wi_dboid = dboid;
  	worker->wi_backend_id = InvalidBackendId;
  	worker->wi_launchtime = GetCurrentTimestamp();
  
! 	CoordinatorShmem->co_startingWorker = worker;
  
! 	LWLockRelease(WorkerInfoLock);
  
! 	SendPostmasterSignal(PMSIGNAL_START_BGWORKER);
  }
  
  /*
*************** autovacuum_select_database(void)
*** 1689,1701 ****
  				oldcxt;
  
  	/* return quickly when there are no free workers */
! 	LWLockAcquire(AutovacuumLock, LW_SHARED);
! 	if (AutoVacuumShmem->av_freeWorkers == NULL)
  	{
! 		LWLockRelease(AutovacuumLock);
  		return InvalidOid;
  	}
! 	LWLockRelease(AutovacuumLock);
  
  	/*
  	 * Create and switch to a temporary context to avoid leaking the memory
--- 1686,1698 ----
  				oldcxt;
  
  	/* return quickly when there are no free workers */
! 	LWLockAcquire(WorkerInfoLock, LW_SHARED);
! 	if (CoordinatorShmem->co_freeWorkers == NULL)
  	{
! 		LWLockRelease(WorkerInfoLock);
  		return InvalidOid;
  	}
! 	LWLockRelease(WorkerInfoLock);
  
  	/*
  	 * Create and switch to a temporary context to avoid leaking the memory
*************** avl_sigterm_handler(SIGNAL_ARGS)
*** 1920,1937 ****
  
  #ifdef EXEC_BACKEND
  /*
!  * forkexec routines for the autovacuum worker.
   *
   * Format up the arglist, then fork and exec.
   */
  static pid_t
! avworker_forkexec(void)
  {
  	char	   *av[10];
  	int			ac = 0;
  
  	av[ac++] = "postgres";
! 	av[ac++] = "--forkavworker";
  	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
  	av[ac] = NULL;
  
--- 1917,1934 ----
  
  #ifdef EXEC_BACKEND
  /*
!  * forkexec routines for background workers.
   *
   * Format up the arglist, then fork and exec.
   */
  static pid_t
! bgworker_forkexec(void)
  {
  	char	   *av[10];
  	int			ac = 0;
  
  	av[ac++] = "postgres";
! 	av[ac++] = "--forkbgworker";
  	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
  	av[ac] = NULL;
  
*************** void
*** 1944,1974 ****
   * We need this set from the outside, before InitProcess is called
   */
  void
! AutovacuumWorkerIAm(void)
  {
! 	am_autovacuum_worker = true;
  }
  #endif
  
  /*
!  * Main entry point for autovacuum worker process.
   *
   * This code is heavily based on pgarch.c, q.v.
   */
  int
! StartAutoVacWorker(void)
  {
  	pid_t		worker_pid;
  
  #ifdef EXEC_BACKEND
! 	switch ((worker_pid = avworker_forkexec()))
  #else
  	switch ((worker_pid = fork_process()))
  #endif
  	{
  		case -1:
  			ereport(LOG,
! 					(errmsg("could not fork autovacuum worker process: %m")));
  			return 0;
  
  #ifndef EXEC_BACKEND
--- 1941,1971 ----
   * We need this set from the outside, before InitProcess is called
   */
  void
! BackgroundWorkerIAm(void)
  {
! 	am_background_worker = true;
  }
  #endif
  
  /*
!  * Main entry point for a background worker process.
   *
   * This code is heavily based on pgarch.c, q.v.
   */
  int
! StartBackgroundWorker(void)
  {
  	pid_t		worker_pid;
  
  #ifdef EXEC_BACKEND
! 	switch ((worker_pid = bgworker_forkexec()))
  #else
  	switch ((worker_pid = fork_process()))
  #endif
  	{
  		case -1:
  			ereport(LOG,
! 					(errmsg("could not fork background worker process: %m")));
  			return 0;
  
  #ifndef EXEC_BACKEND
*************** StartAutoVacWorker(void)
*** 1980,1986 ****
  			/* Lose the postmaster's on-exit routines */
  			on_exit_reset();
  
! 			AutoVacWorkerMain(0, NULL);
  			break;
  #endif
  		default:
--- 1977,1983 ----
  			/* Lose the postmaster's on-exit routines */
  			on_exit_reset();
  
! 			BackgroundWorkerMain(0, NULL);
  			break;
  #endif
  		default:
*************** StartAutoVacWorker(void)
*** 1995,2001 ****
   * add_as_idle_worker
   *
   * Marks the current worker as idle by adding it to the database's list of
!  * idle worker backends. The caller is expected to hold the AutovacuumLock.
   */
  static void
  add_as_idle_worker(Oid dbid, bool inc_connected_count)
--- 1992,1998 ----
   * add_as_idle_worker
   *
   * Marks the current worker as idle by adding it to the database's list of
!  * idle worker backends. The caller is expected to hold the WorkerInfoLock.
   */
  static void
  add_as_idle_worker(Oid dbid, bool inc_connected_count)
*************** bgworker_job_initialize(worker_state new
*** 2053,2060 ****
  	 * one for other book-keeping of the various background jobs across
  	 * transactions, for example to keep the list of relations to vacuum.
  	 */
! 	Assert(AutovacWorkerMemCxt == NULL);
! 	AutovacWorkerMemCxt = AllocSetContextCreate(TopMemoryContext,
  										   "Background Worker",
  										   ALLOCSET_DEFAULT_MINSIZE,
  										   ALLOCSET_DEFAULT_INITSIZE,
--- 2050,2057 ----
  	 * one for other book-keeping of the various background jobs across
  	 * transactions, for example to keep the list of relations to vacuum.
  	 */
! 	Assert(BgWorkerMemCxt == NULL);
! 	BgWorkerMemCxt = AllocSetContextCreate(TopMemoryContext,
  										   "Background Worker",
  										   ALLOCSET_DEFAULT_MINSIZE,
  										   ALLOCSET_DEFAULT_INITSIZE,
*************** bgworker_job_initialize(worker_state new
*** 2066,2072 ****
  										   ALLOCSET_DEFAULT_INITSIZE,
  										   ALLOCSET_DEFAULT_MAXSIZE);
  
! 	MemoryContextSwitchTo(AutovacWorkerMemCxt);
  }
  
  /*
--- 2063,2069 ----
  										   ALLOCSET_DEFAULT_INITSIZE,
  										   ALLOCSET_DEFAULT_MAXSIZE);
  
! 	MemoryContextSwitchTo(BgWorkerMemCxt);
  }
  
  /*
*************** bgworker_reset(void)
*** 2091,2097 ****
  void
  bgworker_reset(void)
  {
! 	BackendId AutovacuumLauncherId;
  	IMessage *msg;
  
  	elog(DEBUG5, "bg worker [%d/%d]: resetting",
--- 2088,2094 ----
  void
  bgworker_reset(void)
  {
! 	BackendId CoordinatorId;
  	IMessage *msg;
  
  	elog(DEBUG5, "bg worker [%d/%d]: resetting",
*************** bgworker_reset(void)
*** 2105,2130 ****
  	set_ps_display("bg worker: idle", false);
  
  	/* clean up memory contexts */
! 	Assert(AutovacWorkerMemCxt);
  	MemoryContextSwitchTo(TopMemoryContext);
! 	MemoryContextDelete(AutovacWorkerMemCxt);
! 	AutovacWorkerMemCxt = NULL;
  	MemoryContextDelete(MessageContext);
  	MessageContext = NULL;
  
! 	/* Reset the the process-local cleanup handler state. */
  	BgWorkerCleanupInProgress = false;
  
  	/* propagate as idle worker, inform the coordinator */
! 	LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  	add_as_idle_worker(MyDatabaseId, false);
! 	LWLockRelease(AutovacuumLock);
  
! 	AutovacuumLauncherId = GetAutovacuumLauncherId();
! 	if (AutovacuumLauncherId != InvalidBackendId)
  	{
  		msg = IMessageCreate(IMSGT_READY, 0);
! 		IMessageActivate(msg, AutovacuumLauncherId);
  	}
  	else
  		elog(WARNING, "bg worker [%d/%d]: no coordinator?!?",
--- 2102,2127 ----
  	set_ps_display("bg worker: idle", false);
  
  	/* clean up memory contexts */
! 	Assert(BgWorkerMemCxt);
  	MemoryContextSwitchTo(TopMemoryContext);
! 	MemoryContextDelete(BgWorkerMemCxt);
! 	BgWorkerMemCxt = NULL;
  	MemoryContextDelete(MessageContext);
  	MessageContext = NULL;
  
! 	/* Reset the process-local cleanup handler state. */
  	BgWorkerCleanupInProgress = false;
  
  	/* propagate as idle worker, inform the coordinator */
! 	LWLockAcquire(WorkerInfoLock, LW_EXCLUSIVE);
  	add_as_idle_worker(MyDatabaseId, false);
! 	LWLockRelease(WorkerInfoLock);
  
! 	CoordinatorId = GetCoordinatorId();
! 	if (CoordinatorId != InvalidBackendId)
  	{
  		msg = IMessageCreate(IMSGT_READY, 0);
! 		IMessageActivate(msg, CoordinatorId);
  	}
  	else
  		elog(WARNING, "bg worker [%d/%d]: no coordinator?!?",
*************** bgworker_job_failed(int errcode)
*** 2164,2173 ****
  }
  
  /*
!  * AutoVacWorkerMain
   */
  NON_EXEC_STATIC void
! AutoVacWorkerMain(int argc, char *argv[])
  {
  	sigjmp_buf	local_sigjmp_buf;
  	BackendId   coordinator_id;
--- 2161,2170 ----
  }
  
  /*
!  * BackgroundWorkerMain
   */
  NON_EXEC_STATIC void
! BackgroundWorkerMain(int argc, char *argv[])
  {
  	sigjmp_buf	local_sigjmp_buf;
  	BackendId   coordinator_id;
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 2178,2184 ****
  
  	/* we are a postmaster subprocess now */
  	IsUnderPostmaster = true;
! 	am_autovacuum_worker = true;
  
  	/* reset MyProcPid */
  	MyProcPid = getpid();
--- 2175,2181 ----
  
  	/* we are a postmaster subprocess now */
  	IsUnderPostmaster = true;
! 	am_background_worker = true;
  
  	/* reset MyProcPid */
  	MyProcPid = getpid();
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 2187,2193 ****
  	MyStartTime = time(NULL);
  
  	/* Identify myself via ps */
! 	init_ps_display("autovacuum worker process", "", "", "");
  
  	SetProcessingMode(InitProcessing);
  
--- 2184,2190 ----
  	MyStartTime = time(NULL);
  
  	/* Identify myself via ps */
! 	init_ps_display("background worker process", "", "", "");
  
  	SetProcessingMode(InitProcessing);
  
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 2267,2275 ****
  	PG_SETMASK(&UnBlockSig);
  
  	/*
! 	 * Force zero_damaged_pages OFF in the autovac process, even if it is set
! 	 * in postgresql.conf.	We don't really want such a dangerous option being
! 	 * applied non-interactively.
  	 */
  	SetConfigOption("zero_damaged_pages", "false", PGC_SUSET, PGC_S_OVERRIDE);
  
--- 2264,2272 ----
  	PG_SETMASK(&UnBlockSig);
  
  	/*
! 	 * Force zero_damaged_pages OFF in the background worker, even if it is
! 	 * set in postgresql.conf.	We don't really want such a dangerous option
! 	 * being applied non-interactively.
  	 */
  	SetConfigOption("zero_damaged_pages", "false", PGC_SUSET, PGC_S_OVERRIDE);
  
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 2282,2288 ****
  	/*
  	 * Get the info about the database we're going to work on.
  	 */
! 	LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  
  	/*
  	 * beware of startingWorker being INVALID; this should normally not
--- 2279,2285 ----
  	/*
  	 * Get the info about the database we're going to work on.
  	 */
! 	LWLockAcquire(WorkerInfoLock, LW_EXCLUSIVE);
  
  	/*
  	 * beware of startingWorker being INVALID; this should normally not
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 2290,2304 ****
  	 * launcher might have decided to remove it from the queue and start
  	 * again.
  	 */
! 	if (AutoVacuumShmem->av_startingWorker == NULL)
  	{
  		/* no worker entry for me, go away */
! 		elog(WARNING, "autovacuum worker started without a worker entry");
! 		LWLockRelease(AutovacuumLock);
  		proc_exit(0);
  	}
  
! 	MyWorkerInfo = AutoVacuumShmem->av_startingWorker;
  	dbid = MyWorkerInfo->wi_dboid;
  
  	/* FIXME: indentation */
--- 2287,2301 ----
  	 * launcher might have decided to remove it from the queue and start
  	 * again.
  	 */
! 	if (CoordinatorShmem->co_startingWorker == NULL)
  	{
  		/* no worker entry for me, go away */
! 		elog(WARNING, "background worker started without a worker entry");
! 		LWLockRelease(WorkerInfoLock);
  		proc_exit(0);
  	}
  
! 	MyWorkerInfo = CoordinatorShmem->co_startingWorker;
  	dbid = MyWorkerInfo->wi_dboid;
  
  	/* FIXME: indentation */
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 2308,2317 ****
  		 * remove from the "starting" pointer, so that the launcher can start
  		 * a new worker if required
  		 */
! 		AutoVacuumShmem->av_startingWorker = NULL;
  
! 		coordinator_id = AutoVacuumShmem->av_launcherid;
! 		LWLockRelease(AutovacuumLock);
  
  		on_shmem_exit(FreeWorkerInfo, 0);
  
--- 2305,2314 ----
  		 * remove from the "starting" pointer, so that the launcher can start
  		 * a new worker if required
  		 */
! 		CoordinatorShmem->co_startingWorker = NULL;
  
! 		coordinator_id = CoordinatorShmem->co_coordinatorid;
! 		LWLockRelease(WorkerInfoLock);
  
  		on_shmem_exit(FreeWorkerInfo, 0);
  
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 2336,2342 ****
  		set_ps_display("bg worker: idle", false);
  	}
  
! 	AutovacWorkerMemCxt = NULL;
  
  	MyWorkerInfo->wi_backend_id = MyBackendId;
  	MyWorkerInfo->wi_state = WS_IDLE;
--- 2333,2339 ----
  		set_ps_display("bg worker: idle", false);
  	}
  
! 	BgWorkerMemCxt = NULL;
  
  	MyWorkerInfo->wi_backend_id = MyBackendId;
  	MyWorkerInfo->wi_state = WS_IDLE;
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 2351,2359 ****
  	 * set MyProc->databaseId in InitPostgres, so the coordinator can
  	 * determine which database we are connected to.
  	 */
! 	LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  	add_as_idle_worker(dbid, true);
! 	LWLockRelease(AutovacuumLock);
  
  	/* register with the coordinator */
  	if (coordinator_id != InvalidBackendId)
--- 2348,2356 ----
  	 * set MyProc->databaseId in InitPostgres, so the coordinator can
  	 * determine which database we are connected to.
  	 */
! 	LWLockAcquire(WorkerInfoLock, LW_EXCLUSIVE);
  	add_as_idle_worker(dbid, true);
! 	LWLockRelease(WorkerInfoLock);
  
  	/* register with the coordinator */
  	if (coordinator_id != InvalidBackendId)
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 2414,2423 ****
  					/*
  					 * Add ourselves to the list of runningWorkers
  					 */
! 					LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
! 					SHMQueueInsertBefore(&AutoVacuumShmem->av_runningWorkers,
  										 &MyWorkerInfo->wi_links);
! 					LWLockRelease(AutovacuumLock);
  
  					/* do an appropriate amount of work */
  					do_autovacuum();
--- 2411,2420 ----
  					/*
  					 * Add ourselves to the list of runningWorkers
  					 */
! 					LWLockAcquire(WorkerInfoLock, LW_EXCLUSIVE);
! 					SHMQueueInsertBefore(&CoordinatorShmem->co_runningWorkers,
  										 &MyWorkerInfo->wi_links);
! 					LWLockRelease(WorkerInfoLock);
  
  					/* do an appropriate amount of work */
  					do_autovacuum();
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 2426,2434 ****
  					 * Remove ourselves from the list of runningWorkers and
  					 * mark as available background worker.
  					 */
! 					LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  					SHMQueueDelete(&MyWorkerInfo->wi_links);
! 					LWLockRelease(AutovacuumLock);
  
  					bgworker_job_completed();
  					break;
--- 2423,2431 ----
  					 * Remove ourselves from the list of runningWorkers and
  					 * mark as available background worker.
  					 */
! 					LWLockAcquire(WorkerInfoLock, LW_EXCLUSIVE);
  					SHMQueueDelete(&MyWorkerInfo->wi_links);
! 					LWLockRelease(WorkerInfoLock);
  
  					bgworker_job_completed();
  					break;
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 2452,2458 ****
  			ErrorData *errdata;
  			MemoryContext ecxt;
  
! 			ecxt = MemoryContextSwitchTo(AutovacWorkerMemCxt);
  			errdata = CopyErrorData();
  
  			elog(WARNING, "bg worker [%d/%d]: caught error '%s' in %s:%d, state %s",
--- 2449,2455 ----
  			ErrorData *errdata;
  			MemoryContext ecxt;
  
! 			ecxt = MemoryContextSwitchTo(BgWorkerMemCxt);
  			errdata = CopyErrorData();
  
  			elog(WARNING, "bg worker [%d/%d]: caught error '%s' in %s:%d, state %s",
*************** FreeWorkerInfo(int code, Datum arg)
*** 2505,2516 ****
  	co_database *codb;
  	if (MyWorkerInfo != NULL)
  	{
! 		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  
  		if (!SHMQueueIsDetached(&MyWorkerInfo->wi_links))
  			SHMQueueDelete(&MyWorkerInfo->wi_links);
  
! 		MyWorkerInfo->wi_links.next = (SHM_QUEUE *) AutoVacuumShmem->av_freeWorkers;
  		MyWorkerInfo->wi_dboid = InvalidOid;
  		MyWorkerInfo->wi_tableoid = InvalidOid;
  		MyWorkerInfo->wi_backend_id = InvalidBackendId;
--- 2502,2513 ----
  	co_database *codb;
  	if (MyWorkerInfo != NULL)
  	{
! 		LWLockAcquire(WorkerInfoLock, LW_EXCLUSIVE);
  
  		if (!SHMQueueIsDetached(&MyWorkerInfo->wi_links))
  			SHMQueueDelete(&MyWorkerInfo->wi_links);
  
! 		MyWorkerInfo->wi_links.next = (SHM_QUEUE *) CoordinatorShmem->co_freeWorkers;
  		MyWorkerInfo->wi_dboid = InvalidOid;
  		MyWorkerInfo->wi_tableoid = InvalidOid;
  		MyWorkerInfo->wi_backend_id = InvalidBackendId;
*************** FreeWorkerInfo(int code, Datum arg)
*** 2518,2524 ****
  		MyWorkerInfo->wi_cost_delay = 0;
  		MyWorkerInfo->wi_cost_limit = 0;
  		MyWorkerInfo->wi_cost_limit_base = 0;
! 		AutoVacuumShmem->av_freeWorkers = MyWorkerInfo;
  		/* not mine anymore */
  		MyWorkerInfo = NULL;
  
--- 2515,2521 ----
  		MyWorkerInfo->wi_cost_delay = 0;
  		MyWorkerInfo->wi_cost_limit = 0;
  		MyWorkerInfo->wi_cost_limit_base = 0;
! 		CoordinatorShmem->co_freeWorkers = MyWorkerInfo;
  		/* not mine anymore */
  		MyWorkerInfo = NULL;
  
*************** FreeWorkerInfo(int code, Datum arg)
*** 2529,2535 ****
  		codb->codb_num_connected_workers--;
  		LWLockRelease(CoordinatorDatabasesLock);
  
! 		LWLockRelease(AutovacuumLock);
  	}
  }
  
--- 2526,2532 ----
  		codb->codb_num_connected_workers--;
  		LWLockRelease(CoordinatorDatabasesLock);
  
! 		LWLockRelease(WorkerInfoLock);
  	}
  }
  
*************** AutoVacuumUpdateDelay(void)
*** 2551,2557 ****
   * autovac_balance_cost
   *		Recalculate the cost limit setting for each active workers.
   *
!  * Caller must hold the AutovacuumLock in exclusive mode.
   */
  static void
  autovac_balance_cost(void)
--- 2548,2554 ----
   * autovac_balance_cost
   *		Recalculate the cost limit setting for each active workers.
   *
!  * Caller must hold the WorkerInfoLock in exclusive mode.
   */
  static void
  autovac_balance_cost(void)
*************** autovac_balance_cost(void)
*** 2575,2582 ****
  
  	/* caculate the total base cost limit of active workers */
  	cost_total = 0.0;
! 	worker = (WorkerInfo) SHMQueueNext(&AutoVacuumShmem->av_runningWorkers,
! 									   &AutoVacuumShmem->av_runningWorkers,
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
--- 2572,2579 ----
  
  	/* caculate the total base cost limit of active workers */
  	cost_total = 0.0;
! 	worker = (WorkerInfo) SHMQueueNext(&CoordinatorShmem->co_runningWorkers,
! 									   &CoordinatorShmem->co_runningWorkers,
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
*************** autovac_balance_cost(void)
*** 2585,2591 ****
  			cost_total +=
  				(double) worker->wi_cost_limit_base / worker->wi_cost_delay;
  
! 		worker = (WorkerInfo) SHMQueueNext(&AutoVacuumShmem->av_runningWorkers,
  										   &worker->wi_links,
  										 offsetof(WorkerInfoData, wi_links));
  	}
--- 2582,2588 ----
  			cost_total +=
  				(double) worker->wi_cost_limit_base / worker->wi_cost_delay;
  
! 		worker = (WorkerInfo) SHMQueueNext(&CoordinatorShmem->co_runningWorkers,
  										   &worker->wi_links,
  										 offsetof(WorkerInfoData, wi_links));
  	}
*************** autovac_balance_cost(void)
*** 2598,2605 ****
  	 * limit to autovacuum_vacuum_cost_limit.
  	 */
  	cost_avail = (double) vac_cost_limit / vac_cost_delay;
! 	worker = (WorkerInfo) SHMQueueNext(&AutoVacuumShmem->av_runningWorkers,
! 									   &AutoVacuumShmem->av_runningWorkers,
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
--- 2595,2602 ----
  	 * limit to autovacuum_vacuum_cost_limit.
  	 */
  	cost_avail = (double) vac_cost_limit / vac_cost_delay;
! 	worker = (WorkerInfo) SHMQueueNext(&CoordinatorShmem->co_runningWorkers,
! 									   &CoordinatorShmem->co_runningWorkers,
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
*************** autovac_balance_cost(void)
*** 2620,2626 ****
  				 worker->wi_tableoid, worker->wi_cost_limit, worker->wi_cost_delay);
  		}
  
! 		worker = (WorkerInfo) SHMQueueNext(&AutoVacuumShmem->av_runningWorkers,
  										   &worker->wi_links,
  										 offsetof(WorkerInfoData, wi_links));
  	}
--- 2617,2623 ----
  				 worker->wi_tableoid, worker->wi_cost_limit, worker->wi_cost_delay);
  		}
  
! 		worker = (WorkerInfo) SHMQueueNext(&CoordinatorShmem->co_runningWorkers,
  										   &worker->wi_links,
  										 offsetof(WorkerInfoData, wi_links));
  	}
*************** autovac_balance_cost(void)
*** 2630,2636 ****
   * get_database_list
   *		Return a list of all databases found in pg_database.
   *
!  * Note: this is the only function in which the autovacuum launcher uses a
   * transaction.  Although we aren't attached to any particular database and
   * therefore can't access most catalogs, we do have enough infrastructure
   * to do a seqscan on pg_database.
--- 2627,2633 ----
   * get_database_list
   *		Return a list of all databases found in pg_database.
   *
!  * Note: this is the only function in which the coordinator uses a
   * transaction.  Although we aren't attached to any particular database and
   * therefore can't access most catalogs, we do have enough infrastructure
   * to do a seqscan on pg_database.
*************** get_database_list(void)
*** 2653,2660 ****
  	StartTransactionCommand();
  	(void) GetTransactionSnapshot();
  
! 	/* Allocate our results in AutovacLauncherMemCxt, not transaction context */
! 	MemoryContextSwitchTo(AutovacLauncherMemCxt);
  
  	rel = heap_open(DatabaseRelationId, AccessShareLock);
  	scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
--- 2650,2657 ----
  	StartTransactionCommand();
  	(void) GetTransactionSnapshot();
  
! 	/* Allocate our results in CoordinatorMemCxt, not transaction context */
! 	MemoryContextSwitchTo(CoordinatorMemCxt);
  
  	rel = heap_open(DatabaseRelationId, AccessShareLock);
  	scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
*************** do_autovacuum(void)
*** 2752,2758 ****
  					NameStr(dbForm->datname))));
  
  	/* StartTransactionCommand changed elsewhere */
! 	MemoryContextSwitchTo(AutovacWorkerMemCxt);
  
  	/* The database hash where pgstat keeps shared relations */
  	shared = pgstat_fetch_stat_dbentry(InvalidOid);
--- 2749,2755 ----
  					NameStr(dbForm->datname))));
  
  	/* StartTransactionCommand changed elsewhere */
! 	MemoryContextSwitchTo(BgWorkerMemCxt);
  
  	/* The database hash where pgstat keeps shared relations */
  	shared = pgstat_fetch_stat_dbentry(InvalidOid);
*************** do_autovacuum(void)
*** 2966,2972 ****
  	 * create a memory context to act as fake PortalContext, so that the
  	 * contexts created in the vacuum code are cleaned up for each table.
  	 */
! 	PortalContext = AllocSetContextCreate(AutovacWorkerMemCxt,
  										  "Autovacuum Portal",
  										  ALLOCSET_DEFAULT_INITSIZE,
  										  ALLOCSET_DEFAULT_MINSIZE,
--- 2963,2969 ----
  	 * create a memory context to act as fake PortalContext, so that the
  	 * contexts created in the vacuum code are cleaned up for each table.
  	 */
! 	PortalContext = AllocSetContextCreate(BgWorkerMemCxt,
  										  "Autovacuum Portal",
  										  ALLOCSET_DEFAULT_INITSIZE,
  										  ALLOCSET_DEFAULT_MINSIZE,
*************** do_autovacuum(void)
*** 2986,3004 ****
  
  		/*
  		 * hold schedule lock from here until we're sure that this table still
! 		 * needs vacuuming.  We also need the AutovacuumLock to walk the
  		 * worker array, but we'll let go of that one quickly.
  		 */
! 		LWLockAcquire(AutovacuumScheduleLock, LW_EXCLUSIVE);
! 		LWLockAcquire(AutovacuumLock, LW_SHARED);
  
  		/*
  		 * Check whether the table is being vacuumed concurrently by another
  		 * worker.
  		 */
  		skipit = false;
! 		worker = (WorkerInfo) SHMQueueNext(&AutoVacuumShmem->av_runningWorkers,
! 										 &AutoVacuumShmem->av_runningWorkers,
  										 offsetof(WorkerInfoData, wi_links));
  		while (worker)
  		{
--- 2983,3001 ----
  
  		/*
  		 * hold schedule lock from here until we're sure that this table still
! 		 * needs vacuuming.  We also need the WorkerInfoLock to walk the
  		 * worker array, but we'll let go of that one quickly.
  		 */
! 		LWLockAcquire(WorkerScheduleLock, LW_EXCLUSIVE);
! 		LWLockAcquire(WorkerInfoLock, LW_SHARED);
  
  		/*
  		 * Check whether the table is being vacuumed concurrently by another
  		 * worker.
  		 */
  		skipit = false;
! 		worker = (WorkerInfo) SHMQueueNext(&CoordinatorShmem->co_runningWorkers,
! 										 &CoordinatorShmem->co_runningWorkers,
  										 offsetof(WorkerInfoData, wi_links));
  		while (worker)
  		{
*************** do_autovacuum(void)
*** 3017,3030 ****
  			}
  
  	next_worker:
! 			worker = (WorkerInfo) SHMQueueNext(&AutoVacuumShmem->av_runningWorkers,
  											   &worker->wi_links,
  										 offsetof(WorkerInfoData, wi_links));
  		}
! 		LWLockRelease(AutovacuumLock);
  		if (skipit)
  		{
! 			LWLockRelease(AutovacuumScheduleLock);
  			continue;
  		}
  
--- 3014,3027 ----
  			}
  
  	next_worker:
! 			worker = (WorkerInfo) SHMQueueNext(&CoordinatorShmem->co_runningWorkers,
  											   &worker->wi_links,
  										 offsetof(WorkerInfoData, wi_links));
  		}
! 		LWLockRelease(WorkerInfoLock);
  		if (skipit)
  		{
! 			LWLockRelease(WorkerScheduleLock);
  			continue;
  		}
  
*************** do_autovacuum(void)
*** 3038,3049 ****
  		 * that somebody just finished vacuuming this table.  The window to
  		 * the race condition is not closed but it is very small.
  		 */
! 		MemoryContextSwitchTo(AutovacWorkerMemCxt);
  		tab = table_recheck_autovac(relid, table_toast_map, pg_class_desc);
  		if (tab == NULL)
  		{
  			/* someone else vacuumed the table, or it went away */
! 			LWLockRelease(AutovacuumScheduleLock);
  			continue;
  		}
  
--- 3035,3046 ----
  		 * that somebody just finished vacuuming this table.  The window to
  		 * the race condition is not closed but it is very small.
  		 */
! 		MemoryContextSwitchTo(BgWorkerMemCxt);
  		tab = table_recheck_autovac(relid, table_toast_map, pg_class_desc);
  		if (tab == NULL)
  		{
  			/* someone else vacuumed the table, or it went away */
! 			LWLockRelease(WorkerScheduleLock);
  			continue;
  		}
  
*************** do_autovacuum(void)
*** 3052,3065 ****
  		 * the lock so that other workers don't vacuum it concurrently.
  		 */
  		MyWorkerInfo->wi_tableoid = relid;
! 		LWLockRelease(AutovacuumScheduleLock);
  
  		/* Set the initial vacuum cost parameters for this table */
  		VacuumCostDelay = tab->at_vacuum_cost_delay;
  		VacuumCostLimit = tab->at_vacuum_cost_limit;
  
  		/* Last fixups before actually starting to work */
! 		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  
  		/* advertise my cost delay parameters for the balancing algorithm */
  		MyWorkerInfo->wi_cost_delay = tab->at_vacuum_cost_delay;
--- 3049,3062 ----
  		 * the lock so that other workers don't vacuum it concurrently.
  		 */
  		MyWorkerInfo->wi_tableoid = relid;
! 		LWLockRelease(WorkerScheduleLock);
  
  		/* Set the initial vacuum cost parameters for this table */
  		VacuumCostDelay = tab->at_vacuum_cost_delay;
  		VacuumCostLimit = tab->at_vacuum_cost_limit;
  
  		/* Last fixups before actually starting to work */
! 		LWLockAcquire(WorkerInfoLock, LW_EXCLUSIVE);
  
  		/* advertise my cost delay parameters for the balancing algorithm */
  		MyWorkerInfo->wi_cost_delay = tab->at_vacuum_cost_delay;
*************** do_autovacuum(void)
*** 3070,3076 ****
  		autovac_balance_cost();
  
  		/* done */
! 		LWLockRelease(AutovacuumLock);
  
  		/* clean up memory before each iteration */
  		MemoryContextResetAndDeleteChildren(PortalContext);
--- 3067,3073 ----
  		autovac_balance_cost();
  
  		/* done */
! 		LWLockRelease(WorkerInfoLock);
  
  		/* clean up memory before each iteration */
  		MemoryContextResetAndDeleteChildren(PortalContext);
*************** deleted:
*** 3147,3155 ****
  		pfree(tab);
  
  		/* remove my info from shared memory */
! 		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  		MyWorkerInfo->wi_tableoid = InvalidOid;
! 		LWLockRelease(AutovacuumLock);
  	}
  
  	/*
--- 3144,3152 ----
  		pfree(tab);
  
  		/* remove my info from shared memory */
! 		LWLockAcquire(WorkerInfoLock, LW_EXCLUSIVE);
  		MyWorkerInfo->wi_tableoid = InvalidOid;
! 		LWLockRelease(WorkerInfoLock);
  	}
  
  	/*
*************** autovac_init(void)
*** 3579,3622 ****
  }
  
  /*
!  * IsAutoVacuum functions
!  *		Return whether this is either a launcher autovacuum process or a worker
!  *		process.
   */
  bool
! IsAutoVacuumLauncherProcess(void)
  {
! 	return am_autovacuum_launcher;
  }
  
  bool
! IsAutoVacuumWorkerProcess(void)
  {
! 	return am_autovacuum_worker;
  }
  
  /*
!  * GetAutovacuumLauncherId
   *     Returns the backendId of the currently active coordinator process.
   */ 
  BackendId
! GetAutovacuumLauncherId(void)
  {
! 	BackendId AutovacuumLauncherId;
  
! 	LWLockAcquire(AutovacuumLock, LW_SHARED);
! 	AutovacuumLauncherId = AutoVacuumShmem->av_launcherid;
! 	LWLockRelease(AutovacuumLock);
     
! 	return AutovacuumLauncherId;
  }
  
  /*
!  * AutoVacuumShmemSize
   *		Compute space needed for autovacuum-related shared memory
   */
  Size
! AutoVacuumShmemSize(void)
  {
  	Size		size;
  
--- 3576,3619 ----
  }
  
  /*
!  * process identification functions
!  *		Return whether this is either a coordinator process or a background
!  *		worker process.
   */
  bool
! IsCoordinatorProcess(void)
  {
! 	return am_coordinator;
  }
  
  bool
! IsBackgroundWorkerProcess(void)
  {
! 	return am_background_worker;
  }
  
  /*
!  * GetCoordinatorId
   *     Returns the backendId of the currently active coordinator process.
   */ 
  BackendId
! GetCoordinatorId(void)
  {
! 	BackendId CoordinatorId;
  
! 	LWLockAcquire(WorkerInfoLock, LW_SHARED);
! 	CoordinatorId = CoordinatorShmem->co_coordinatorid;
! 	LWLockRelease(WorkerInfoLock);
     
! 	return CoordinatorId;
  }
  
  /*
!  * CoordinatorShmemSize
   *		Compute space needed for autovacuum-related shared memory
   */
  Size
! CoordinatorShmemSize(void)
  {
  	Size		size;
  
*************** AutoVacuumShmemSize(void)
*** 3624,3653 ****
  	 * Need the fixed struct and the array of WorkerInfoData, plus per
  	 * database entries in a hash. As we only track databases which have at
  	 * least one worker attached, we won't ever need more than
! 	 * autovacuum_max_workers entries.
  	 */
! 	size = sizeof(AutoVacuumShmemStruct);
  	size = MAXALIGN(size);
! 	size = add_size(size, mul_size(autovacuum_max_workers,
  								   sizeof(WorkerInfoData)));
! 	size = add_size(size, hash_estimate_size(autovacuum_max_workers,
  											 sizeof(co_database)));
  	return size;
  }
  
  /*
!  * AutoVacuumShmemInit
   *		Allocate and initialize autovacuum-related shared memory
   */
  void
! AutoVacuumShmemInit(void)
  {
  	HASHCTL     hctl;
  	bool		found;
  
! 	AutoVacuumShmem = (AutoVacuumShmemStruct *)
! 		ShmemInitStruct("AutoVacuum Data",
! 						AutoVacuumShmemSize(),
  						&found);
  
  	if (!IsUnderPostmaster)
--- 3621,3650 ----
  	 * Need the fixed struct and the array of WorkerInfoData, plus per
  	 * database entries in a hash. As we only track databases which have at
  	 * least one worker attached, we won't ever need more than
! 	 * max_background_workers entries.
  	 */
! 	size = sizeof(CoordinatorShmemStruct);
  	size = MAXALIGN(size);
! 	size = add_size(size, mul_size(max_background_workers,
  								   sizeof(WorkerInfoData)));
! 	size = add_size(size, hash_estimate_size(max_background_workers,
  											 sizeof(co_database)));
  	return size;
  }
  
  /*
!  * CoordinatorShmemInit
   *		Allocate and initialize autovacuum-related shared memory
   */
  void
! CoordinatorShmemInit(void)
  {
  	HASHCTL     hctl;
  	bool		found;
  
! 	CoordinatorShmem = (CoordinatorShmemStruct *)
! 		ShmemInitStruct("Background Worker Data",
! 						CoordinatorShmemSize(),
  						&found);
  
  	if (!IsUnderPostmaster)
*************** AutoVacuumShmemInit(void)
*** 3657,3675 ****
  
  		Assert(!found);
  
! 		AutoVacuumShmem->av_launcherid = InvalidBackendId;
! 		AutoVacuumShmem->av_freeWorkers = NULL;
! 		SHMQueueInit(&AutoVacuumShmem->av_runningWorkers);
! 		AutoVacuumShmem->av_startingWorker = NULL;
  
! 		worker = (WorkerInfo) ((char *) AutoVacuumShmem +
! 							   MAXALIGN(sizeof(AutoVacuumShmemStruct)));
  
  		/* initialize the WorkerInfo free list */
! 		for (i = 0; i < autovacuum_max_workers; i++)
  		{
! 			worker[i].wi_links.next = (SHM_QUEUE *) AutoVacuumShmem->av_freeWorkers;
! 			AutoVacuumShmem->av_freeWorkers = &worker[i];
  		}
  	}
  	else
--- 3654,3672 ----
  
  		Assert(!found);
  
! 		CoordinatorShmem->co_coordinatorid = InvalidBackendId;
! 		CoordinatorShmem->co_freeWorkers = NULL;
! 		SHMQueueInit(&CoordinatorShmem->co_runningWorkers);
! 		CoordinatorShmem->co_startingWorker = NULL;
  
! 		worker = (WorkerInfo) ((char *) CoordinatorShmem +
! 							   MAXALIGN(sizeof(CoordinatorShmemStruct)));
  
  		/* initialize the WorkerInfo free list */
! 		for (i = 0; i < max_background_workers; i++)
  		{
! 			worker[i].wi_links.next = (SHM_QUEUE *) CoordinatorShmem->co_freeWorkers;
! 			CoordinatorShmem->co_freeWorkers = &worker[i];
  		}
  	}
  	else
*************** AutoVacuumShmemInit(void)
*** 3679,3686 ****
  	hctl.entrysize = sizeof(co_database);
  	hctl.hash = oid_hash;
  	co_databases = ShmemInitHash("Coordinator Database Info",
! 								 autovacuum_max_workers,
! 								 autovacuum_max_workers,
  								 &hctl,
  								 HASH_ELEM | HASH_FUNCTION);
  }
--- 3676,3683 ----
  	hctl.entrysize = sizeof(co_database);
  	hctl.hash = oid_hash;
  	co_databases = ShmemInitHash("Coordinator Database Info",
! 								 max_background_workers,
! 								 max_background_workers,
  								 &hctl,
  								 HASH_ELEM | HASH_FUNCTION);
  }
*************** AutoVacuumShmemInit(void)
*** 3690,3698 ****
   *		Refresh pgstats data for an autovacuum process
   *
   * Cause the next pgstats read operation to obtain fresh data, but throttle
!  * such refreshing in the autovacuum launcher.	This is mostly to avoid
!  * rereading the pgstats files too many times in quick succession when there
!  * are many databases.
   *
   * Note: we avoid throttling in the autovac worker, as it would be
   * counterproductive in the recheck logic.
--- 3687,3695 ----
   *		Refresh pgstats data for an autovacuum process
   *
   * Cause the next pgstats read operation to obtain fresh data, but throttle
!  * such refreshing in the coordinator.	This is mostly to avoid rereading
!  * the pgstats files too many times in quick succession when there are many
!  * databases.
   *
   * Note: we avoid throttling in the autovac worker, as it would be
   * counterproductive in the recheck logic.
*************** autovac_refresh_stats(void)
*** 3700,3706 ****
  static void
  autovac_refresh_stats(void)
  {
! 	if (IsAutoVacuumLauncherProcess())
  	{
  		static TimestampTz last_read = 0;
  		TimestampTz current_time;
--- 3697,3703 ----
  static void
  autovac_refresh_stats(void)
  {
! 	if (IsCoordinatorProcess())
  	{
  		static TimestampTz last_read = 0;
  		TimestampTz current_time;
============================================================
*** src/include/postmaster/autovacuum.h	0937b36c82e931677a8fed1cdd638f0138155086
--- src/include/postmaster/autovacuum.h	dc90d7a23ab19ae8e9f5fb9815469058bce97f31
*************** typedef enum
*** 31,44 ****
  
  } worker_state;
  
! #define IsIdleWorker(wi)			(IsAutoVacuumWorkerProcess() && (wi->wi_state == WS_IDLE))
! #define IsAutoVacuumWorker(wi)      (IsAutoVacuumWorkerProcess() && (wi->wi_state == WS_AUTOVACUUM))
  
  
  /*-------------
   * This struct holds information about a single worker's whereabouts.  We keep
   * an array of these in shared memory, sized according to
!  * autovacuum_max_workers.
   *
   * wi_links			entry into free list or running list
   * wi_dboid			OID of the database this worker is supposed to work on
--- 31,44 ----
  
  } worker_state;
  
! #define IsIdleWorker(wi)			(IsBackgroundWorkerProcess() && (wi->wi_state == WS_IDLE))
! #define IsAutoVacuumWorker(wi)      (IsBackgroundWorkerProcess() && (wi->wi_state == WS_AUTOVACUUM))
  
  
  /*-------------
   * This struct holds information about a single worker's whereabouts.  We keep
   * an array of these in shared memory, sized according to
!  * max_background_workers.
   *
   * wi_links			entry into free list or running list
   * wi_dboid			OID of the database this worker is supposed to work on
*************** typedef enum
*** 48,55 ****
   * wi_tableoid		OID of the table currently being vacuumed
   * wi_cost_*		Vacuum cost-based delay parameters current in this worker
   *
!  * All fields are protected by AutovacuumLock, except for wi_tableoid which is
!  * protected by AutovacuumScheduleLock (which is read-only for everyone except
   * that worker itself).
   *-------------
   */
--- 48,55 ----
   * wi_tableoid		OID of the table currently being vacuumed
   * wi_cost_*		Vacuum cost-based delay parameters current in this worker
   *
!  * All fields are protected by WorkerInfoLock, except for wi_tableoid which is
!  * protected by WorkerScheduleLock (which is read-only for everyone except
   * that worker itself).
   *-------------
   */
*************** extern bool autovacuum_enabled;
*** 88,94 ****
  
  /* GUC variables */
  extern bool autovacuum_enabled;
! extern int	autovacuum_max_workers;
  extern int min_spare_background_workers;
  extern int max_spare_background_workers;
  extern int	autovacuum_naptime;
--- 88,94 ----
  
  /* GUC variables */
  extern bool autovacuum_enabled;
! extern int	max_background_workers;
  extern int min_spare_background_workers;
  extern int max_spare_background_workers;
  extern int	autovacuum_naptime;
*************** extern char *decode_worker_state(worker_
*** 105,132 ****
  extern char *decode_worker_state(worker_state state);
  
  /* Status inquiry functions */
! extern bool IsAutoVacuumLauncherProcess(void);
! extern bool IsAutoVacuumWorkerProcess(void);
! extern BackendId GetAutovacuumLauncherId(void);
  
  /* Functions to start autovacuum process, called from postmaster */
  extern void autovac_init(void);
! extern int	StartAutoVacLauncher(void);
! extern int	StartAutoVacWorker(void);
  
  /* autovacuum cost-delay balancer */
  extern void AutoVacuumUpdateDelay(void);
  
  #ifdef EXEC_BACKEND
! extern void AutoVacLauncherMain(int argc, char *argv[]);
! extern void AutoVacWorkerMain(int argc, char *argv[]);
! extern void AutovacuumWorkerIAm(void);
! extern void AutovacuumLauncherIAm(void);
  #endif
  
  /* shared memory stuff */
! extern Size AutoVacuumShmemSize(void);
! extern void AutoVacuumShmemInit(void);
  
  /* bgworker job management functions */
  extern void bgworker_job_failed(int errcode);
--- 105,132 ----
  extern char *decode_worker_state(worker_state state);
  
  /* Status inquiry functions */
! extern bool IsCoordinatorProcess(void);
! extern bool IsBackgroundWorkerProcess(void);
! extern BackendId GetCoordinatorId(void);
  
  /* Functions to start autovacuum process, called from postmaster */
  extern void autovac_init(void);
! extern int	StartCoordinator(void);
! extern int	StartBackgroundWorker(void);
  
  /* autovacuum cost-delay balancer */
  extern void AutoVacuumUpdateDelay(void);
  
  #ifdef EXEC_BACKEND
! extern void CoordinatorMain(int argc, char *argv[]);
! extern void BackgroundWorkerMain(int argc, char *argv[]);
! extern void BackgroundWorkerIAm(void);
! extern void CoordinatorIAm(void);
  #endif
  
  /* shared memory stuff */
! extern Size CoordinatorShmemSize(void);
! extern void CoordinatorShmemInit(void);
  
  /* bgworker job management functions */
  extern void bgworker_job_failed(int errcode);
============================================================
*** src/backend/access/gin/ginvacuum.c	26dc9ae0821cbed10085e6e6b7a0de4d8dd55192
--- src/backend/access/gin/ginvacuum.c	a5ee1da6e8a01eaa4e7df35f77e1b3696de99f4b
*************** ginvacuumcleanup(PG_FUNCTION_ARGS)
*** 717,723 ****
  	 */
  	if (info->analyze_only)
  	{
! 		if (IsAutoVacuumWorkerProcess())
  		{
  			initGinState(&ginstate, index);
  			ginInsertCleanup(index, &ginstate, true, stats);
--- 717,723 ----
  	 */
  	if (info->analyze_only)
  	{
! 		if (IsBackgroundWorkerProcess())
  		{
  			initGinState(&ginstate, index);
  			ginInsertCleanup(index, &ginstate, true, stats);
