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
