From e57bc91775ee4a888a4f11908cbd72ceadbd2ae5 Mon Sep 17 00:00:00 2001
From: Greg Burd <greg@burd.me>
Date: Thu, 12 Nov 2026 12:12:40 -0500
Subject: [PATCH v4 07/21] port: Fix two bugs in the native Windows
 pg_threads.h mapping.

pg_cnd_timedwait() reported a timeout as an error, because the
SleepConditionVariable*() functions fail with ERROR_TIMEOUT, while
pg_thrd_map_windows_error() only recognized WAIT_TIMEOUT, which is
what the WaitFor*() functions return.  Map both to pg_thrd_timedout.
test_pg_threads found this when built with PG_THREADS_USE_WINDOWS_H.

pg_tss_ensure_fls_registered() checked pg_tss_fls without holding
the lock, which is a data race, and it returned an uninitialized
result when FlsAlloc() failed.  Allocate the index with
pg_call_once() instead, keeping the error code for the failure
report.  A failure is now remembered rather than retried, which
seems fine for running out of FLS indexes.
---
 src/port/pg_threads.c | 35 ++++++++++++-----------------------
 1 file changed, 12 insertions(+), 23 deletions(-)

diff --git a/src/port/pg_threads.c b/src/port/pg_threads.c
index 3c0b1a2aeaa..292771c94aa 100644
--- a/src/port/pg_threads.c
+++ b/src/port/pg_threads.c
@@ -142,6 +142,7 @@ pg_thrd_map_windows_error(DWORD windows_error)
 			return pg_thrd_nomem;
 		case ERROR_BUSY:
 			return pg_thrd_busy;
+		case ERROR_TIMEOUT:
 		case WAIT_TIMEOUT:
 			return pg_thrd_timedout;
 		default:
@@ -306,6 +307,8 @@ static thread_local pg_thrd_t pg_thrd_current_thread_local;
 static thread_local pg_tss_value pg_tss_values[PG_TSS_MAX];
 static pg_tss_slot pg_tss_slots[PG_TSS_MAX];
 static DWORD pg_tss_fls = FLS_OUT_OF_INDEXES;
+static DWORD pg_tss_fls_error;
+static pg_once_flag pg_tss_fls_once = PG_ONCE_FLAG_INIT;
 static pg_mtx_t pg_tss_lock = PG_MTX_PLAIN_INIT;
 
 static BOOL CALLBACK
@@ -623,28 +626,12 @@ pg_tss_fls_destructor(void *data)
 	}
 }
 
-static bool
-pg_tss_ensure_fls_registered(void)
+static void
+pg_tss_register_fls(void)
 {
-	bool		success;
-
-	if (pg_tss_fls != FLS_OUT_OF_INDEXES)
-		return true;			/* fast exit, already set */
-
-	pg_mtx_lock(&pg_tss_lock);
-	if (pg_tss_fls != FLS_OUT_OF_INDEXES)
-	{
-		success = true;
-	}
-	else
-	{
-		pg_tss_fls = FlsAlloc(pg_tss_fls_destructor);
-		if (pg_tss_fls != FLS_OUT_OF_INDEXES)
-			success = true;
-	}
-	pg_mtx_unlock(&pg_tss_lock);
-
-	return success;
+	pg_tss_fls = FlsAlloc(pg_tss_fls_destructor);
+	if (pg_tss_fls == FLS_OUT_OF_INDEXES)
+		pg_tss_fls_error = GetLastError();
 }
 
 int
@@ -652,8 +639,10 @@ pg_tss_create_impl(pg_tss_t *id, pg_tss_dtor_t dtor)
 {
 	bool		created = false;
 
-	if (!pg_tss_ensure_fls_registered())
-		return pg_thrd_map_windows_error(GetLastError());
+	/* Allocate the FLS index that runs our destructors, on first use. */
+	pg_call_once(&pg_tss_fls_once, pg_tss_register_fls);
+	if (pg_tss_fls == FLS_OUT_OF_INDEXES)
+		return pg_thrd_map_windows_error(pg_tss_fls_error);
 
 	pg_mtx_lock(&pg_tss_lock);
 	for (int i = 0; i < lengthof(pg_tss_slots); ++i)
-- 
2.54.0

