From 95ba1d70dd3143b5c3f393cf7d20d4cd4f002725 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 1 Jul 2026 17:15:26 +1200
Subject: [PATCH v1 07/14] libpq: Use pg_threads.h.

* adopt pg_threads.h for mutexes
* remove a local pthreads abstraction

Discussion:
Reviewed-by:
---
 src/interfaces/libpq/Makefile            |  3 +-
 src/interfaces/libpq/fe-auth-oauth.c     |  7 +--
 src/interfaces/libpq/fe-connect.c        | 13 ++---
 src/interfaces/libpq/fe-misc.c           |  7 +--
 src/interfaces/libpq/fe-secure-openssl.c | 15 ++----
 src/interfaces/libpq/fe-secure.c         |  7 +--
 src/interfaces/libpq/libpq-int.h         |  5 --
 src/interfaces/libpq/meson.build         |  5 +-
 src/interfaces/libpq/pthread-win32.c     | 66 ------------------------
 src/port/pthread-win32.h                 | 31 -----------
 src/tools/pginclude/headerscheck         |  1 -
 11 files changed, 21 insertions(+), 139 deletions(-)
 delete mode 100644 src/interfaces/libpq/pthread-win32.c
 delete mode 100644 src/port/pthread-win32.h

diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 0963995eed4..6756dcd534f 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -75,7 +75,6 @@ endif
 
 ifeq ($(PORTNAME), win32)
 OBJS += \
-	pthread-win32.o \
 	win32.o
 endif
 
@@ -187,6 +186,6 @@ uninstall: uninstall-lib
 clean distclean: clean-lib
 	$(MAKE) -C test $@
 	rm -rf tmp_check
-	rm -f $(OBJS) $(OBJS_SHLIB) $(OBJS_STATIC) pthread.h libpq-refs-stamp
+	rm -f $(OBJS) $(OBJS_SHLIB) $(OBJS_STATIC) libpq-refs-stamp
 # Might be left over from a Win32 client-only build
 	rm -f pg_config_paths.h
diff --git a/src/interfaces/libpq/fe-auth-oauth.c b/src/interfaces/libpq/fe-auth-oauth.c
index 826f7461cb3..ba637e8e7e2 100644
--- a/src/interfaces/libpq/fe-auth-oauth.c
+++ b/src/interfaces/libpq/fe-auth-oauth.c
@@ -28,6 +28,7 @@
 #include "mb/pg_wchar.h"
 #include "oauth-debug.h"
 #include "pg_config_paths.h"
+#include "port/pg_threads.h"
 #include "utils/memdebug.h"
 
 static PostgresPollingStatusType do_async(fe_oauth_state *state,
@@ -869,7 +870,7 @@ static int
 use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *request)
 {
 	static bool initialized = false;
-	static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
+	static pg_mtx_t init_mutex = PG_MTX_INIT;
 	int			lockerr;
 
 	void		(*init) (libpq_gettext_func gettext_impl);
@@ -946,7 +947,7 @@ use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *re
 		 * assigning them while another thread is executing the flows feels
 		 * like tempting fate.
 		 */
-		if ((lockerr = pthread_mutex_lock(&init_mutex)) != 0)
+		if ((lockerr = pg_mtx_lock(&init_mutex)) != pg_thrd_success)
 		{
 			/* Should not happen... but don't continue if it does. */
 			Assert(false);
@@ -972,7 +973,7 @@ use_builtin_flow(PGconn *conn, fe_oauth_state *state, PGoauthBearerRequestV2 *re
 			initialized = true;
 		}
 
-		pthread_mutex_unlock(&init_mutex);
+		pg_mtx_unlock(&init_mutex);
 	}
 
 	return (start_flow(conn, request) == 0) ? 1 : -1;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 38422becc48..ab4f4b64e93 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -35,6 +35,7 @@
 #include "mb/pg_wchar.h"
 #include "pg_config_paths.h"
 #include "port/pg_bswap.h"
+#include "port/pg_threads.h"
 
 #ifdef WIN32
 #include "win32.h"
@@ -56,12 +57,6 @@
 #include <pwd.h>
 #endif
 
-#ifdef WIN32
-#include "pthread-win32.h"
-#else
-#include <pthread.h>
-#endif
-
 #ifdef USE_LDAP
 #ifdef WIN32
 #include <winldap.h>
@@ -8405,16 +8400,16 @@ pqParseProtocolVersion(const char *value, ProtocolVersion *result, PGconn *conn,
 static void
 default_threadlock(int acquire)
 {
-	static pthread_mutex_t singlethread_lock = PTHREAD_MUTEX_INITIALIZER;
+	static pg_mtx_t singlethread_lock = PG_MTX_INIT;
 
 	if (acquire)
 	{
-		if (pthread_mutex_lock(&singlethread_lock))
+		if (pg_mtx_lock(&singlethread_lock) != pg_thrd_success)
 			Assert(false);
 	}
 	else
 	{
-		if (pthread_mutex_unlock(&singlethread_lock))
+		if (pg_mtx_unlock(&singlethread_lock) != pg_thrd_success)
 			Assert(false);
 	}
 }
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 905344d5c38..e7e5aa2c723 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -50,6 +50,7 @@
 #include "mb/pg_wchar.h"
 #include "pg_config_paths.h"
 #include "port/pg_bswap.h"
+#include "port/pg_threads.h"
 
 static int	pqPutMsgBytes(const void *buf, size_t len, PGconn *conn);
 static int	pqSendSome(PGconn *conn, int len);
@@ -1311,7 +1312,7 @@ libpq_binddomain(void)
 	 * might as well do it the same way everywhere.
 	 */
 	static volatile bool already_bound = false;
-	static pthread_mutex_t binddomain_mutex = PTHREAD_MUTEX_INITIALIZER;
+	static pg_mtx_t binddomain_mutex = PG_MTX_INIT;
 
 	if (!already_bound)
 	{
@@ -1322,7 +1323,7 @@ libpq_binddomain(void)
 		int			save_errno = errno;
 #endif
 
-		(void) pthread_mutex_lock(&binddomain_mutex);
+		pg_mtx_lock(&binddomain_mutex);
 
 		if (!already_bound)
 		{
@@ -1339,7 +1340,7 @@ libpq_binddomain(void)
 			already_bound = true;
 		}
 
-		(void) pthread_mutex_unlock(&binddomain_mutex);
+		pg_mtx_unlock(&binddomain_mutex);
 
 #ifdef WIN32
 		SetLastError(save_errno);
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 6b44eeb68eb..5bd3b7c08d8 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -30,6 +30,7 @@
 #include "fe-auth.h"
 #include "fe-secure-common.h"
 #include "libpq-int.h"
+#include "port/pg_threads.h"
 
 #ifdef WIN32
 #include "win32.h"
@@ -44,12 +45,6 @@
 
 #include <sys/stat.h>
 
-#ifdef WIN32
-#include "pthread-win32.h"
-#else
-#include <pthread.h>
-#endif
-
 /*
  * These SSL-related #includes must come after all system-provided headers.
  * This ensures that OpenSSL can take care of conflicts with Windows'
@@ -83,7 +78,7 @@ static int	pgconn_bio_write(BIO *h, const char *buf, int size);
 static BIO_METHOD *pgconn_bio_method(void);
 static int	ssl_set_pgconn_bio(PGconn *conn);
 
-static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pg_mtx_t ssl_config_mutex = PG_MTX_INIT;
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
@@ -1835,7 +1830,7 @@ pgconn_bio_method(void)
 {
 	BIO_METHOD *res;
 
-	if (pthread_mutex_lock(&ssl_config_mutex))
+	if (pg_mtx_lock(&ssl_config_mutex) != pg_thrd_success)
 		return NULL;
 
 	res = pgconn_bio_method_ptr;
@@ -1865,13 +1860,13 @@ pgconn_bio_method(void)
 	}
 
 	pgconn_bio_method_ptr = res;
-	pthread_mutex_unlock(&ssl_config_mutex);
+	pg_mtx_unlock(&ssl_config_mutex);
 	return res;
 
 err:
 	if (res)
 		BIO_meth_free(res);
-	pthread_mutex_unlock(&ssl_config_mutex);
+	pg_mtx_unlock(&ssl_config_mutex);
 	return NULL;
 }
 
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 31d5b48d3f9..9fd458cc7e0 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -35,15 +35,10 @@
 
 #include <sys/stat.h>
 
-#ifdef WIN32
-#include "pthread-win32.h"
-#else
-#include <pthread.h>
-#endif
-
 #include "fe-auth.h"
 #include "libpq-fe.h"
 #include "libpq-int.h"
+#include "port/pg_threads.h"
 
 /*
  * Macros to handle disabling and then restoring the state of SIGPIPE handling.
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 461b39620c3..7abc38bf6a3 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -31,11 +31,6 @@
 #include <sys/time.h>
 #endif
 
-#ifdef WIN32
-#include "pthread-win32.h"
-#else
-#include <pthread.h>
-#endif
 #include <signal.h>
 
 /* include stuff common to fe and be */
diff --git a/src/interfaces/libpq/meson.build b/src/interfaces/libpq/meson.build
index b0ae72167a1..78d2f9d1f85 100644
--- a/src/interfaces/libpq/meson.build
+++ b/src/interfaces/libpq/meson.build
@@ -20,7 +20,7 @@ libpq_sources = files(
 libpq_so_sources = [] # for shared lib, in addition to the above
 
 if host_system == 'windows'
-  libpq_sources += files('pthread-win32.c', 'win32.c')
+  libpq_sources += files('win32.c')
   libpq_so_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
     '--NAME', 'libpq',
     '--FILEDESC', 'PostgreSQL Access Library',])
@@ -42,8 +42,7 @@ export_file = custom_target('libpq.exports',
   kwargs: gen_export_kwargs,
 )
 
-# port needs to be in include path due to pthread-win32.h
-libpq_inc = include_directories('.', '../../port')
+libpq_inc = include_directories('.')
 libpq_c_args = ['-DSO_MAJOR_VERSION=5']
 
 # The OAuth implementation differs depending on the type of library being built.
diff --git a/src/interfaces/libpq/pthread-win32.c b/src/interfaces/libpq/pthread-win32.c
deleted file mode 100644
index cf66284f007..00000000000
--- a/src/interfaces/libpq/pthread-win32.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*-------------------------------------------------------------------------
-*
-* pthread-win32.c
-*	 partial pthread implementation for win32
-*
-* Copyright (c) 2004-2026, PostgreSQL Global Development Group
-* IDENTIFICATION
-*	src/interfaces/libpq/pthread-win32.c
-*
-*-------------------------------------------------------------------------
-*/
-
-#include "postgres_fe.h"
-
-#include "pthread-win32.h"
-
-DWORD
-pthread_self(void)
-{
-	return GetCurrentThreadId();
-}
-
-void
-pthread_setspecific(pthread_key_t key, void *val)
-{
-}
-
-void *
-pthread_getspecific(pthread_key_t key)
-{
-	return NULL;
-}
-
-int
-pthread_mutex_init(pthread_mutex_t *mp, void *attr)
-{
-	mp->initstate = 0;
-	return 0;
-}
-
-int
-pthread_mutex_lock(pthread_mutex_t *mp)
-{
-	/* Initialize the csection if not already done */
-	if (mp->initstate != 1)
-	{
-		LONG		istate;
-
-		while ((istate = InterlockedExchange(&mp->initstate, 2)) == 2)
-			Sleep(0);			/* wait, another thread is doing this */
-		if (istate != 1)
-			InitializeCriticalSection(&mp->csection);
-		InterlockedExchange(&mp->initstate, 1);
-	}
-	EnterCriticalSection(&mp->csection);
-	return 0;
-}
-
-int
-pthread_mutex_unlock(pthread_mutex_t *mp)
-{
-	if (mp->initstate != 1)
-		return EINVAL;
-	LeaveCriticalSection(&mp->csection);
-	return 0;
-}
diff --git a/src/port/pthread-win32.h b/src/port/pthread-win32.h
deleted file mode 100644
index 5f33269057c..00000000000
--- a/src/port/pthread-win32.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * src/port/pthread-win32.h
- */
-#ifndef __PTHREAD_H
-#define __PTHREAD_H
-
-typedef ULONG pthread_key_t;
-
-typedef struct pthread_mutex_t
-{
-	/* initstate = 0: not initialized; 1: init done; 2: init in progress */
-	LONG		initstate;
-	CRITICAL_SECTION csection;
-} pthread_mutex_t;
-
-#define PTHREAD_MUTEX_INITIALIZER	{ 0 }
-
-typedef int pthread_once_t;
-
-DWORD		pthread_self(void);
-
-void		pthread_setspecific(pthread_key_t, void *);
-void	   *pthread_getspecific(pthread_key_t);
-
-int			pthread_mutex_init(pthread_mutex_t *, void *attr);
-int			pthread_mutex_lock(pthread_mutex_t *);
-
-/* blocking */
-int			pthread_mutex_unlock(pthread_mutex_t *);
-
-#endif
diff --git a/src/tools/pginclude/headerscheck b/src/tools/pginclude/headerscheck
index 785d6f867ad..2d8d2b8627e 100755
--- a/src/tools/pginclude/headerscheck
+++ b/src/tools/pginclude/headerscheck
@@ -105,7 +105,6 @@ do
 	test "$f" = src/include/port/win32_msvc/dirent.h && continue
 	test "$f" = src/include/port/win32_msvc/utime.h && continue
 	test "$f" = src/include/port/win32ntdll.h && continue
-	test "$f" = src/port/pthread-win32.h && continue
 
 	# Likewise, these files are platform-specific, and the one
 	# relevant to our platform will be included by atomics.h.
-- 
2.47.3

