On Sat, Jul 16, 2022 at 5:18 PM Thomas Munro <thomas.mu...@gmail.com> wrote:
> On Sat, Jul 16, 2022 at 1:28 AM Tom Lane <t...@sss.pgh.pa.us> wrote:
> > Thomas Munro <thomas.mu...@gmail.com> writes:
> > > On Fri, Jul 15, 2022 at 9:34 AM Tom Lane <t...@sss.pgh.pa.us> wrote:
> > >> (Someday we oughta go ahead and make our Windows signal API look more
> > >> like POSIX, as I suggested back in 2015.  I'm still not taking
> > >> point on that, though.)
> >
> > > For the sigprocmask() part, here's a patch that passes CI.  Only the
> > > SIG_SETMASK case is actually exercised by our current code, though.
> >
> > Passes an eyeball check, but I can't actually test it.
>
> Thanks.  Pushed.
>
> I'm not brave enough to try to write a replacement sigaction() yet,
> but it does appear that we could rip more ugliness and inconsistencies
> that way, eg sa_mask.

Here's a draft patch that adds a minimal sigaction() implementation
for Windows.  It doesn't implement stuff we're not using, for sample
sa_sigaction functions, but it has the sa_mask feature so we can
harmonize the stuff that I believe you were talking about.
From f00f169e10b884dab337b73b31de89577fb662de Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.mu...@gmail.com>
Date: Wed, 17 Aug 2022 07:36:53 +1200
Subject: [PATCH] Provide sigaction for Windows.

Harmonize Unix and Windows code by modernizing our signal emulation to
use sigaction() instead of signal().

Discussion: https://postgr.es/m/CA%2BhUKGK5ax1AoFMvXksLROrxwB1%3D-EsT%3D7%2B6oUtCn47GbC9zjA%40mail.gmail.com
---
 src/backend/libpq/pqsignal.c        |  9 -----
 src/backend/port/win32/signal.c     | 41 +++++++++++++++------
 src/backend/postmaster/postmaster.c | 57 ++---------------------------
 src/include/libpq/pqsignal.h        | 14 +++++++
 src/port/pqsignal.c                 | 22 +++++------
 5 files changed, 56 insertions(+), 87 deletions(-)

diff --git a/src/backend/libpq/pqsignal.c b/src/backend/libpq/pqsignal.c
index 26ed671d1a..1ab34c5214 100644
--- a/src/backend/libpq/pqsignal.c
+++ b/src/backend/libpq/pqsignal.c
@@ -110,16 +110,10 @@ pqinitmask(void)
  * postmaster ever unblocks signals.
  *
  * pqinitmask() must have been invoked previously.
- *
- * On Windows, this function is just an alias for pqsignal()
- * (and note that it's calling the code in src/backend/port/win32/signal.c,
- * not src/port/pqsignal.c).  On that platform, the postmaster's signal
- * handlers still have to block signals for themselves.
  */
 pqsigfunc
 pqsignal_pm(int signo, pqsigfunc func)
 {
-#ifndef WIN32
 	struct sigaction act,
 				oact;
 
@@ -142,7 +136,4 @@ pqsignal_pm(int signo, pqsigfunc func)
 	if (sigaction(signo, &act, &oact) < 0)
 		return SIG_ERR;
 	return oact.sa_handler;
-#else							/* WIN32 */
-	return pqsignal(signo, func);
-#endif
 }
diff --git a/src/backend/port/win32/signal.c b/src/backend/port/win32/signal.c
index 53b93a50b2..d533de1bc6 100644
--- a/src/backend/port/win32/signal.c
+++ b/src/backend/port/win32/signal.c
@@ -34,7 +34,7 @@ HANDLE		pgwin32_initial_signal_pipe = INVALID_HANDLE_VALUE;
 static CRITICAL_SECTION pg_signal_crit_sec;
 
 /* Note that array elements 0 are unused since they correspond to signal 0 */
-static pqsigfunc pg_signal_array[PG_SIGNAL_COUNT];
+static struct sigaction pg_signal_array[PG_SIGNAL_COUNT];
 static pqsigfunc pg_signal_defaults[PG_SIGNAL_COUNT];
 
 
@@ -85,7 +85,9 @@ pgwin32_signal_initialize(void)
 
 	for (i = 0; i < PG_SIGNAL_COUNT; i++)
 	{
-		pg_signal_array[i] = SIG_DFL;
+		pg_signal_array[i].sa_handler = SIG_DFL;
+		pg_signal_array[i].sa_mask = 0;
+		pg_signal_array[i].sa_flags = 0;
 		pg_signal_defaults[i] = SIG_IGN;
 	}
 	pg_signal_mask = 0;
@@ -131,15 +133,27 @@ pgwin32_dispatch_queued_signals(void)
 			if (exec_mask & sigmask(i))
 			{
 				/* Execute this signal */
-				pqsigfunc	sig = pg_signal_array[i];
+				struct sigaction *act = &pg_signal_array[i];
+				pqsigfunc	sig = act->sa_handler;
 
 				if (sig == SIG_DFL)
 					sig = pg_signal_defaults[i];
 				pg_signal_queue &= ~sigmask(i);
 				if (sig != SIG_ERR && sig != SIG_IGN && sig != SIG_DFL)
 				{
+					sigset_t	block_mask;
+					sigset_t	save_mask;
+
 					LeaveCriticalSection(&pg_signal_crit_sec);
+
+					block_mask = act->sa_mask;
+					if ((act->sa_flags & SA_NODEFER) == 0)
+						block_mask |= sigmask(i);
+
+					sigprocmask(SIG_BLOCK, &block_mask, &save_mask);
 					sig(i);
+					sigprocmask(SIG_SETMASK, &save_mask, NULL);
+
 					EnterCriticalSection(&pg_signal_crit_sec);
 					break;		/* Restart outer loop, in case signal mask or
 								 * queue has been modified inside signal
@@ -187,22 +201,25 @@ pqsigprocmask(int how, const sigset_t *set, sigset_t *oset)
 	return 0;
 }
 
-
 /*
  * Unix-like signal handler installation
  *
  * Only called on main thread, no sync required
  */
-pqsigfunc
-pqsignal(int signum, pqsigfunc handler)
+int
+pqsigaction(int signum, const struct sigaction *act,
+			struct sigaction *oldact)
 {
-	pqsigfunc	prevfunc;
-
 	if (signum >= PG_SIGNAL_COUNT || signum < 0)
-		return SIG_ERR;
-	prevfunc = pg_signal_array[signum];
-	pg_signal_array[signum] = handler;
-	return prevfunc;
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (oldact)
+		*oldact = pg_signal_array[signum];
+	if (act)
+		pg_signal_array[signum] = *act;
+	return 0;
 }
 
 /* Create the signal listener pipe for specified PID */
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 8a038d1b2a..9227061cad 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -620,10 +620,10 @@ PostmasterMain(int argc, char *argv[])
 	 * is used by all child processes and client processes).  That has a
 	 * couple of special behaviors:
 	 *
-	 * 1. Except on Windows, 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.
+	 * 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
@@ -2730,14 +2730,6 @@ SIGHUP_handler(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
-	/*
-	 * We rely on the signal mechanism to have blocked all signals ... except
-	 * on Windows, which lacks sigaction(), so we have to do it manually.
-	 */
-#ifdef WIN32
-	PG_SETMASK(&BlockSig);
-#endif
-
 	if (Shutdown <= SmartShutdown)
 	{
 		ereport(LOG,
@@ -2794,10 +2786,6 @@ SIGHUP_handler(SIGNAL_ARGS)
 #endif
 	}
 
-#ifdef WIN32
-	PG_SETMASK(&UnBlockSig);
-#endif
-
 	errno = save_errno;
 }
 
@@ -2810,14 +2798,6 @@ pmdie(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
-	/*
-	 * We rely on the signal mechanism to have blocked all signals ... except
-	 * on Windows, which lacks sigaction(), so we have to do it manually.
-	 */
-#ifdef WIN32
-	PG_SETMASK(&BlockSig);
-#endif
-
 	ereport(DEBUG2,
 			(errmsg_internal("postmaster received signal %d",
 							 postgres_signal_arg)));
@@ -2942,10 +2922,6 @@ pmdie(SIGNAL_ARGS)
 			break;
 	}
 
-#ifdef WIN32
-	PG_SETMASK(&UnBlockSig);
-#endif
-
 	errno = save_errno;
 }
 
@@ -2959,14 +2935,6 @@ reaper(SIGNAL_ARGS)
 	int			pid;			/* process id of dead child process */
 	int			exitstatus;		/* its exit status */
 
-	/*
-	 * We rely on the signal mechanism to have blocked all signals ... except
-	 * on Windows, which lacks sigaction(), so we have to do it manually.
-	 */
-#ifdef WIN32
-	PG_SETMASK(&BlockSig);
-#endif
-
 	ereport(DEBUG4,
 			(errmsg_internal("reaping dead processes")));
 
@@ -3259,11 +3227,6 @@ reaper(SIGNAL_ARGS)
 	 */
 	PostmasterStateMachine();
 
-	/* Done with signal handler */
-#ifdef WIN32
-	PG_SETMASK(&UnBlockSig);
-#endif
-
 	errno = save_errno;
 }
 
@@ -5110,14 +5073,6 @@ sigusr1_handler(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
-	/*
-	 * We rely on the signal mechanism to have blocked all signals ... except
-	 * on Windows, which lacks sigaction(), so we have to do it manually.
-	 */
-#ifdef WIN32
-	PG_SETMASK(&BlockSig);
-#endif
-
 	/*
 	 * RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
 	 * unexpected states. If the startup process quickly starts up, completes
@@ -5258,10 +5213,6 @@ sigusr1_handler(SIGNAL_ARGS)
 		signal_child(StartupPID, SIGUSR2);
 	}
 
-#ifdef WIN32
-	PG_SETMASK(&UnBlockSig);
-#endif
-
 	errno = save_errno;
 }
 
diff --git a/src/include/libpq/pqsignal.h b/src/include/libpq/pqsignal.h
index d17ddb787e..7890b426a8 100644
--- a/src/include/libpq/pqsignal.h
+++ b/src/include/libpq/pqsignal.h
@@ -21,12 +21,26 @@
 /* Emulate POSIX sigset_t APIs on Windows */
 typedef int sigset_t;
 
+#define SA_RESTART				1
+#define SA_NODEFER				2
+
+struct sigaction
+{
+	void		(*sa_handler) (int);
+	/* sa_sigaction not yet implemented */
+	sigset_t	sa_mask;
+	int			sa_flags;
+};
+
 extern int	pqsigprocmask(int how, const sigset_t *set, sigset_t *oset);
+extern int	pqsigaction(int signum, const struct sigaction *act,
+						struct sigaction *oldact);
 
 #define SIG_BLOCK				1
 #define SIG_UNBLOCK				2
 #define SIG_SETMASK				3
 #define sigprocmask(how, set, oset) pqsigprocmask((how), (set), (oset))
+#define sigaction(signum, act, oldact) pqsigaction((signum), (act), (oldact))
 #define sigemptyset(set)		(*(set) = 0)
 #define sigfillset(set)			(*(set) = ~0)
 #define sigaddset(set, signum)	(*(set) |= (sigmask(signum)))
diff --git a/src/port/pqsignal.c b/src/port/pqsignal.c
index 6cb0320edb..fbb35005f4 100644
--- a/src/port/pqsignal.c
+++ b/src/port/pqsignal.c
@@ -12,15 +12,10 @@
  *	  src/port/pqsignal.c
  *
  *	We now assume that all Unix-oid systems have POSIX sigaction(2)
- *	with support for restartable signals (SA_RESTART).  We used to also
- *	support BSD-style signal(2), but there really shouldn't be anything
- *	out there anymore that doesn't have the POSIX API.
+ *	with support for restartable signals (SA_RESTART).
  *
- *	Windows, of course, is resolutely in a class by itself.  In the backend,
- *	we don't use this file at all; src/backend/port/win32/signal.c provides
- *	pqsignal() for the backend environment.  Frontend programs can use
- *	this version of pqsignal() if they wish, but beware that this does
- *	not provide restartable signals on Windows.
+ *	Frontend programs can use this version of pqsignal() if they wish, but
+ *	beware that this does not provide restartable signals on Windows.
  *
  * ------------------------------------------------------------------------
  */
@@ -29,7 +24,9 @@
 
 #include <signal.h>
 
-#if !defined(WIN32) || defined(FRONTEND)
+#ifndef FRONTEND
+#include "libpq/pqsignal.h"
+#endif
 
 /*
  * Set up a signal handler, with SA_RESTART, for signal "signo"
@@ -39,7 +36,7 @@
 pqsigfunc
 pqsignal(int signo, pqsigfunc func)
 {
-#ifndef WIN32
+#if !(defined(WIN32) && defined(FRONTEND))
 	struct sigaction act,
 				oact;
 
@@ -53,9 +50,8 @@ pqsignal(int signo, pqsigfunc func)
 	if (sigaction(signo, &act, &oact) < 0)
 		return SIG_ERR;
 	return oact.sa_handler;
-#else							/* WIN32 */
+#else
+	/* Forward to Windows native signal system. */
 	return signal(signo, func);
 #endif
 }
-
-#endif							/* !defined(WIN32) || defined(FRONTEND) */
-- 
2.37.1

Reply via email to