diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c
index f8ca52e..e84e2fc 100644
--- a/src/backend/port/win32_shmem.c
+++ b/src/backend/port/win32_shmem.c
@@ -382,6 +382,7 @@ PGSharedMemoryReAttach(void)
 
 	Assert(UsedShmemSegAddr != NULL);
 	Assert(IsUnderPostmaster);
+	/* FIXME change FATAL to proc_exit(1) */
 
 	/*
 	 * Release memory region reservation that was made by the postmaster
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index a4b53b3..eadf7f1 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -180,6 +180,14 @@ typedef struct bkend
 	bool		dead_end;		/* is it going to send an error and quit? */
 	bool		bgworker_notify;	/* gets bgworker start/stop notifications */
 	dlist_node	elem;			/* list link in BackendList */
+
+#ifdef EXEC_BACKEND
+#define RETRYABLE_FORK
+#endif
+#ifdef RETRYABLE_FORK
+	Port	   *port;			/* for still-opening backends */
+	int			retry_count;
+#endif
 } Backend;
 
 static dlist_head BackendList = DLIST_STATIC_INIT(BackendList);
@@ -412,6 +420,7 @@ static void BackendInitialize(Port *port);
 static void BackendRun(Port *port) pg_attribute_noreturn();
 static void ExitPostmaster(int status) pg_attribute_noreturn();
 static int	ServerLoop(void);
+static pid_t BackendFork(Backend *bn);
 static int	BackendStartup(Port *port);
 static int	ProcessStartupPacket(Port *port, bool SSLdone);
 static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
@@ -427,6 +436,7 @@ static void TerminateChildren(int signal);
 #define SignalChildren(sig)			   SignalSomeChildren(sig, BACKEND_TYPE_ALL)
 
 static int	CountChildren(int target);
+static pid_t do_fork_bgworker(RegisteredBgWorker *rw);
 static bool assign_backendlist_entry(RegisteredBgWorker *rw);
 static void maybe_start_bgworkers(void);
 static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
@@ -543,6 +553,7 @@ static bool save_backend_variables(BackendParameters *param, Port *port,
 #endif
 
 static void ShmemBackendArrayAdd(Backend *bn);
+static void ShmemBackendArrayUpdate(Backend *bn, pid_t oldpid);
 static void ShmemBackendArrayRemove(Backend *bn);
 #endif							/* EXEC_BACKEND */
 
@@ -556,6 +567,7 @@ static void ShmemBackendArrayRemove(Backend *bn);
 #define EXIT_STATUS_0(st)  ((st) == 0)
 #define EXIT_STATUS_1(st)  (WIFEXITED(st) && WEXITSTATUS(st) == 1)
 #define EXIT_STATUS_3(st)  (WIFEXITED(st) && WEXITSTATUS(st) == 3)
+#define EXIT_STATUS_4(st)  (WIFEXITED(st) && WEXITSTATUS(st) == 4)
 
 #ifndef WIN32
 /*
@@ -1703,14 +1715,8 @@ ServerLoop(void)
 					port = ConnCreate(ListenSocket[i]);
 					if (port)
 					{
+						/* FIXME refactor more to happen in BackendStartup() */
 						BackendStartup(port);
-
-						/*
-						 * We no longer need the open socket or port structure
-						 * in this process
-						 */
-						StreamClose(port->sock);
-						ConnFree(port);
 					}
 				}
 			}
@@ -2311,7 +2317,11 @@ processCancelRequest(Port *port, void *pkt)
 								backendPID)));
 			return;
 		}
+#ifndef EXEC_BACKEND
+	}
+#else
 	}
+#endif
 
 	/* No matching backend */
 	ereport(LOG,
@@ -2393,8 +2403,6 @@ ConnCreate(int serverFd)
 
 	if (StreamConnection(serverFd, port) != STATUS_OK)
 	{
-		if (port->sock != PGINVALID_SOCKET)
-			StreamClose(port->sock);
 		ConnFree(port);
 		return NULL;
 	}
@@ -2425,6 +2433,8 @@ ConnCreate(int serverFd)
 static void
 ConnFree(Port *conn)
 {
+	if (conn->sock != PGINVALID_SOCKET)
+		StreamClose(conn->sock);
 #ifdef USE_SSL
 	secure_close(conn);
 #endif
@@ -2814,6 +2824,8 @@ reaper(SIGNAL_ARGS)
 				continue;
 			}
 
+			/* FIXME restart startup process after status 4 */
+
 			/*
 			 * Unexpected exit of startup process (including FATAL exit)
 			 * during PM_STARTUP is treated as catastrophic. There are no
@@ -2897,14 +2909,15 @@ reaper(SIGNAL_ARGS)
 		}
 
 		/*
-		 * Was it the bgwriter?  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.
+		 * Was it the bgwriter?  Normal exit and retryable startup failure 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 == BgWriterPID)
 		{
 			BgWriterPID = 0;
-			if (!EXIT_STATUS_0(exitstatus))
+			if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_4(exitstatus))
 				HandleChildCrash(pid, exitstatus,
 								 _("background writer process"));
 			continue;
@@ -2953,6 +2966,7 @@ reaper(SIGNAL_ARGS)
 				if (PgStatPID != 0)
 					signal_child(PgStatPID, SIGQUIT);
 			}
+			/* FIXME retry checkpointer */
 			else
 			{
 				/*
@@ -2967,44 +2981,47 @@ reaper(SIGNAL_ARGS)
 		}
 
 		/*
-		 * Was it the wal writer?  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.
+		 * Was it the wal writer?  Normal exit and retryable startup failure
+		 * 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 == WalWriterPID)
 		{
 			WalWriterPID = 0;
-			if (!EXIT_STATUS_0(exitstatus))
+			if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_4(exitstatus))
 				HandleChildCrash(pid, exitstatus,
 								 _("WAL writer process"));
 			continue;
 		}
 
 		/*
-		 * Was it the wal receiver?  If exit status is zero (normal) or one
-		 * (FATAL exit), we assume everything is all right just like normal
-		 * backends.  (If we need a new wal receiver, we'll start one at the
-		 * next iteration of the postmaster's main loop.)
+		 * Was it the wal receiver?  If exit status is zero (normal), one
+		 * (FATAL exit) or four (retryable startup failure), we assume
+		 * everything is all right just like normal backends.  (If we need a
+		 * new wal receiver, we'll start one at the next iteration of the
+		 * postmaster's main loop.)
 		 */
 		if (pid == WalReceiverPID)
 		{
 			WalReceiverPID = 0;
-			if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
+			if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus) &&
+				!EXIT_STATUS_4(exitstatus))
 				HandleChildCrash(pid, exitstatus,
 								 _("WAL receiver process"));
 			continue;
 		}
 
 		/*
-		 * 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.
+		 * Was it the autovacuum launcher? Normal exit and retryable startup
+		 * failure 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))
+			if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_4(exitstatus))
 				HandleChildCrash(pid, exitstatus,
 								 _("autovacuum launcher process"));
 			continue;
@@ -3116,33 +3133,60 @@ CleanupBackgroundWorker(int pid,
 		snprintf(namebuf, MAXPGPATH, _("background worker \"%s\""),
 				 rw->rw_worker.bgw_type);
 
+		LogChildExit(DEBUG2, namebuf, pid, exitstatus);
 
-		if (!EXIT_STATUS_0(exitstatus))
-		{
-			/* Record timestamp, so we know when to restart the worker. */
-			rw->rw_crashed_at = GetCurrentTimestamp();
-		}
-		else
+		if (EXIT_STATUS_0(exitstatus))
 		{
 			/* Zero exit status means terminate */
 			rw->rw_crashed_at = 0;
 			rw->rw_terminate = true;
 		}
+		else if (!EXIT_STATUS_4(exitstatus))
+		{
+			/* Record timestamp, so we know when to restart the worker. */
+			rw->rw_crashed_at = GetCurrentTimestamp();
+		}
 
 		/*
 		 * Additionally, for shared-memory-connected workers, just like a
-		 * backend, any exit status other than 0 or 1 is considered a crash
+		 * backend, any exit status other than 0, 1 or 4 is considered a crash
 		 * and causes a system-wide restart.
 		 */
 		if ((rw->rw_worker.bgw_flags & BGWORKER_SHMEM_ACCESS) != 0)
 		{
-			if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
+			if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus) &&
+				!EXIT_STATUS_4(exitstatus))
 			{
 				HandleChildCrash(pid, exitstatus, namebuf);
 				return true;
 			}
 		}
 
+#ifdef RETRYABLE_FORK
+		if (EXIT_STATUS_4(exitstatus))
+		{
+			Backend *bp = rw->rw_backend;
+
+			if (++bp->retry_count >= 100) /* FIXME use a constant */
+			{
+				ereport(LOG,
+						(errmsg("giving up after too many tries to reserve shared memory"),
+						 errhint("This might be caused by ASLR or antivirus software.")));
+			}
+			else
+			{
+				bp->pid = do_fork_bgworker(rw);
+				if (bp->pid > 0)
+				{
+					ShmemBackendArrayUpdate(bp, pid);
+					return true;
+				}
+			}
+			/* mark entry as crashed, so we'll try again later */
+			rw->rw_crashed_at = GetCurrentTimestamp();
+		}
+#endif
+
 		/*
 		 * We must release the postmaster child slot whether this worker is
 		 * connected to shared memory or not, but we only treat it as a crash
@@ -3187,7 +3231,8 @@ CleanupBackgroundWorker(int pid,
 /*
  * CleanupBackend -- cleanup after terminated backend.
  *
- * Remove all local state associated with backend.
+ * If the backend failed during early startup, retry forking it.  Otherwise,
+ * remove all local state associated with backend.
  *
  * If you change this, see also CleanupBackgroundWorker.
  */
@@ -3222,7 +3267,8 @@ CleanupBackend(int pid,
 	}
 #endif
 
-	if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
+	if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus) &&
+		!EXIT_STATUS_4(exitstatus))
 	{
 		HandleChildCrash(pid, exitstatus, _("server process"));
 		return;
@@ -3234,6 +3280,40 @@ CleanupBackend(int pid,
 
 		if (bp->pid == pid)
 		{
+#ifdef RETRYABLE_FORK
+			if (EXIT_STATUS_4(exitstatus))
+			{
+				Assert(bp->port);
+
+				if (++bp->retry_count >= 100)
+				{
+					ereport(LOG,
+							(errmsg("giving up after too many tries to reserve shared memory"),
+							 errhint("This might be caused by ASLR or antivirus software.")));
+					report_fork_failure_to_client(bp->port, EAGAIN);
+				}
+				else
+				{
+					bp->pid = BackendFork(bp);
+					if (bp->pid > 0)
+					{
+						if (!bp->dead_end)
+							ShmemBackendArrayUpdate(bp, pid);
+						return;
+					}
+					/* Else, fork failed.  Fall through to remove local state. */
+				}
+			}
+			/*
+			 * For a sufficiently long-running backend, FIXME will have
+			 * already called ConnFree().
+			 */
+			if (bp->port)
+			{
+				ConnFree(bp->port);
+				bp->port = NULL;
+			}
+#endif
 			if (!bp->dead_end)
 			{
 				if (!ReleasePostmasterChildSlot(bp->child_slot))
@@ -3607,6 +3687,14 @@ LogChildExit(int lev, const char *procname, int pid, int exitstatus)
 static void
 PostmasterStateMachine(void)
 {
+	if (pmState == PM_STARTUP && StartupStatus == STARTUP_NOT_RUNNING)
+	{
+		/* We need a startup process, either the  */
+		StartupPID = StartupDataBase();
+		Assert(StartupPID != 0);
+		StartupStatus = STARTUP_RUNNING;
+	}
+
 	if (pmState == PM_WAIT_BACKUP)
 	{
 		/*
@@ -3954,9 +4042,60 @@ TerminateChildren(int signal)
 		signal_child(PgStatPID, signal);
 }
 
+/* FIXME comment */
+static pid_t
+BackendFork(Backend *bn)
+{
+	pid_t		pid;
+
+	MyCancelKey = bn->cancel_key;
+	MyPMChildSlot = bn->child_slot;
+
+#ifdef EXEC_BACKEND
+	pid = backend_forkexec(bn->port);
+#else							/* !EXEC_BACKEND */
+	pid = fork_process();
+	if (pid == 0)				/* child */
+	{
+		/* Detangle from postmaster */
+		InitPostmasterChild();
+
+		/* Close the postmaster's sockets */
+		ClosePostmasterPorts(false);
+
+		/* Perform additional initialization and collect startup packet */
+		BackendInitialize(bn->port);
+
+		/* And run the backend */
+		BackendRun(bn->port);
+	}
+#endif							/* EXEC_BACKEND */
+
+	if (pid < 0)
+	{
+		/* in parent, fork failed */
+		int			save_errno = errno;
+
+		errno = save_errno;
+		ereport(LOG,
+				(errmsg("could not fork new process for connection: %m")));
+		report_fork_failure_to_client(bn->port, save_errno);
+	}
+	else
+	{
+		/* in parent, successful fork */
+		ereport(DEBUG2,
+				(errmsg_internal("forked new backend, pid=%d socket=%d",
+								 (int) pid, (int) bn->port->sock)));
+	}
+
+	return pid;
+}
+
 /*
  * BackendStartup -- start backend process
  *
+ * FIXME
  * returns: STATUS_ERROR if the fork failed, STATUS_OK otherwise.
  *
  * Note: if you change this code, also consider StartAutovacuumWorker.
@@ -3965,7 +4104,6 @@ static int
 BackendStartup(Port *port)
 {
 	Backend    *bn;				/* for backend cleanup */
-	pid_t		pid;
 
 	/*
 	 * Create backend data structure.  Better before the fork() so we can
@@ -3977,24 +4115,13 @@ BackendStartup(Port *port)
 		ereport(LOG,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of memory")));
-		return STATUS_ERROR;
-	}
-
-	/*
-	 * Compute the cancel key that will be assigned to this backend. The
-	 * backend will have its own copy in the forked-off process' value of
-	 * MyCancelKey, so that it can transmit the key to the frontend.
-	 */
-	if (!RandomCancelKey(&MyCancelKey))
-	{
-		free(bn);
-		ereport(LOG,
-				(errcode(ERRCODE_INTERNAL_ERROR),
-				 errmsg("could not generate random cancel key")));
-		return STATUS_ERROR;
+		goto err;
 	}
 
-	bn->cancel_key = MyCancelKey;
+#ifdef RETRYABLE_FORK
+	bn->port = port;
+	bn->retry_count = 0;
+#endif
 
 	/* Pass down canAcceptConnections state */
 	port->canAcceptConnections = canAcceptConnections();
@@ -4005,60 +4132,38 @@ BackendStartup(Port *port)
 	 * Unless it's a dead_end child, assign it a child slot number
 	 */
 	if (!bn->dead_end)
-		bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
+		bn->child_slot = AssignPostmasterChildSlot();
 	else
 		bn->child_slot = 0;
 
 	/* Hasn't asked to be notified about any bgworkers yet */
 	bn->bgworker_notify = false;
 
-#ifdef EXEC_BACKEND
-	pid = backend_forkexec(port);
-#else							/* !EXEC_BACKEND */
-	pid = fork_process();
-	if (pid == 0)				/* child */
+	/* Compute the cancel key that will be assigned to this backend. */
+	if (!RandomCancelKey(&bn->cancel_key))
 	{
-		free(bn);
-
-		/* Detangle from postmaster */
-		InitPostmasterChild();
-
-		/* Close the postmaster's sockets */
-		ClosePostmasterPorts(false);
-
-		/* Perform additional initialization and collect startup packet */
-		BackendInitialize(port);
-
-		/* And run the backend */
-		BackendRun(port);
-	}
-#endif							/* EXEC_BACKEND */
-
-	if (pid < 0)
-	{
-		/* in parent, fork failed */
-		int			save_errno = errno;
-
-		if (!bn->dead_end)
-			(void) ReleasePostmasterChildSlot(bn->child_slot);
-		free(bn);
-		errno = save_errno;
 		ereport(LOG,
-				(errmsg("could not fork new process for connection: %m")));
-		report_fork_failure_to_client(port, save_errno);
-		return STATUS_ERROR;
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("could not generate random cancel key")));
+		goto err;
 	}
 
-	/* in parent, successful fork */
-	ereport(DEBUG2,
-			(errmsg_internal("forked new backend, pid=%d socket=%d",
-							 (int) pid, (int) port->sock)));
+	bn->pid = BackendFork(bn);
+	if (bn->pid < 0)
+		goto err;
+
+#ifndef RETRYABLE_FORK
+	/*
+	 * We no longer need the open socket or port structure
+	 * in this process
+	 */
+	ConnFree(port);
+#endif
 
 	/*
 	 * Everything's been successful, it's safe to add this backend to our list
 	 * of backends.
 	 */
-	bn->pid = pid;
 	bn->bkend_type = BACKEND_TYPE_NORMAL;	/* Can change later to WALSND */
 	dlist_push_head(&BackendList, &bn->elem);
 
@@ -4068,6 +4173,16 @@ BackendStartup(Port *port)
 #endif
 
 	return STATUS_OK;
+
+err:
+	ConnFree(port);
+	if (bn)
+	{
+		if (bn->child_slot)
+			(void) ReleasePostmasterChildSlot(bn->child_slot);
+		free(bn);
+	}
+	return STATUS_ERROR;
 }
 
 /*
@@ -4708,6 +4823,15 @@ retry:
 #endif							/* WIN32 */
 
 
+static void
+MarkSharedStateComplete(void)
+{
+#ifdef FIXME_WIN32
+	SendPostmasterSignal(PMSIGNAL_BACKEND_SHMAT);
+#endif
+}
+
+
 /*
  * SubPostmasterMain -- Get the fork/exec'd process into a state equivalent
  *			to what it would be if we'd simply forked on Unix, and then
@@ -4783,7 +4907,24 @@ SubPostmasterMain(int argc, char *argv[])
 		strcmp(argv[1], "--forkavworker") == 0 ||
 		strcmp(argv[1], "--forkboot") == 0 ||
 		strncmp(argv[1], "--forkbgworker=", 15) == 0)
+	{
+		/* test retryable starts */
+		if (0 && (strcmp(argv[1], "--forkbackend") == 0 ||
+				  strcmp(argv[1], "--forkavlauncher") == 0 ||
+				  strcmp(argv[1], "--forkavworker") == 0 ||
+				  /* strcmp(argv[1], "--forkboot") == 0 || */
+				  strncmp(argv[1], "--forkbgworker=", 15) == 0))
+		{
+			MyProcPid = getpid(); /* FIXME not needed? */
+			srandom((unsigned int ) (MyProcPid ^ MyStartTime));
+			if (random() % 10 != 0)
+			{
+				elog(LOG, "failing startup for testing purposes");
+				proc_exit(4);
+			}
+		}
 		PGSharedMemoryReAttach();
+	}
 	else
 		PGSharedMemoryNoReAttach();
 
@@ -4878,6 +5019,7 @@ SubPostmasterMain(int argc, char *argv[])
 
 		/* Attach process to shared data structures */
 		CreateSharedMemoryAndSemaphores(false, 0);
+		MarkSharedStateComplete();
 
 		/* And run the backend */
 		BackendRun(&port);		/* does not return */
@@ -4892,6 +5034,7 @@ SubPostmasterMain(int argc, char *argv[])
 
 		/* Attach process to shared data structures */
 		CreateSharedMemoryAndSemaphores(false, 0);
+		MarkSharedStateComplete();
 
 		AuxiliaryProcessMain(argc - 2, argv + 2);	/* does not return */
 	}
@@ -4905,6 +5048,7 @@ SubPostmasterMain(int argc, char *argv[])
 
 		/* Attach process to shared data structures */
 		CreateSharedMemoryAndSemaphores(false, 0);
+		MarkSharedStateComplete();
 
 		AutoVacLauncherMain(argc - 2, argv + 2);	/* does not return */
 	}
@@ -4918,6 +5062,7 @@ SubPostmasterMain(int argc, char *argv[])
 
 		/* Attach process to shared data structures */
 		CreateSharedMemoryAndSemaphores(false, 0);
+		MarkSharedStateComplete();
 
 		AutoVacWorkerMain(argc - 2, argv + 2);	/* does not return */
 	}
@@ -4936,6 +5081,7 @@ SubPostmasterMain(int argc, char *argv[])
 
 		/* Attach process to shared data structures */
 		CreateSharedMemoryAndSemaphores(false, 0);
+		MarkSharedStateComplete();
 
 		/* Fetch MyBgworkerEntry from shared memory */
 		shmem_slot = atoi(argv[1] + 15);
@@ -4952,12 +5098,14 @@ SubPostmasterMain(int argc, char *argv[])
 	if (strcmp(argv[1], "--forkcol") == 0)
 	{
 		/* Do not want to attach to shared memory */
+		MarkSharedStateComplete();
 
 		PgstatCollectorMain(argc, argv);	/* does not return */
 	}
 	if (strcmp(argv[1], "--forklog") == 0)
 	{
 		/* Do not want to attach to shared memory */
+		MarkSharedStateComplete();
 
 		SysLoggerMain(argc, argv);	/* does not return */
 	}
@@ -5620,40 +5768,14 @@ bgworker_forkexec(int shmem_slot)
 }
 #endif
 
-/*
- * Start a new bgworker.
- * Starting time conditions must have been checked already.
- *
- * Returns true on success, false on failure.
- * In either case, update the RegisteredBgWorker's state appropriately.
- *
- * This code is heavily based on autovacuum.c, q.v.
- */
-static bool
-do_start_bgworker(RegisteredBgWorker *rw)
+/* FIXME comment */
+static pid_t
+do_fork_bgworker(RegisteredBgWorker *rw)
 {
 	pid_t		worker_pid;
 
-	Assert(rw->rw_pid == 0);
-
-	/*
-	 * Allocate and assign the Backend element.  Note we must do this before
-	 * forking, so that we can handle out of memory properly.
-	 *
-	 * Treat failure as though the worker had crashed.  That way, the
-	 * postmaster will wait a bit before attempting to start it again; if it
-	 * tried again right away, most likely it'd find itself repeating the
-	 * out-of-memory or fork failure condition.
-	 */
-	if (!assign_backendlist_entry(rw))
-	{
-		rw->rw_crashed_at = GetCurrentTimestamp();
-		return false;
-	}
-
-	ereport(DEBUG1,
-			(errmsg("starting background worker process \"%s\"",
-					rw->rw_worker.bgw_name)));
+	MyCancelKey = rw->rw_backend->cancel_key;
+	MyPMChildSlot = rw->rw_backend->child_slot;
 
 #ifdef EXEC_BACKEND
 	switch ((worker_pid = bgworker_forkexec(rw->rw_shmem_slot)))
@@ -5665,13 +5787,6 @@ do_start_bgworker(RegisteredBgWorker *rw)
 			/* in postmaster, fork failed ... */
 			ereport(LOG,
 					(errmsg("could not fork worker process: %m")));
-			/* undo what assign_backendlist_entry did */
-			ReleasePostmasterChildSlot(rw->rw_child_slot);
-			rw->rw_child_slot = 0;
-			free(rw->rw_backend);
-			rw->rw_backend = NULL;
-			/* mark entry as crashed, so we'll try again later */
-			rw->rw_crashed_at = GetCurrentTimestamp();
 			break;
 
 #ifndef EXEC_BACKEND
@@ -5702,15 +5817,73 @@ do_start_bgworker(RegisteredBgWorker *rw)
 #endif
 		default:
 			/* in postmaster, fork successful ... */
-			rw->rw_pid = worker_pid;
+			rw->rw_pid = worker_pid; /* FIXME move later */
 			rw->rw_backend->pid = rw->rw_pid;
-			ReportBackgroundWorkerPID(rw);
-			/* add new worker to lists of backends */
-			dlist_push_head(&BackendList, &rw->rw_backend->elem);
+			ReportBackgroundWorkerPID(rw); /* FIXME move later */
+
+			ereport(DEBUG2,
+					(errmsg_internal("FIXME forked new bgworker, pid=%d",
+									 (int) worker_pid)));
+	}
+
+	return worker_pid;
+}
+
+/*
+ * Start a new bgworker.
+ * Starting time conditions must have been checked already.
+ *
+ * Returns true on success, false on failure.
+ * In either case, update the RegisteredBgWorker's state appropriately.
+ * FIXME responsible for BackendList lifecycle
+ *
+ * This code is heavily based on autovacuum.c, q.v.
+ */
+static bool
+do_start_bgworker(RegisteredBgWorker *rw)
+{
+	pid_t		worker_pid;
+
+	Assert(rw->rw_pid == 0);
+
+	/*
+	 * Allocate and assign the Backend element.  Note we must do this before
+	 * forking, so that we can handle out of memory properly.
+	 *
+	 * Treat failure as though the worker had crashed.  That way, the
+	 * postmaster will wait a bit before attempting to start it again; if it
+	 * tried again right away, most likely it'd find itself repeating the
+	 * out-of-memory or fork failure condition.
+	 */
+	if (!assign_backendlist_entry(rw))
+	{
+		rw->rw_crashed_at = GetCurrentTimestamp();
+		return false;
+	}
+
+	ereport(DEBUG1,
+			(errmsg("starting background worker process \"%s\"",
+					rw->rw_worker.bgw_name)));
+
+	worker_pid = do_fork_bgworker(rw);
+	if (worker_pid == -1)
+	{
+		/* undo what assign_backendlist_entry did */
+		ReleasePostmasterChildSlot(rw->rw_child_slot);
+		rw->rw_child_slot = 0;
+		free(rw->rw_backend);
+		rw->rw_backend = NULL;
+		/* mark entry as crashed, so we'll try again later */
+		rw->rw_crashed_at = GetCurrentTimestamp();
+	}
+	else
+	{
+		/* add new worker to lists of backends */
+		dlist_push_head(&BackendList, &rw->rw_backend->elem);
 #ifdef EXEC_BACKEND
-			ShmemBackendArrayAdd(rw->rw_backend);
+		ShmemBackendArrayAdd(rw->rw_backend);
 #endif
-			return true;
+		return true;
 	}
 
 	return false;
@@ -5797,6 +5970,10 @@ assign_backendlist_entry(RegisteredBgWorker *rw)
 	bn->bkend_type = BACKEND_TYPE_BGWORKER;
 	bn->dead_end = false;
 	bn->bgworker_notify = false;
+#ifdef RETRYABLE_FORK
+	bn->port = NULL;
+	bn->retry_count = 0;
+#endif
 
 	rw->rw_backend = bn;
 	rw->rw_child_slot = bn->child_slot;
@@ -6306,6 +6483,15 @@ ShmemBackendArrayAdd(Backend *bn)
 }
 
 static void
+ShmemBackendArrayUpdate(Backend *bn, pid_t oldpid)
+{
+	int			i = bn->child_slot - 1;
+
+	Assert(ShmemBackendArray[i].pid == oldpid);
+	ShmemBackendArray[i] = *bn;
+}
+
+static void
 ShmemBackendArrayRemove(Backend *bn)
 {
 	int			i = bn->child_slot - 1;
diff --git a/src/include/storage/pmsignal.h b/src/include/storage/pmsignal.h
index 0747341..1a6a46c 100644
--- a/src/include/storage/pmsignal.h
+++ b/src/include/storage/pmsignal.h
@@ -35,6 +35,7 @@ typedef enum
 	PMSIGNAL_RECOVERY_STARTED,	/* recovery has started */
 	PMSIGNAL_BEGIN_HOT_STANDBY, /* begin Hot Standby */
 	PMSIGNAL_WAKEN_ARCHIVER,	/* send a NOTIFY signal to xlog archiver */
+	PMSIGNAL_BACKEND_SHMAT,		/* newest backend no longer seeks shmem attachment */
 	PMSIGNAL_ROTATE_LOGFILE,	/* send SIGUSR1 to syslogger to rotate logfile */
 	PMSIGNAL_START_AUTOVAC_LAUNCHER,	/* start an autovacuum launcher */
 	PMSIGNAL_START_AUTOVAC_WORKER,	/* start an autovacuum worker */
