On Sat, Dec 3, 2022 at 10:41 AM Thomas Munro <thomas.mu...@gmail.com> wrote:
> Here's an iteration like that.  Still WIP grade.  It passes, but there
> must be something I don't understand about this computer program yet,
> because if I move the "if (pending_..." section up into the block
> where WL_LATCH_SET has arrived (instead of testing those variables
> every time through the loop), a couple of tests leave zombie
> (unreaped) processes behind, indicating that something funky happened
> to the state machine that I haven't yet grokked.  Will look more next
> week.

Duh.  The reason for that was the pre-existing special case for
PM_WAIT_DEAD_END, which used a sleep(100ms) loop to wait for children
to exit, which I needed to change to a latch wait.  Fixed in the next
iteration, attached.

The reason for the existing sleep-based approach was that we didn't
want to accept any more connections (or spin furiously because the
listen queue was non-empty).  So in this version I invented a way to
suppress socket events temporarily with WL_SOCKET_IGNORE, and then
reactivate them after crash reinit.

Still WIP, but I hope travelling in the right direction.
From 65b5fa1f7024cb78cee9ba57d36a78dc17ffe492 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.mu...@gmail.com>
Date: Wed, 9 Nov 2022 22:59:58 +1300
Subject: [PATCH v3] Give the postmaster a WaitEventSet and a latch.

Traditionally, the postmaster's architecture was quite unusual.  It did
a lot of work inside signal handlers, which were only unblocked while
waiting in select() to make that safe.

Switch to a more typical architecture, where signal handlers just set
flags and use a latch to close races.  Now the postmaster looks like
all other PostgreSQL processes, multiplexing its event processing in
epoll_wait()/kevent()/poll()/WaitForMultipleObjects() depending on the
OS.

Changes:

 * WL_SOCKET_ACCEPT is a new event for an incoming connection (on Unix,
   this is just another name for WL_SOCKET_READABLE, but Window has a
   different underlying event; this mirrors WL_SOCKET_CONNECTED on the
   other end of a connection)

 * WL_SOCKET_IGNORE is a new way to stop waking up for new incoming
   connections while shutting down.

 * Small adjustments to WaitEventSet to allow running in the postmaster.

 * Allow the postmaster to set up its own local latch.  For now we don't
   want other backends setting the postmaster's latch directly (perhaps
   later we'll figure out how to use a shared latch "robustly", so that
   memory corruption can't interfere with the postmaster's
   cleanup-and-restart responsibilities, but for now there is a two-step
   signal protocol SIGUSR1 -> SIGURG).

 * The existing signal handlers are cut in two: a handle_XXX part that
   sets a pending_XXX variable and sets the local latch, and a
   process_XXX part.

 * ServerLoop(), the process_XXX() functions and
   PostmasterStateMachine() now all take a pointer to a Postmaster
   object that lives on the stack as a parameter that initially holds the
   WaitEventSet they need to do their job.  Many other global variables
   could be moved into it, but that's not done here.

 * Signal handlers are now installed with the regular pqsignal()
   function rather then the special pqsignal_pm() function; the concerns
   about the portability of SA_RESTART vs select() are no longer
   relevant: SUSv2 left it implementation-defined whether select()
   restarts, but didn't add that qualification for poll(), and it doesn't
   matter anyway because we call SetLatch() creating a new reason to wake
   up.

Reviewed-by: Andres Freund <and...@anarazel.de>
Discussion: https://postgr.es/m/CA%2BhUKG%2BZ-HpOj1JsO9eWUP%2Bar7npSVinsC_npxSy%2BjdOMsx%3DGg%40mail.gmail.com
---
 src/backend/libpq/pqsignal.c        |  40 ---
 src/backend/postmaster/postmaster.c | 413 +++++++++++++++-------------
 src/backend/storage/ipc/latch.c     |  22 ++
 src/backend/tcop/postgres.c         |   1 -
 src/backend/utils/init/miscinit.c   |  13 +-
 src/include/libpq/pqsignal.h        |   3 -
 src/include/miscadmin.h             |   1 +
 src/include/storage/latch.h         |   9 +-
 8 files changed, 266 insertions(+), 236 deletions(-)

diff --git a/src/backend/libpq/pqsignal.c b/src/backend/libpq/pqsignal.c
index 1ab34c5214..718043a39d 100644
--- a/src/backend/libpq/pqsignal.c
+++ b/src/backend/libpq/pqsignal.c
@@ -97,43 +97,3 @@ pqinitmask(void)
 	sigdelset(&StartupBlockSig, SIGALRM);
 #endif
 }
-
-/*
- * Set up a postmaster signal handler for signal "signo"
- *
- * Returns the previous handler.
- *
- * This is used only in the postmaster, which has its own odd approach to
- * signal handling.  For signals with handlers, we block all signals for the
- * duration of signal handler execution.  We also do not set the SA_RESTART
- * flag; this should be safe given the tiny range of code in which the
- * postmaster ever unblocks signals.
- *
- * pqinitmask() must have been invoked previously.
- */
-pqsigfunc
-pqsignal_pm(int signo, pqsigfunc func)
-{
-	struct sigaction act,
-				oact;
-
-	act.sa_handler = func;
-	if (func == SIG_IGN || func == SIG_DFL)
-	{
-		/* in these cases, act the same as pqsignal() */
-		sigemptyset(&act.sa_mask);
-		act.sa_flags = SA_RESTART;
-	}
-	else
-	{
-		act.sa_mask = BlockSig;
-		act.sa_flags = 0;
-	}
-#ifdef SA_NOCLDSTOP
-	if (signo == SIGCHLD)
-		act.sa_flags |= SA_NOCLDSTOP;
-#endif
-	if (sigaction(signo, &act, &oact) < 0)
-		return SIG_ERR;
-	return oact.sa_handler;
-}
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index a8a246921f..5000fb987d 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -70,7 +70,6 @@
 #include <time.h>
 #include <sys/wait.h>
 #include <ctype.h>
-#include <sys/select.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <fcntl.h>
@@ -325,6 +324,16 @@ typedef enum
 	PM_NO_CHILDREN				/* all important children have exited */
 } PMState;
 
+/*
+ * Object representing the state of a postmaster.
+ *
+ * XXX Lots of global variables could move in here.
+ */
+typedef struct
+{
+	WaitEventSet	*wes;
+} Postmaster;
+
 static PMState pmState = PM_INIT;
 
 /*
@@ -362,6 +371,14 @@ static volatile sig_atomic_t WalReceiverRequested = false;
 static volatile bool StartWorkerNeeded = true;
 static volatile bool HaveCrashedWorker = false;
 
+/* set when signals arrive */
+static volatile sig_atomic_t pending_action_request;
+static volatile sig_atomic_t pending_child_exit;
+static volatile sig_atomic_t pending_reload_request;
+static volatile sig_atomic_t pending_shutdown_request;
+
+static bool		reenable_server_socket_events;
+
 #ifdef USE_SSL
 /* Set when and if SSL has been initialized properly */
 static bool LoadedSSL = false;
@@ -380,10 +397,14 @@ static void getInstallationPaths(const char *argv0);
 static void checkControlFile(void);
 static Port *ConnCreate(int serverFd);
 static void ConnFree(Port *port);
-static void SIGHUP_handler(SIGNAL_ARGS);
-static void pmdie(SIGNAL_ARGS);
-static void reaper(SIGNAL_ARGS);
-static void sigusr1_handler(SIGNAL_ARGS);
+static void handle_action_request_signal(SIGNAL_ARGS);
+static void handle_child_exit_signal(SIGNAL_ARGS);
+static void handle_reload_request_signal(SIGNAL_ARGS);
+static void handle_shutdown_request_signal(SIGNAL_ARGS);
+static void process_action_request(Postmaster *postmaster);
+static void process_child_exit(Postmaster *postmaster);
+static void process_reload_request(void);
+static void process_shutdown_request(Postmaster *postmaster);
 static void process_startup_packet_die(SIGNAL_ARGS);
 static void dummy_handler(SIGNAL_ARGS);
 static void StartupPacketTimeoutHandler(void);
@@ -392,16 +413,15 @@ static bool CleanupBackgroundWorker(int pid, int exitstatus);
 static void HandleChildCrash(int pid, int exitstatus, const char *procname);
 static void LogChildExit(int lev, const char *procname,
 						 int pid, int exitstatus);
-static void PostmasterStateMachine(void);
+static void PostmasterStateMachine(Postmaster *postmaster);
 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 int	ServerLoop(Postmaster *postmaster);
 static int	BackendStartup(Port *port);
 static int	ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done);
 static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
 static void processCancelRequest(Port *port, void *pkt);
-static int	initMasks(fd_set *rmask);
 static void report_fork_failure_to_client(Port *port, int errnum);
 static CAC_state canAcceptConnections(int backend_type);
 static bool RandomCancelKey(int32 *cancel_key);
@@ -568,6 +588,7 @@ PostmasterMain(int argc, char *argv[])
 	bool		listen_addr_saved = false;
 	int			i;
 	char	   *output_config_variable = NULL;
+	Postmaster	postmaster = {0};
 
 	InitProcessGlobals();
 
@@ -609,26 +630,6 @@ PostmasterMain(int argc, char *argv[])
 	/*
 	 * Set up signal handlers for the postmaster process.
 	 *
-	 * In the postmaster, we use pqsignal_pm() rather than pqsignal() (which
-	 * is used by all child processes and client processes).  That has a
-	 * couple of special behaviors:
-	 *
-	 * 1. We tell sigaction() to block all signals for the duration of the
-	 * signal handler.  This is faster than our old approach of
-	 * blocking/unblocking explicitly in the signal handler, and it should also
-	 * prevent excessive stack consumption if signals arrive quickly.
-	 *
-	 * 2. We do not set the SA_RESTART flag.  This is because signals will be
-	 * blocked at all times except when ServerLoop is waiting for something to
-	 * happen, and during that window, we want signals to exit the select(2)
-	 * wait so that ServerLoop can respond if anything interesting happened.
-	 * On some platforms, signals marked SA_RESTART would not cause the
-	 * select() wait to end.
-	 *
-	 * Child processes will generally want SA_RESTART, so pqsignal() sets that
-	 * flag.  We expect children to set up their own handlers before
-	 * unblocking signals.
-	 *
 	 * CAUTION: when changing this list, check for side-effects on the signal
 	 * handling setup of child processes.  See tcop/postgres.c,
 	 * bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/walwriter.c,
@@ -638,26 +639,21 @@ PostmasterMain(int argc, char *argv[])
 	pqinitmask();
 	PG_SETMASK(&BlockSig);
 
-	pqsignal_pm(SIGHUP, SIGHUP_handler);	/* reread config file and have
-											 * children do same */
-	pqsignal_pm(SIGINT, pmdie); /* send SIGTERM and shut down */
-	pqsignal_pm(SIGQUIT, pmdie);	/* send SIGQUIT and die */
-	pqsignal_pm(SIGTERM, pmdie);	/* wait for children and shut down */
-	pqsignal_pm(SIGALRM, SIG_IGN);	/* ignored */
-	pqsignal_pm(SIGPIPE, SIG_IGN);	/* ignored */
-	pqsignal_pm(SIGUSR1, sigusr1_handler);	/* message from child process */
-	pqsignal_pm(SIGUSR2, dummy_handler);	/* unused, reserve for children */
-	pqsignal_pm(SIGCHLD, reaper);	/* handle child termination */
+	pqsignal(SIGHUP, handle_reload_request_signal);
+	pqsignal(SIGINT, handle_shutdown_request_signal);
+	pqsignal(SIGQUIT, handle_shutdown_request_signal);
+	pqsignal(SIGTERM, handle_shutdown_request_signal);
+	pqsignal(SIGALRM, SIG_IGN);	/* ignored */
+	pqsignal(SIGPIPE, SIG_IGN);	/* ignored */
+	pqsignal(SIGUSR1, handle_action_request_signal);
+	pqsignal(SIGUSR2, dummy_handler);	/* unused, reserve for children */
+	pqsignal(SIGCHLD, handle_child_exit_signal);
 
-#ifdef SIGURG
+	/* This may configure SIGURG, depending on platform. */
+	InitializeLatchSupport();
+	InitLocalLatch();
 
-	/*
-	 * Ignore SIGURG for now.  Child processes may change this (see
-	 * InitializeLatchSupport), but they will not receive any such signals
-	 * until they wait on a latch.
-	 */
-	pqsignal_pm(SIGURG, SIG_IGN);	/* ignored */
-#endif
+	PG_SETMASK(&UnBlockSig);
 
 	/*
 	 * No other place in Postgres should touch SIGTTIN/SIGTTOU handling.  We
@@ -667,15 +663,15 @@ PostmasterMain(int argc, char *argv[])
 	 * child processes should just allow the inherited settings to stand.
 	 */
 #ifdef SIGTTIN
-	pqsignal_pm(SIGTTIN, SIG_IGN);	/* ignored */
+	pqsignal(SIGTTIN, SIG_IGN);	/* ignored */
 #endif
 #ifdef SIGTTOU
-	pqsignal_pm(SIGTTOU, SIG_IGN);	/* ignored */
+	pqsignal(SIGTTOU, SIG_IGN);	/* ignored */
 #endif
 
 	/* ignore SIGXFSZ, so that ulimit violations work like disk full */
 #ifdef SIGXFSZ
-	pqsignal_pm(SIGXFSZ, SIG_IGN);	/* ignored */
+	pqsignal(SIGXFSZ, SIG_IGN);	/* ignored */
 #endif
 
 	/*
@@ -1460,7 +1456,7 @@ PostmasterMain(int argc, char *argv[])
 	/* Some workers may be scheduled to start now */
 	maybe_start_bgworkers();
 
-	status = ServerLoop();
+	status = ServerLoop(&postmaster);
 
 	/*
 	 * ServerLoop probably shouldn't ever return, but if it does, close down.
@@ -1698,105 +1694,112 @@ DetermineSleepTime(struct timeval *timeout)
 	}
 }
 
+/*
+ * Initialize the WaitEventSet we'll use in our main event loop.
+ */
+static void
+InitializeWaitSet(Postmaster *postmaster)
+{
+	/* Set up a WaitEventSet for our latch and listening sockets. */
+	postmaster->wes = CreateWaitEventSet(CurrentMemoryContext, 1 + MAXLISTEN);
+	AddWaitEventToSet(postmaster->wes, WL_LATCH_SET, PGINVALID_SOCKET, MyLatch, NULL);
+	for (int i = 0; i < MAXLISTEN; i++)
+	{
+		int			fd = ListenSocket[i];
+
+		if (fd == PGINVALID_SOCKET)
+			break;
+		AddWaitEventToSet(postmaster->wes, WL_SOCKET_ACCEPT, fd, NULL, NULL);
+	}
+}
+
+/*
+ * Activate or deactivate the server socket events.
+ */
+static void
+AdjustServerSocketEvents(Postmaster *postmaster, bool active)
+{
+	for (int pos = 1; pos < GetNumRegisteredWaitEvents(postmaster->wes); ++pos)
+		ModifyWaitEvent(postmaster->wes,
+						pos, active ? WL_SOCKET_ACCEPT : WL_SOCKET_IGNORE,
+						NULL);
+}
+
 /*
  * Main idle loop of postmaster
  *
  * NB: Needs to be called with signals blocked
  */
 static int
-ServerLoop(void)
+ServerLoop(Postmaster *postmaster)
 {
-	fd_set		readmask;
-	int			nSockets;
 	time_t		last_lockfile_recheck_time,
 				last_touch_time;
+	WaitEvent	events[MAXLISTEN];
+	int			nevents;
 
+	InitializeWaitSet(postmaster);
 	last_lockfile_recheck_time = last_touch_time = time(NULL);
 
-	nSockets = initMasks(&readmask);
-
 	for (;;)
 	{
-		fd_set		rmask;
-		int			selres;
 		time_t		now;
+		struct timeval timeout;
 
-		/*
-		 * Wait for a connection request to arrive.
-		 *
-		 * We block all signals except while sleeping. That makes it safe for
-		 * signal handlers, which again block all signals while executing, to
-		 * do nontrivial work.
-		 *
-		 * If we are in PM_WAIT_DEAD_END state, then we don't want to accept
-		 * any new connections, so we don't call select(), and just sleep.
-		 */
-		memcpy((char *) &rmask, (char *) &readmask, sizeof(fd_set));
-
-		if (pmState == PM_WAIT_DEAD_END)
-		{
-			PG_SETMASK(&UnBlockSig);
-
-			pg_usleep(100000L); /* 100 msec seems reasonable */
-			selres = 0;
-
-			PG_SETMASK(&BlockSig);
-		}
-		else
-		{
-			/* must set timeout each time; some OSes change it! */
-			struct timeval timeout;
-
-			/* Needs to run with blocked signals! */
-			DetermineSleepTime(&timeout);
-
-			PG_SETMASK(&UnBlockSig);
+		DetermineSleepTime(&timeout);
 
-			selres = select(nSockets, &rmask, NULL, NULL, &timeout);
+		nevents = WaitEventSetWait(postmaster->wes,
+								   timeout.tv_sec * 1000 + timeout.tv_usec / 1000,
+								   events,
+								   lengthof(events),
+								   0 /* postmaster posts no wait_events */);
 
-			PG_SETMASK(&BlockSig);
-		}
-
-		/* Now check the select() result */
-		if (selres < 0)
+		/*
+		 * Latch set by signal handler, or new connection pending on any of our
+		 * sockets? If the latter, fork a child process to deal with it.
+		 */
+		for (int i = 0; i < nevents; i++)
 		{
-			if (errno != EINTR && errno != EWOULDBLOCK)
+			if (events[i].events & WL_LATCH_SET)
 			{
-				ereport(LOG,
-						(errcode_for_socket_access(),
-						 errmsg("select() failed in postmaster: %m")));
-				return STATUS_ERROR;
+				ResetLatch(MyLatch);
+
+				/* Process work scheduled by signal handlers. */
+				if (pending_action_request)
+					process_action_request(postmaster);
+				if (pending_child_exit)
+					process_child_exit(postmaster);
+				if (pending_reload_request)
+					process_reload_request();
+				if (pending_shutdown_request)
+					process_shutdown_request(postmaster);
 			}
-		}
+			else if (events[i].events & WL_SOCKET_ACCEPT)
+			{
+				Port	   *port;
 
-		/*
-		 * New connection pending on any of our sockets? If so, fork a child
-		 * process to deal with it.
-		 */
-		if (selres > 0)
-		{
-			int			i;
+				/*
+				 * If we are in PM_WAIT_DEAD_END state, then we don't want to
+				 * accept any new connections.  Lazily silence all socket
+				 * events.
+				 */
+				if (pmState == PM_WAIT_DEAD_END)
+				{
+					AdjustServerSocketEvents(postmaster, false);
+					continue;
+				}
 
-			for (i = 0; i < MAXLISTEN; i++)
-			{
-				if (ListenSocket[i] == PGINVALID_SOCKET)
-					break;
-				if (FD_ISSET(ListenSocket[i], &rmask))
+				port = ConnCreate(events[i].fd);
+				if (port)
 				{
-					Port	   *port;
+					BackendStartup(port);
 
-					port = ConnCreate(ListenSocket[i]);
-					if (port)
-					{
-						BackendStartup(port);
-
-						/*
-						 * We no longer need the open socket or port structure
-						 * in this process
-						 */
-						StreamClose(port->sock);
-						ConnFree(port);
-					}
+					/*
+					 * We no longer need the open socket or port structure
+					 * in this process
+					 */
+					StreamClose(port->sock);
+					ConnFree(port);
 				}
 			}
 		}
@@ -1939,34 +1942,6 @@ ServerLoop(void)
 	}
 }
 
-/*
- * Initialise the masks for select() for the ports we are listening on.
- * Return the number of sockets to listen on.
- */
-static int
-initMasks(fd_set *rmask)
-{
-	int			maxsock = -1;
-	int			i;
-
-	FD_ZERO(rmask);
-
-	for (i = 0; i < MAXLISTEN; i++)
-	{
-		int			fd = ListenSocket[i];
-
-		if (fd == PGINVALID_SOCKET)
-			break;
-		FD_SET(fd, rmask);
-
-		if (fd > maxsock)
-			maxsock = fd;
-	}
-
-	return maxsock + 1;
-}
-
-
 /*
  * Read a client's startup packet and do something according to it.
  *
@@ -2707,14 +2682,42 @@ InitProcessGlobals(void)
 #endif
 }
 
+/*
+ * Child processes use SIGUSR1 to for pmsignals.  pg_ctl uses SIGUSR1 to ask
+ * postmaster to check for logrotate and promote files.
+ */
+static void
+handle_action_request_signal(SIGNAL_ARGS)
+{
+	int save_errno = errno;
+
+	pending_action_request = true;
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
+
+/*
+ * pg_ctl uses SIGHUP to request a reload of the configuration files.
+ */
+static void
+handle_reload_request_signal(SIGNAL_ARGS)
+{
+	int save_errno = errno;
+
+	pending_reload_request = true;
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
 
 /*
- * SIGHUP -- reread config files, and tell children to do same
+ * Re-read config files, and tell children to do same.
  */
 static void
-SIGHUP_handler(SIGNAL_ARGS)
+process_reload_request(void)
 {
-	int			save_errno = errno;
+	pending_reload_request = false;
 
 	if (Shutdown <= SmartShutdown)
 	{
@@ -2771,27 +2774,47 @@ SIGHUP_handler(SIGNAL_ARGS)
 		write_nondefault_variables(PGC_SIGHUP);
 #endif
 	}
-
-	errno = save_errno;
 }
 
-
 /*
- * pmdie -- signal handler for processing various postmaster signals.
+ * pg_ctl uses SIGTERM, SIGINT and SIGQUIT to request different types of
+ * shutdown.
  */
 static void
-pmdie(SIGNAL_ARGS)
+handle_shutdown_request_signal(SIGNAL_ARGS)
 {
-	int			save_errno = errno;
-
-	ereport(DEBUG2,
-			(errmsg_internal("postmaster received signal %d",
-							 postgres_signal_arg)));
+	int save_errno = errno;
 
 	switch (postgres_signal_arg)
 	{
 		case SIGTERM:
+			pending_shutdown_request = SmartShutdown;
+			break;
+		case SIGINT:
+			pending_shutdown_request = FastShutdown;
+			break;
+		case SIGQUIT:
+			pending_shutdown_request = ImmediateShutdown;
+			break;
+	}
+	SetLatch(MyLatch);
+
+	errno = save_errno;
+}
 
+/*
+ * Process shutdown request.
+ */
+static void
+process_shutdown_request(Postmaster *postmaster)
+{
+	int		mode = pending_shutdown_request;
+
+	pending_shutdown_request = NoShutdown;
+
+	switch (mode)
+	{
+		case SmartShutdown:
 			/*
 			 * Smart Shutdown:
 			 *
@@ -2827,11 +2850,10 @@ pmdie(SIGNAL_ARGS)
 			 * that is already the case, PostmasterStateMachine will take the
 			 * next step.
 			 */
-			PostmasterStateMachine();
+			PostmasterStateMachine(postmaster);
 			break;
 
-		case SIGINT:
-
+		case FastShutdown:
 			/*
 			 * Fast Shutdown:
 			 *
@@ -2868,11 +2890,10 @@ pmdie(SIGNAL_ARGS)
 			 * PostmasterStateMachine will issue any necessary signals, or
 			 * take the next step if no child processes need to be killed.
 			 */
-			PostmasterStateMachine();
+			PostmasterStateMachine(postmaster);
 			break;
 
-		case SIGQUIT:
-
+		case ImmediateShutdown:
 			/*
 			 * Immediate Shutdown:
 			 *
@@ -2905,23 +2926,33 @@ pmdie(SIGNAL_ARGS)
 			 * Now wait for backends to exit.  If there are none,
 			 * PostmasterStateMachine will take the next step.
 			 */
-			PostmasterStateMachine();
+			PostmasterStateMachine(postmaster);
 			break;
 	}
+}
+
+static void
+handle_child_exit_signal(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	pending_child_exit = true;
+	SetLatch(MyLatch);
 
 	errno = save_errno;
 }
 
 /*
- * Reaper -- signal handler to cleanup after a child process dies.
+ * Cleanup after a child process dies.
  */
 static void
-reaper(SIGNAL_ARGS)
+process_child_exit(Postmaster *postmaster)
 {
-	int			save_errno = errno;
 	int			pid;			/* process id of dead child process */
 	int			exitstatus;		/* its exit status */
 
+	pending_child_exit = false;
+
 	ereport(DEBUG4,
 			(errmsg_internal("reaping dead processes")));
 
@@ -3212,9 +3243,7 @@ reaper(SIGNAL_ARGS)
 	 * After cleaning out the SIGCHLD queue, see if we have any state changes
 	 * or actions to make.
 	 */
-	PostmasterStateMachine();
-
-	errno = save_errno;
+	PostmasterStateMachine(postmaster);
 }
 
 /*
@@ -3642,11 +3671,12 @@ LogChildExit(int lev, const char *procname, int pid, int exitstatus)
 /*
  * Advance the postmaster's state machine and take actions as appropriate
  *
- * This is common code for pmdie(), reaper() and sigusr1_handler(), which
- * receive the signals that might mean we need to change state.
+ * This is common code for process_shutdown_request(), process_child_exit() and
+ * process_action_request(), which process the signals that might mean we need
+ * to change state.
  */
 static void
-PostmasterStateMachine(void)
+PostmasterStateMachine(Postmaster *postmaster)
 {
 	/* If we're doing a smart shutdown, try to advance that state. */
 	if (pmState == PM_RUN || pmState == PM_HOT_STANDBY)
@@ -3819,6 +3849,9 @@ PostmasterStateMachine(void)
 			Assert(AutoVacPID == 0);
 			/* syslogger is not considered here */
 			pmState = PM_NO_CHILDREN;
+
+			/* re-activate server socket events */
+			AdjustServerSocketEvents(postmaster, true);
 		}
 	}
 
@@ -3905,6 +3938,9 @@ PostmasterStateMachine(void)
 		pmState = PM_STARTUP;
 		/* crash recovery started, reset SIGKILL flag */
 		AbortStartTime = 0;
+
+		/* start accepting server socket connection events again */
+		reenable_server_socket_events = true;
 	}
 }
 
@@ -4094,6 +4130,7 @@ BackendStartup(Port *port)
 	/* Hasn't asked to be notified about any bgworkers yet */
 	bn->bgworker_notify = false;
 
+	PG_SETMASK(&BlockSig);
 #ifdef EXEC_BACKEND
 	pid = backend_forkexec(port);
 #else							/* !EXEC_BACKEND */
@@ -4124,6 +4161,7 @@ BackendStartup(Port *port)
 		BackendRun(port);
 	}
 #endif							/* EXEC_BACKEND */
+	PG_SETMASK(&UnBlockSig);
 
 	if (pid < 0)
 	{
@@ -5013,12 +5051,13 @@ ExitPostmaster(int status)
 }
 
 /*
- * sigusr1_handler - handle signal conditions from child processes
+ * Handle pmsignal conditions representing requests from backends,
+ * and check for promote and logrotate requests from pg_ctl.
  */
 static void
-sigusr1_handler(SIGNAL_ARGS)
+process_action_request(Postmaster *postmaster)
 {
-	int			save_errno = errno;
+	pending_action_request = false;
 
 	/*
 	 * RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
@@ -5143,7 +5182,7 @@ sigusr1_handler(SIGNAL_ARGS)
 	 */
 	if (CheckPostmasterSignal(PMSIGNAL_ADVANCE_STATE_MACHINE))
 	{
-		PostmasterStateMachine();
+		PostmasterStateMachine(postmaster);
 	}
 
 	if (StartupPID != 0 &&
@@ -5159,8 +5198,6 @@ sigusr1_handler(SIGNAL_ARGS)
 		 */
 		signal_child(StartupPID, SIGUSR2);
 	}
-
-	errno = save_errno;
 }
 
 /*
@@ -5271,6 +5308,7 @@ StartChildProcess(AuxProcType type)
 {
 	pid_t		pid;
 
+	PG_SETMASK(&BlockSig);
 #ifdef EXEC_BACKEND
 	{
 		char	   *av[10];
@@ -5310,6 +5348,7 @@ StartChildProcess(AuxProcType type)
 		AuxiliaryProcessMain(type); /* does not return */
 	}
 #endif							/* EXEC_BACKEND */
+	PG_SETMASK(&UnBlockSig);
 
 	if (pid < 0)
 	{
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index eb3a569aae..3bfef592eb 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -283,6 +283,17 @@ InitializeLatchSupport(void)
 #ifdef WAIT_USE_SIGNALFD
 	sigset_t	signalfd_mask;
 
+	if (IsUnderPostmaster)
+	{
+		if (signal_fd != -1)
+		{
+			/* Release postmaster's signal FD; ignore any error */
+			(void) close(signal_fd);
+			signal_fd = -1;
+			ReleaseExternalFD();
+		}
+	}
+
 	/* Block SIGURG, because we'll receive it through a signalfd. */
 	sigaddset(&UnBlockSig, SIGURG);
 
@@ -1069,6 +1080,7 @@ WaitEventAdjustEpoll(WaitEventSet *set, WaitEvent *event, int action)
 		Assert(event->fd != PGINVALID_SOCKET);
 		Assert(event->events & (WL_SOCKET_READABLE |
 								WL_SOCKET_WRITEABLE |
+								WL_SOCKET_IGNORE |
 								WL_SOCKET_CLOSED));
 
 		if (event->events & WL_SOCKET_READABLE)
@@ -1117,6 +1129,7 @@ WaitEventAdjustPoll(WaitEventSet *set, WaitEvent *event)
 	{
 		Assert(event->events & (WL_SOCKET_READABLE |
 								WL_SOCKET_WRITEABLE |
+								WL_SOCKET_IGNORE |
 								WL_SOCKET_CLOSED));
 		pollfd->events = 0;
 		if (event->events & WL_SOCKET_READABLE)
@@ -1201,6 +1214,7 @@ WaitEventAdjustKqueue(WaitEventSet *set, WaitEvent *event, int old_events)
 		   event->events == WL_POSTMASTER_DEATH ||
 		   (event->events & (WL_SOCKET_READABLE |
 							 WL_SOCKET_WRITEABLE |
+							 WL_SOCKET_IGNORE |
 							 WL_SOCKET_CLOSED)));
 
 	if (event->events == WL_POSTMASTER_DEATH)
@@ -1312,6 +1326,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
 			flags |= FD_WRITE;
 		if (event->events & WL_SOCKET_CONNECTED)
 			flags |= FD_CONNECT;
+		if (event->events & WL_SOCKET_ACCEPT)
+			flags |= FD_ACCEPT;
 
 		if (*handle == WSA_INVALID_EVENT)
 		{
@@ -2067,6 +2083,12 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 			/* connected */
 			occurred_events->events |= WL_SOCKET_CONNECTED;
 		}
+		if ((cur_event->events & WL_SOCKET_ACCEPT) &&
+			(resEvents.lNetworkEvents & FD_ACCEPT))
+		{
+			/* incoming connection ready to accept */
+			occurred_events->events |= WL_SOCKET_ACCEPT;
+		}
 		if (resEvents.lNetworkEvents & FD_CLOSE)
 		{
 			/* EOF/error, so signal all caller-requested socket flags */
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3082093d1e..655e881688 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -24,7 +24,6 @@
 #include <signal.h>
 #include <unistd.h>
 #include <sys/resource.h>
-#include <sys/select.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index eb1046450b..1348261220 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -135,8 +135,7 @@ InitPostmasterChild(void)
 
 	/* Initialize process-local latch support */
 	InitializeLatchSupport();
-	MyLatch = &LocalLatchData;
-	InitLatch(MyLatch);
+	InitLocalLatch();
 	InitializeLatchWaitSet();
 
 	/*
@@ -189,8 +188,7 @@ InitStandaloneProcess(const char *argv0)
 
 	/* Initialize process-local latch support */
 	InitializeLatchSupport();
-	MyLatch = &LocalLatchData;
-	InitLatch(MyLatch);
+	InitLocalLatch();
 	InitializeLatchWaitSet();
 
 	/*
@@ -232,6 +230,13 @@ SwitchToSharedLatch(void)
 	SetLatch(MyLatch);
 }
 
+void
+InitLocalLatch(void)
+{
+	MyLatch = &LocalLatchData;
+	InitLatch(MyLatch);
+}
+
 void
 SwitchBackToLocalLatch(void)
 {
diff --git a/src/include/libpq/pqsignal.h b/src/include/libpq/pqsignal.h
index 7890b426a8..76eb380a4f 100644
--- a/src/include/libpq/pqsignal.h
+++ b/src/include/libpq/pqsignal.h
@@ -53,7 +53,4 @@ extern PGDLLIMPORT sigset_t StartupBlockSig;
 
 extern void pqinitmask(void);
 
-/* pqsigfunc is declared in src/include/port.h */
-extern pqsigfunc pqsignal_pm(int signo, pqsigfunc func);
-
 #endif							/* PQSIGNAL_H */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 795182fa51..0975867197 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -310,6 +310,7 @@ extern PGDLLIMPORT char *DatabasePath;
 /* now in utils/init/miscinit.c */
 extern void InitPostmasterChild(void);
 extern void InitStandaloneProcess(const char *argv0);
+extern void InitLocalLatch(void);
 extern void SwitchToSharedLatch(void);
 extern void SwitchBackToLocalLatch(void);
 
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 68ab740f16..ce1f4bd44e 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -135,10 +135,17 @@ typedef struct Latch
 #define WL_SOCKET_CONNECTED  WL_SOCKET_WRITEABLE
 #endif
 #define WL_SOCKET_CLOSED 	 (1 << 7)
+#ifdef WIN32
+#define WL_SOCKET_ACCEPT	 (1 << 8)
+#else
+#define WL_SOCKET_ACCEPT	 WL_SOCKET_READABLE
+#endif
+#define WL_SOCKET_IGNORE	 (1 << 9)
 #define WL_SOCKET_MASK		(WL_SOCKET_READABLE | \
 							 WL_SOCKET_WRITEABLE | \
 							 WL_SOCKET_CONNECTED | \
-							 WL_SOCKET_CLOSED)
+							 WL_SOCKET_CLOSED | \
+							 WL_SOCKET_ACCEPT)
 
 typedef struct WaitEvent
 {
-- 
2.38.1

Reply via email to