Windows has support for some signals[0], like SIGTERM and SIGINT. SIGINT
must be handled with care on Windows since it is handled in a separate
thread. SIGTERM however can be handled in a similar way to UNIX-like
systems. I audited a few pqsignal calls that were blocked by WIN32 to
see if they could become used, and made some adjustments. Definitely
hoping for someone with more Windows knowledge to audit this.

In addition, I found that signal_cleanup() in pg_test_fsync.c was not
using signal-safe functions, so I went ahead and fixed those to use
their signal-safe equivalents.

[0]: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/signal

-- 
Tristan Partin
Neon (https://neon.tech)
From 2c4573afe55e83d3b5c99460c7429ff7cf3adcdf Mon Sep 17 00:00:00 2001
From: Tristan Partin <tris...@neon.tech>
Date: Thu, 6 Jul 2023 15:25:14 -0500
Subject: [PATCH v1 1/2] Use signal-safe functions in signal handler

According to signal-safety(7), exit(3) and puts(3) are not safe to call
in a signal handler.
---
 src/bin/pg_test_fsync/pg_test_fsync.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index 435df8d808..2fc4b69444 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -596,8 +596,8 @@ signal_cleanup(SIGNAL_ARGS)
 	if (needs_unlink)
 		unlink(filename);
 	/* Finish incomplete line on stdout */
-	puts("");
-	exit(1);
+	write(STDOUT_FILENO, "", 1);
+	_exit(1);
 }
 
 #ifdef HAVE_FSYNC_WRITETHROUGH
-- 
Tristan Partin
Neon (https://neon.tech)

From ce4cbf8e720301e7ddbd2f0816c1f03c82138515 Mon Sep 17 00:00:00 2001
From: Tristan Partin <tris...@neon.tech>
Date: Thu, 6 Jul 2023 15:17:36 -0500
Subject: [PATCH v1 2/2] Cleanup some signal usage on Windows

SIGTERM can be handled the same as on a UNIX-like system. SIGINT, on the
other hand, requires special considerations. The Windows documentation
says the following:

> SIGINT is not supported for any Win32 application. When a CTRL+C
> interrupt occurs, Win32 operating systems generate a new thread to
> specifically handle that interrupt. This can cause a single-thread
> application, such as one in UNIX, to become multithreaded and cause
> unexpected behavior.

Therefore, if all the SIGINT handler does is read from memory and/or
exit, we can use the same handler. If the signal handler writes to
memory, such as setting a boolean, the original thread would never
become aware of the changed state.
---
 src/bin/initdb/initdb.c                | 17 ++++-------------
 src/bin/pg_basebackup/pg_receivewal.c  |  5 +----
 src/bin/pg_basebackup/pg_recvlogical.c |  9 ++++-----
 src/bin/pg_test_fsync/pg_test_fsync.c  |  3 ---
 4 files changed, 9 insertions(+), 25 deletions(-)

diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 3f4167682a..cac58eae2a 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2773,26 +2773,17 @@ void
 setup_signals(void)
 {
 	/* some of these are not valid on Windows */
-#ifdef SIGHUP
-	pqsignal(SIGHUP, trapsig);
-#endif
-#ifdef SIGINT
+	pqsignal(SIGTERM, trapsig);
+
+#ifndef WIN32
 	pqsignal(SIGINT, trapsig);
-#endif
-#ifdef SIGQUIT
+	pqsignal(SIGHUP, trapsig);
 	pqsignal(SIGQUIT, trapsig);
-#endif
-#ifdef SIGTERM
-	pqsignal(SIGTERM, trapsig);
-#endif
 
 	/* Ignore SIGPIPE when writing to backend, so we can clean up */
-#ifdef SIGPIPE
 	pqsignal(SIGPIPE, SIG_IGN);
-#endif
 
 	/* Prevent SIGSYS so we can probe for kernel calls that might not work */
-#ifdef SIGSYS
 	pqsignal(SIGSYS, SIG_IGN);
 #endif
 }
diff --git a/src/bin/pg_basebackup/pg_receivewal.c b/src/bin/pg_basebackup/pg_receivewal.c
index d0a4079d50..55143c4037 100644
--- a/src/bin/pg_basebackup/pg_receivewal.c
+++ b/src/bin/pg_basebackup/pg_receivewal.c
@@ -611,14 +611,11 @@ StreamLog(void)
  * When SIGINT/SIGTERM are caught, just tell the system to exit at the next
  * possible moment.
  */
-#ifndef WIN32
-
 static void
 sigexit_handler(SIGNAL_ARGS)
 {
 	time_to_stop = true;
 }
-#endif
 
 int
 main(int argc, char **argv)
@@ -838,9 +835,9 @@ main(int argc, char **argv)
 	 * Trap signals.  (Don't do this until after the initial password prompt,
 	 * if one is needed, in GetConnection.)
 	 */
+	pqsignal(SIGTERM, sigexit_handler);
 #ifndef WIN32
 	pqsignal(SIGINT, sigexit_handler);
-	pqsignal(SIGTERM, sigexit_handler);
 #endif
 
 	/*
diff --git a/src/bin/pg_basebackup/pg_recvlogical.c b/src/bin/pg_basebackup/pg_recvlogical.c
index f3c7937a1d..1228dc03db 100644
--- a/src/bin/pg_basebackup/pg_recvlogical.c
+++ b/src/bin/pg_basebackup/pg_recvlogical.c
@@ -644,10 +644,6 @@ error:
 	conn = NULL;
 }
 
-/*
- * Unfortunately we can't do sensible signal handling on windows...
- */
-#ifndef WIN32
 
 /*
  * When SIGINT/SIGTERM are caught, just tell the system to exit at the next
@@ -659,6 +655,9 @@ sigexit_handler(SIGNAL_ARGS)
 	time_to_abort = true;
 }
 
+/* SIGHUP is not defined on Windows */
+#ifndef WIN32
+
 /*
  * Trigger the output file to be reopened.
  */
@@ -921,9 +920,9 @@ main(int argc, char **argv)
 	 * Trap signals.  (Don't do this until after the initial password prompt,
 	 * if one is needed, in GetConnection.)
 	 */
+	pqsignal(SIGTERM, sigexit_handler);
 #ifndef WIN32
 	pqsignal(SIGINT, sigexit_handler);
-	pqsignal(SIGTERM, sigexit_handler);
 	pqsignal(SIGHUP, sighup_handler);
 #endif
 
diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index 2fc4b69444..263f5867a2 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -109,9 +109,6 @@ main(int argc, char *argv[])
 	pqsignal(SIGTERM, signal_cleanup);
 #ifndef WIN32
 	pqsignal(SIGALRM, process_alarm);
-#endif
-#ifdef SIGHUP
-	/* Not defined on win32 */
 	pqsignal(SIGHUP, signal_cleanup);
 #endif
 
-- 
Tristan Partin
Neon (https://neon.tech)

Reply via email to