Attached is v2, rebased over master. Only 0003 changed to remove a
declaration it had as context. The fixes are identical to v1.

Of the four, only 0001 has any practical server impact. If CreateThread
fails, every timer-based timeout in that process is silently disabled
for the rest of its lifetime. 0002 can hang pg_test_fsync. 0003 and
0004 are defensive fixes with no reachable in-core trigger.

I'll add this to the next commitfest.


Regards,
-- Sehrope Sarkuni
Founder & CEO | JackDB, Inc. | https://www.jackdb.com/
From d453fd0c24f3fba6a02e11bec6cee099fe1b5267 Mon Sep 17 00:00:00 2001
From: Sehrope Sarkuni <[email protected]>
Date: Wed, 10 Jun 2026 08:23:53 -0400
Subject: [PATCH v2 3/4] Fix off-by-one in pgwin32_select event array

The events array must hold up to 2*FD_SETSIZE socket events plus the
signal event. With full disjoint read and write fd sets the signal
event was written one element past the end of the array.
---
 src/backend/port/win32/socket.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/backend/port/win32/socket.c b/src/backend/port/win32/socket.c
index e16fd85ddd4..cf64983bece 100644
--- a/src/backend/port/win32/socket.c
+++ b/src/backend/port/win32/socket.c
@@ -516,9 +516,10 @@ pgwin32_send(SOCKET s, const void *buf, int len, int flags)
 int
 pgwin32_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout)
 {
-	WSAEVENT	events[FD_SETSIZE * 2]; /* worst case is readfds totally
-										 * different from writefds, so
-										 * 2*FD_SETSIZE sockets */
+	WSAEVENT	events[FD_SETSIZE * 2 + 1]; /* worst case is readfds totally
+											 * different from writefds, so
+											 * 2*FD_SETSIZE sockets, plus one
+											 * for the signal event */
 	SOCKET		sockets[FD_SETSIZE * 2];
 	int			numevents = 0;
 	int			r;
-- 
2.17.1

From 53e1f4ac0027649ae3445898552cc9c8aa2cc8fd Mon Sep 17 00:00:00 2001
From: Sehrope Sarkuni <[email protected]>
Date: Wed, 10 Jun 2026 09:00:44 -0400
Subject: [PATCH v2 4/4] Do not treat a failed wait as a timeout in win32
 pg_usleep

Only WAIT_OBJECT_0 was distinguished, so WAIT_FAILED silently turned
the sleep into an immediate return. Callers using pg_usleep in retry
loops would then spin at full speed if the event handle were ever
bad.
---
 src/backend/port/win32/signal.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/src/backend/port/win32/signal.c b/src/backend/port/win32/signal.c
index 1ef0ca999e1..66a05d966aa 100644
--- a/src/backend/port/win32/signal.c
+++ b/src/backend/port/win32/signal.c
@@ -63,13 +63,18 @@ pg_usleep(long microsec)
 		return;
 	}
 
-	if (WaitForSingleObject(pgwin32_signal_event,
-							(microsec < 500 ? 1 : (microsec + 500) / 1000))
-		== WAIT_OBJECT_0)
+	switch (WaitForSingleObject(pgwin32_signal_event,
+								(microsec < 500 ? 1 : (microsec + 500) / 1000)))
 	{
-		pgwin32_dispatch_queued_signals();
-		errno = EINTR;
-		return;
+		case WAIT_OBJECT_0:
+			pgwin32_dispatch_queued_signals();
+			errno = EINTR;
+			return;
+		case WAIT_TIMEOUT:
+			return;
+		default:
+			elog(FATAL, "could not wait on signal event: error code %lu",
+				 GetLastError());
 	}
 }
 
-- 
2.17.1

From fa4f2d6b4891cf193c2b154f58f0c3a484b03abf Mon Sep 17 00:00:00 2001
From: Sehrope Sarkuni <[email protected]>
Date: Wed, 10 Jun 2026 08:22:43 -0400
Subject: [PATCH v2 1/4] Fix CreateThread failure check in win32 timer code

CreateThread returns NULL on failure, not INVALID_HANDLE_VALUE, so the
error branch could never fire and a failed thread creation would
silently disable all timer-based timeouts.
---
 src/backend/port/win32/timer.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/backend/port/win32/timer.c b/src/backend/port/win32/timer.c
index 751327ef03e..16c2114c084 100644
--- a/src/backend/port/win32/timer.c
+++ b/src/backend/port/win32/timer.c
@@ -103,7 +103,7 @@ setitimer(int which, const struct itimerval *value, struct itimerval *ovalue)
 		InitializeCriticalSection(&timerCommArea.crit_sec);
 
 		timerThreadHandle = CreateThread(NULL, 0, pg_timer_thread, NULL, 0, NULL);
-		if (timerThreadHandle == INVALID_HANDLE_VALUE)
+		if (timerThreadHandle == NULL)
 			ereport(FATAL,
 					(errmsg_internal("could not create timer thread: error code %lu",
 									 GetLastError())));
-- 
2.17.1

From f4385467bfd9ed9d85a458511e10fd6ca76c63a3 Mon Sep 17 00:00:00 2001
From: Sehrope Sarkuni <[email protected]>
Date: Wed, 10 Jun 2026 09:32:14 -0400
Subject: [PATCH v2 2/4] Fix CreateThread failure check in pg_test_fsync

CreateThread returns NULL on failure, not INVALID_HANDLE_VALUE, so
the error branch could never fire.
---
 src/bin/pg_test_fsync/pg_test_fsync.c | 3 +--
 1 file changed, 1 insertion(+), 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 c51b1271f1f..39d0792614d 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -50,8 +50,7 @@ do { \
 #define START_TIMER \
 do { \
 	alarm_triggered = false; \
-	if (CreateThread(NULL, 0, process_alarm, NULL, 0, NULL) == \
-		INVALID_HANDLE_VALUE) \
+	if (CreateThread(NULL, 0, process_alarm, NULL, 0, NULL) == NULL) \
 		pg_fatal("could not create thread for alarm"); \
 	gettimeofday(&start_t, NULL); \
 } while (0)
-- 
2.17.1

Reply via email to