Subject: Cleaning up threading code

From: Thomas Munro <[email-masked]>
Date: Sat, 10 Jun 2023 14:23:52 +1200

Hi,

This message is not about inventing backend threads, but I was
reminded to post this by some thought-provoking hallway track
discussion of that large topic at PGCon, mostly with Heikki.

1.  We should completely remove --disable-thread-safety and the
various !defined(ENABLE_THREAD_SAFETY) code paths.  There are no
computers that need it, it's not being tested, and support was already
dropped/hardcoded in meson.build.  Here is a mostly mechanical patch
to finish that job, originally posted in one of the big 'historical
baggage' threads -- I just didn't get around to committing in time for
16.  Also a couple of related tiny cleanups for port/thread.c, which
is by now completely bogus.

2.  I don't like the way we have to deal with POSIX vs Windows at
every site where we use threads, and each place has a different style
of wrappers.  I considered a few different approaches to cleaning this
up:

* provide centralised and thorough pthread emulation for Windows; I
don't like this, I don't even like all of pthreads and there are many
details to get lost in
* adopt C11 <threads.h>; unfortunately it is too early, so you'd need
to write/borrow replacements for at least 3 of our 11 target systems
* invent our own mini-abstraction for a carefully controlled subset of stuff

Here is an attempt at that last thing.  Since I don't really like
making up new names for things, I just used all the C11 names but with
pg_ in front, and declared it all in "port/pg_threads.h" (actually I
tried to write C11 replacements and then noped out and added the pg_
prefixes).  I suppose the idea is that it and the prefixes might
eventually go away.  Note: here I am talking only about very basic
operations like create, exit, join, explicit thread locals -- the
stuff that we actually use today in frontend code.  I'm not talking
about other stuff like C11 atomics, memory models, and the
thread_local storage class, which are all very good and interesting
topics for another day.

One mystery still eludes me on Windows: while trying to fix the
ancient bug that ECPG leaks memory on Windows, because it doesn't call
thread-local destructors, I discovered that if you use FlsAlloc
instead of TlsAlloc you can pass in a destructor (that's
"fibre-local", but we're not using fibres so it's works out the same
as thread local AFAICT).  It seems to crash in strange ways if you
uncomment the line FlsAlloc(destructor).  Maybe it calls the
destructor even for NULL value?  Or maybe it suffers from some kind of
Windowsian multiple-heaps-active problem.  I noticed that some other
attempts at implementing tss on Windows use TlsAlloc but do their own
bookkeeping of destructors and run them at thread exit, which I was
hoping to avoid, but ...

Before I spend time fixing that issue or seeking Windowsian help...
do you think this direction is an improvement?
From 4d0d568fba15543070e7b7e501e1748ae6aab821 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email-masked]>
Date: Fri, 9 Jun 2023 13:56:21 +1200
Subject: [PATCH 1/5] Remove obsolete comments and code from fe-auth.c.

We don't use getpwuid() anymore (see commit e757cdd6), so we don't need
locking around pg_get_user_name().
---
 src/interfaces/libpq/fe-auth.c | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 88fd0f3d80..b65333268f 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -1179,15 +1179,6 @@ pg_fe_getusername(uid_t user_id, PQExpBuffer errorMessage)
 	char		pwdbuf[BUFSIZ];
 #endif
 
-	/*
-	 * Some users are using configure --enable-thread-safety-force, so we
-	 * might as well do the locking within our library to protect getpwuid().
-	 * In fact, application developers can use getpwuid() in their application
-	 * if they use the locking call we provide, or install their own locking
-	 * function using PQregisterThreadLock().
-	 */
-	pglock_thread();
-
 #ifdef WIN32
 	if (GetUserName(username, &namesize))
 		name = username;
@@ -1209,8 +1200,6 @@ pg_fe_getusername(uid_t user_id, PQExpBuffer errorMessage)
 			libpq_append_error(errorMessage, "out of memory");
 	}
 
-	pgunlock_thread();
-
 	return result;
 }
 
-- 
2.39.2

From 6991b8cea31833359bb56c7cfa880507a5b314a7 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email-masked]>
Date: Sat, 10 Jun 2023 12:26:04 +1200
Subject: [PATCH 2/5] Rename port/thread.c to port/user.c.

Historically this module dealt with non-threadsafe system interfaces for
user name and home directory lookup, but now it's just wrapper code.
Arguably the Windows variants of this logic could be moved in here too,
to justify its presence under port, but for now, just tidy up obsolete
references to threads.
---
 src/port/Makefile             |  6 +-----
 src/port/meson.build          |  2 +-
 src/port/{thread.c => user.c} | 13 +++----------
 3 files changed, 5 insertions(+), 16 deletions(-)
 rename src/port/{thread.c => user.c} (88%)

diff --git a/src/port/Makefile b/src/port/Makefile
index 711f59e32b..f205c2c9c5 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -59,7 +59,7 @@ OBJS = \
 	snprintf.o \
 	strerror.o \
 	tar.o \
-	thread.o
+	user.o
 
 # libpgport.a, libpgport_shlib.a, and libpgport_srv.a contain the same files
 # foo.o, foo_shlib.o, and foo_srv.o are all built from foo.c
@@ -84,10 +84,6 @@ libpgport.a: $(OBJS)
 	rm -f $@
 	$(AR) $(AROPT) $@ $^
 
-# thread.o and thread_shlib.o need PTHREAD_CFLAGS (but thread_srv.o does not)
-thread.o: CFLAGS+=$(PTHREAD_CFLAGS)
-thread_shlib.o: CFLAGS+=$(PTHREAD_CFLAGS)
-
 # all versions of pg_crc32c_sse42.o need CFLAGS_CRC
 pg_crc32c_sse42.o: CFLAGS+=$(CFLAGS_CRC)
 pg_crc32c_sse42_shlib.o: CFLAGS+=$(CFLAGS_CRC)
diff --git a/src/port/meson.build b/src/port/meson.build
index 24416b9bfc..9d0cd93c43 100644
--- a/src/port/meson.build
+++ b/src/port/meson.build
@@ -20,7 +20,7 @@ pgport_sources = [
   'snprintf.c',
   'strerror.c',
   'tar.c',
-  'thread.c',
+  'user.c',
 ]
 
 if host_system == 'windows'
diff --git a/src/port/thread.c b/src/port/user.c
similarity index 88%
rename from src/port/thread.c
rename to src/port/user.c
index 375c89b297..d278684fe2 100644
--- a/src/port/thread.c
+++ b/src/port/user.c
@@ -1,13 +1,12 @@
 /*-------------------------------------------------------------------------
  *
- * thread.c
+ * user.c
  *
- *		  Prototypes and macros around system calls, used to help make
- *		  threaded libraries reentrant and safe to use from threaded applications.
+ *		  Wrapper functions for user and home directory lookup.
  *
  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
  *
- * src/port/thread.c
+ * src/port/user.c
  *
  *-------------------------------------------------------------------------
  */
@@ -16,12 +15,6 @@
 
 #include <pwd.h>
 
-
-/*
- *  Historically, the code in this module had to deal with operating systems
- *  that lacked getpwuid_r().
- */
-
 #ifndef WIN32
 
 /*
-- 
2.39.2

From 394bc4cbb9ae881de9b0c2442d1392352adc3229 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email-masked]>
Date: Fri, 9 Jun 2023 14:07:26 +1200
Subject: [PATCH 3/5] Remove --disable-thread-safety.

All supported computers have either POSIX or Windows threads, and we no
longer have any automated testing of --disable-thread-safety.
---
 configure                                     | 56 ++-------------
 configure.ac                                  | 28 ++------
 doc/src/sgml/installation.sgml                | 13 ----
 meson.build                                   | 11 ---
 src/Makefile.global.in                        |  1 -
 src/bin/pgbench/pgbench.c                     | 22 +-----
 src/include/pg_config.h.in                    |  4 --
 src/interfaces/ecpg/ecpglib/connect.c         | 40 -----------
 src/interfaces/ecpg/ecpglib/descriptor.c      | 10 ---
 src/interfaces/ecpg/ecpglib/ecpglib_extern.h  |  2 -
 src/interfaces/ecpg/ecpglib/execute.c         |  2 -
 src/interfaces/ecpg/ecpglib/memory.c          |  7 --
 src/interfaces/ecpg/ecpglib/misc.c            | 47 ------------
 .../ecpg/include/ecpg-pthread-win32.h         |  3 -
 src/interfaces/ecpg/include/ecpg_config.h.in  |  4 --
 src/interfaces/ecpg/include/ecpglib.h         |  2 -
 src/interfaces/ecpg/include/meson.build       |  1 -
 .../ecpg/test/expected/thread-alloc.c         | 43 +++++------
 .../ecpg/test/expected/thread-descriptor.c    | 22 +++---
 .../ecpg/test/expected/thread-prep.c          | 71 ++++++++-----------
 .../ecpg/test/expected/thread-thread.c        | 63 +++++++---------
 .../test/expected/thread-thread_implicit.c    | 63 +++++++---------
 src/interfaces/ecpg/test/thread/alloc.pgc     |  9 ---
 .../ecpg/test/thread/descriptor.pgc           |  8 +--
 src/interfaces/ecpg/test/thread/prep.pgc      |  9 ---
 src/interfaces/ecpg/test/thread/thread.pgc    |  9 ---
 .../ecpg/test/thread/thread_implicit.pgc      |  9 ---
 src/interfaces/libpq/Makefile                 |  5 +-
 src/interfaces/libpq/fe-connect.c             |  4 --
 src/interfaces/libpq/fe-exec.c                |  4 --
 src/interfaces/libpq/fe-print.c               | 13 +---
 src/interfaces/libpq/fe-secure-openssl.c      | 17 +----
 src/interfaces/libpq/fe-secure.c              | 26 +------
 src/interfaces/libpq/legacy-pqsignal.c        |  4 +-
 src/interfaces/libpq/libpq-int.h              |  9 +--
 src/makefiles/meson.build                     |  1 -
 src/tools/msvc/Solution.pm                    |  3 +-
 src/tools/msvc/ecpg_regression.proj           |  2 +-
 38 files changed, 133 insertions(+), 514 deletions(-)

diff --git a/configure b/configure
index 1b415142d1..85ed3f853a 100755
--- a/configure
+++ b/configure
@@ -722,7 +722,6 @@ with_tcl
 ICU_LIBS
 ICU_CFLAGS
 with_icu
-enable_thread_safety
 INCLUDES
 autodepend
 PKG_CONFIG_LIBDIR
@@ -848,7 +847,6 @@ with_CC
 with_llvm
 enable_depend
 enable_cassert
-enable_thread_safety
 with_icu
 with_tcl
 with_tclconfig
@@ -1536,7 +1534,6 @@ Optional Features:
   --enable-tap-tests      enable TAP tests (requires Perl and IPC::Run)
   --enable-depend         turn on automatic dependency tracking
   --enable-cassert        enable assertion checks (for debugging)
-  --disable-thread-safety disable thread-safety in client libraries
   --disable-largefile     omit support for large files
 
 Optional Packages:
@@ -8338,43 +8335,6 @@ $as_echo "$as_me: WARNING: *** Library directory $dir does not exist." >&2;}
 done
 IFS=$ac_save_IFS
 
-#
-# Enable thread-safe client libraries
-#
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking allow thread-safe client libraries" >&5
-$as_echo_n "checking allow thread-safe client libraries... " >&6; }
-
-
-# Check whether --enable-thread-safety was given.
-if test "${enable_thread_safety+set}" = set; then :
-  enableval=$enable_thread_safety;
-  case $enableval in
-    yes)
-      :
-      ;;
-    no)
-      :
-      ;;
-    *)
-      as_fn_error $? "no argument expected for --enable-thread-safety option" "$LINENO" 5
-      ;;
-  esac
-
-else
-  enable_thread_safety=yes
-
-fi
-
-
-if test "$enable_thread_safety" = yes; then
-
-$as_echo "#define ENABLE_THREAD_SAFETY 1" >>confdefs.h
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_thread_safety" >&5
-$as_echo "$enable_thread_safety" >&6; }
-
-
 #
 # ICU
 #
@@ -11065,7 +11025,7 @@ fi
 done

-if test "$enable_thread_safety" = yes -a "$PORTNAME" != "win32"; then :
+if test "$PORTNAME" != "win32"; then :
    # then

@@ -11724,7 +11684,7 @@ if test "x$ac_cv_header_pthread_h" = xyes; then :
 
 else
   as_fn_error $? "
-pthread.h not found;  use --disable-thread-safety to disable thread safety" "$LINENO" 5
+pthread.h not found" "$LINENO" 5
 fi

@@ -12408,8 +12368,7 @@ if test "$ac_res" != no; then :
 fi

-if test "$enable_thread_safety" = yes; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_barrier_wait" >&5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_barrier_wait" >&5
 $as_echo_n "checking for library containing pthread_barrier_wait... " >&6; }
 if ${ac_cv_search_pthread_barrier_wait+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -12465,7 +12424,6 @@ if test "$ac_res" != no; then :
 
 fi
 
-fi
 
 if test "$with_readline" = yes; then
 
@@ -13352,7 +13310,7 @@ else
   thread_safe_libldap=no
 fi
 
-    if test "$enable_thread_safety" = yes -a "$thread_safe_libldap" = no; then
+    if test "$thread_safe_libldap" = no; then
       # Use ldap_r for FE if available, else assume ldap is thread-safe.
       # On some platforms ldap_r fails to link without PTHREAD_LIBS.
       LIBS="$_LIBS"
@@ -13398,8 +13356,6 @@ else
   LDAP_LIBS_FE="-lldap $EXTRA_LDAP_LIBS"
 fi
 
-    else
-      LDAP_LIBS_FE="-lldap $EXTRA_LDAP_LIBS"
     fi
   else
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap_bind in -lwldap32" >&5
@@ -16385,8 +16341,7 @@ fi

-if test "$enable_thread_safety" = yes; then
-  ac_fn_c_check_func "$LINENO" "pthread_barrier_wait" "ac_cv_func_pthread_barrier_wait"
+ac_fn_c_check_func "$LINENO" "pthread_barrier_wait" "ac_cv_func_pthread_barrier_wait"
 if test "x$ac_cv_func_pthread_barrier_wait" = xyes; then :
   $as_echo "#define HAVE_PTHREAD_BARRIER_WAIT 1" >>confdefs.h
 
@@ -16400,7 +16355,6 @@ esac
 fi

-fi
 
 if test "$PORTNAME" = "win32" -o "$PORTNAME" = "cygwin"; then
 	# Cygwin and (apparently, based on test results) Mingw both
diff --git a/configure.ac b/configure.ac
index 09558ada0f..9077f597d5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -837,18 +837,6 @@ for dir in $LIBRARY_DIRS $SRCH_LIB; do
 done
 IFS=$ac_save_IFS
 
-#
-# Enable thread-safe client libraries
-#
-AC_MSG_CHECKING([allow thread-safe client libraries])
-PGAC_ARG_BOOL(enable, thread-safety, yes, [disable thread-safety in client libraries])
-if test "$enable_thread_safety" = yes; then
-  AC_DEFINE([ENABLE_THREAD_SAFETY], 1,
-          [Define to 1 to build client libraries as thread-safe code. (--enable-thread-safety)])
-fi
-AC_MSG_RESULT([$enable_thread_safety])
-AC_SUBST(enable_thread_safety)
-
 #
 # ICU
 #
@@ -1244,7 +1232,7 @@ dnl note: We have to use AS_IF here rather than plain if. The AC_CHECK_HEADER
 dnl invocation below is the first one in the script, and autoconf generates
 dnl additional code for that, which must not be inside the if-block. AS_IF
 dnl knows how to do that.
-AS_IF([test "$enable_thread_safety" = yes -a "$PORTNAME" != "win32"],
+AS_IF([test "$PORTNAME" != "win32"],
 [ # then
 AX_PTHREAD	# set thread flags
 
@@ -1259,7 +1247,7 @@ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
 LIBS="$LIBS $PTHREAD_LIBS"
 
 AC_CHECK_HEADER(pthread.h, [], [AC_MSG_ERROR([
-pthread.h not found;  use --disable-thread-safety to disable thread safety])])
+pthread.h not found])])
 
 AC_CHECK_FUNCS([strerror_r])
 
@@ -1306,9 +1294,7 @@ AC_SEARCH_LIBS(shmget, cygipc)
 # *BSD:
 AC_SEARCH_LIBS(backtrace_symbols, execinfo)
 
-if test "$enable_thread_safety" = yes; then
-  AC_SEARCH_LIBS(pthread_barrier_wait, pthread)
-fi
+AC_SEARCH_LIBS(pthread_barrier_wait, pthread)
 
 if test "$with_readline" = yes; then
   PGAC_CHECK_READLINE
@@ -1436,7 +1422,7 @@ if test "$with_ldap" = yes ; then
     AC_CHECK_FUNC([ldap_verify_credentials],
 		  [thread_safe_libldap=yes],
 		  [thread_safe_libldap=no])
-    if test "$enable_thread_safety" = yes -a "$thread_safe_libldap" = no; then
+    if test "$thread_safe_libldap" = no; then
       # Use ldap_r for FE if available, else assume ldap is thread-safe.
       # On some platforms ldap_r fails to link without PTHREAD_LIBS.
       LIBS="$_LIBS"
@@ -1444,8 +1430,6 @@ if test "$with_ldap" = yes ; then
 		   [LDAP_LIBS_FE="-lldap_r $EXTRA_LDAP_LIBS"],
 		   [LDAP_LIBS_FE="-lldap $EXTRA_LDAP_LIBS"],
 		   [$PTHREAD_CFLAGS $PTHREAD_LIBS $EXTRA_LDAP_LIBS])
-    else
-      LDAP_LIBS_FE="-lldap $EXTRA_LDAP_LIBS"
     fi
   else
     AC_CHECK_LIB(wldap32, ldap_bind, [], [AC_MSG_ERROR([library 'wldap32' is required for LDAP])])
@@ -1860,9 +1844,7 @@ AC_REPLACE_FUNCS(m4_normalize([
 	strnlen
 ]))
 
-if test "$enable_thread_safety" = yes; then
-  AC_REPLACE_FUNCS(pthread_barrier_wait)
-fi
+AC_REPLACE_FUNCS(pthread_barrier_wait)
 
 if test "$PORTNAME" = "win32" -o "$PORTNAME" = "cygwin"; then
 	# Cygwin and (apparently, based on test results) Mingw both
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 75dc81a0a9..5be927db26 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -1306,19 +1306,6 @@ build-postgresql:
        </listitem>
       </varlistentry>
 
-      <varlistentry id="configure-option-disable-thread-safety">
-       <term><option>--disable-thread-safety</option></term>
-       <listitem>
-        <para>
-         Disable the thread-safety of client libraries.  This prevents
-         concurrent threads in <application>libpq</application> and
-         <application>ECPG</application> programs from safely controlling
-         their private connection handles.  Use this only on platforms
-         with deficient threading support.
-        </para>
-       </listitem>
-      </varlistentry>
-
      </variablelist>
 
    </sect3>
diff --git a/meson.build b/meson.build
index 16b2e86646..a4f3da351a 100644
--- a/meson.build
+++ b/meson.build
@@ -2519,17 +2519,6 @@ cdata.set_quoted('PG_VERSION_STR',
 )

-
-###############################################################
-# Threading
-###############################################################
-
-# XXX: About to rely on thread safety in the autoconf build, so not worth
-# implementing a fallback.
-cdata.set('ENABLE_THREAD_SAFETY', 1)
-
-
-
 ###############################################################
 # NLS / Gettext
 ###############################################################
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 974b1dfef9..df9f721a41 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -202,7 +202,6 @@ enable_debug	= @enable_debug@
 enable_dtrace	= @enable_dtrace@
 enable_coverage	= @enable_coverage@
 enable_tap_tests	= @enable_tap_tests@
-enable_thread_safety	= @enable_thread_safety@
 
 python_includespec	= @python_includespec@
 python_libdir		= @python_libdir@
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 1d1670d4c2..683fc5860f 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -137,7 +137,7 @@ typedef struct socket_set
 	EnterSynchronizationBarrier((barrier), \
 								SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY)
 #define THREAD_BARRIER_DESTROY(barrier)
-#elif defined(ENABLE_THREAD_SAFETY)
+#else
 /* Use POSIX threads */
 #include "port/pg_pthread.h"
 #define THREAD_T pthread_t
@@ -153,16 +153,6 @@ typedef struct socket_set
 	pthread_barrier_init((barrier), NULL, (n))
 #define THREAD_BARRIER_WAIT(barrier) pthread_barrier_wait((barrier))
 #define THREAD_BARRIER_DESTROY(barrier) pthread_barrier_destroy((barrier))
-#else
-/* No threads implementation, use none (-j 1) */
-#define THREAD_T void *
-#define THREAD_FUNC_RETURN_TYPE void *
-#define THREAD_FUNC_RETURN return NULL
-#define THREAD_FUNC_CC
-#define THREAD_BARRIER_T int
-#define THREAD_BARRIER_INIT(barrier, n) (*(barrier) = 0)
-#define THREAD_BARRIER_WAIT(barrier)
-#define THREAD_BARRIER_DESTROY(barrier)
 #endif

@@ -6749,10 +6739,6 @@ main(int argc, char **argv)
 				{
 					exit(1);
 				}
-#ifndef ENABLE_THREAD_SAFETY
-				if (nthreads != 1)
-					pg_fatal("threads are not supported on this platform; use -j1");
-#endif							/* !ENABLE_THREAD_SAFETY */
 				break;
 			case 'l':
 				benchmarking_option_set = true;
@@ -7236,7 +7222,6 @@ main(int argc, char **argv)
 	if (errno != 0)
 		pg_fatal("could not initialize barrier: %m");
 
-#ifdef ENABLE_THREAD_SAFETY
 	/* start all threads but thread 0 which is executed directly later */
 	for (i = 1; i < nthreads; i++)
 	{
@@ -7248,9 +7233,6 @@ main(int argc, char **argv)
 		if (errno != 0)
 			pg_fatal("could not create thread: %m");
 	}
-#else
-	Assert(nthreads == 1);
-#endif							/* ENABLE_THREAD_SAFETY */
 
 	/* compute when to stop */
 	threads[0].create_time = pg_time_now();
@@ -7268,10 +7250,8 @@ main(int argc, char **argv)
 	{
 		TState	   *thread = &threads[i];
 
-#ifdef ENABLE_THREAD_SAFETY
 		if (i > 0)
 			THREAD_JOIN(thread->thread);
-#endif							/* ENABLE_THREAD_SAFETY */
 
 		for (int j = 0; j < thread->nstate; j++)
 			if (thread->state[j].state != CSTATE_FINISHED)
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 6d572c3820..18e90fd842 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -51,10 +51,6 @@
 /* Define to 1 if you want National Language Support. (--enable-nls) */
 #undef ENABLE_NLS
 
-/* Define to 1 to build client libraries as thread-safe code.
-   (--enable-thread-safety) */
-#undef ENABLE_THREAD_SAFETY
-
 /* Define to 1 if you have the `append_history' function. */
 #undef HAVE_APPEND_HISTORY
 
diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c
index db0bae1fe0..8afb1f0a26 100644
--- a/src/interfaces/ecpg/ecpglib/connect.c
+++ b/src/interfaces/ecpg/ecpglib/connect.c
@@ -14,15 +14,12 @@
 locale_t	ecpg_clocale = (locale_t) 0;
 #endif
 
-#ifdef ENABLE_THREAD_SAFETY
 static pthread_mutex_t connections_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t actual_connection_key;
 static pthread_once_t actual_connection_key_once = PTHREAD_ONCE_INIT;
-#endif
 static struct connection *actual_connection = NULL;
 static struct connection *all_connections = NULL;
 
-#ifdef ENABLE_THREAD_SAFETY
 static void
 ecpg_actual_connection_init(void)
 {
@@ -34,7 +31,6 @@ ecpg_pthreads_init(void)
 {
 	pthread_once(&actual_connection_key_once, ecpg_actual_connection_init);
 }
-#endif
 
 static struct connection *
 ecpg_get_connection_nr(const char *connection_name)
@@ -43,7 +39,6 @@ ecpg_get_connection_nr(const char *connection_name)
 
 	if ((connection_name == NULL) || (strcmp(connection_name, "CURRENT") == 0))
 	{
-#ifdef ENABLE_THREAD_SAFETY
 		ecpg_pthreads_init();	/* ensure actual_connection_key is valid */
 
 		ret = pthread_getspecific(actual_connection_key);
@@ -56,9 +51,6 @@ ecpg_get_connection_nr(const char *connection_name)
 		if (ret == NULL)
 			/* no TSD connection, going for global */
 			ret = actual_connection;
-#else
-		ret = actual_connection;
-#endif
 	}
 	else
 	{
@@ -82,7 +74,6 @@ ecpg_get_connection(const char *connection_name)
 
 	if ((connection_name == NULL) || (strcmp(connection_name, "CURRENT") == 0))
 	{
-#ifdef ENABLE_THREAD_SAFETY
 		ecpg_pthreads_init();	/* ensure actual_connection_key is valid */
 
 		ret = pthread_getspecific(actual_connection_key);
@@ -95,21 +86,14 @@ ecpg_get_connection(const char *connection_name)
 		if (ret == NULL)
 			/* no TSD connection here either, using global */
 			ret = actual_connection;
-#else
-		ret = actual_connection;
-#endif
 	}
 	else
 	{
-#ifdef ENABLE_THREAD_SAFETY
 		pthread_mutex_lock(&connections_mutex);
-#endif
 
 		ret = ecpg_get_connection_nr(connection_name);
 
-#ifdef ENABLE_THREAD_SAFETY
 		pthread_mutex_unlock(&connections_mutex);
-#endif
 	}
 
 	return ret;
@@ -143,10 +127,8 @@ ecpg_finish(struct connection *act)
 				con->next = act->next;
 		}
 
-#ifdef ENABLE_THREAD_SAFETY
 		if (pthread_getspecific(actual_connection_key) == act)
 			pthread_setspecific(actual_connection_key, all_connections);
-#endif
 		if (actual_connection == act)
 			actual_connection = all_connections;
 
@@ -212,11 +194,7 @@ ECPGsetconn(int lineno, const char *connection_name)
 	if (!ecpg_init(con, connection_name, lineno))
 		return false;
 
-#ifdef ENABLE_THREAD_SAFETY
 	pthread_setspecific(actual_connection_key, con);
-#else
-	actual_connection = con;
-#endif
 	return true;
 }
 
@@ -326,9 +304,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 	if (dbname == NULL && connection_name == NULL)
 		connection_name = "DEFAULT";
 
-#if ENABLE_THREAD_SAFETY
 	ecpg_pthreads_init();
-#endif
 
 	/* check if the identifier is unique */
 	if (ecpg_get_connection(connection_name))
@@ -505,9 +481,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 	}
 
 	/* add connection to our list */
-#ifdef ENABLE_THREAD_SAFETY
 	pthread_mutex_lock(&connections_mutex);
-#endif
 
 	/*
 	 * ... but first, make certain we have created ecpg_clocale.  Rely on
@@ -519,9 +493,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 		ecpg_clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
 		if (!ecpg_clocale)
 		{
-#ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&connections_mutex);
-#endif
 			ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
 					   ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
 			if (host)
@@ -558,9 +530,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 		this->next = all_connections;
 
 	all_connections = this;
-#ifdef ENABLE_THREAD_SAFETY
 	pthread_setspecific(actual_connection_key, all_connections);
-#endif
 	actual_connection = all_connections;
 
 	ecpg_log("ECPGconnect: opening database %s on %s port %s %s%s %s%s\n",
@@ -678,9 +648,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 		ecpg_log("ECPGconnect: %s", errmsg);
 
 		ecpg_finish(this);
-#ifdef ENABLE_THREAD_SAFETY
 		pthread_mutex_unlock(&connections_mutex);
-#endif
 
 		ecpg_raise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, db);
 		if (realname)
@@ -692,9 +660,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 	if (realname)
 		ecpg_free(realname);
 
-#ifdef ENABLE_THREAD_SAFETY
 	pthread_mutex_unlock(&connections_mutex);
-#endif
 
 	this->autocommit = autocommit;
 
@@ -716,9 +682,7 @@ ECPGdisconnect(int lineno, const char *connection_name)
 		return false;
 	}
 
-#ifdef ENABLE_THREAD_SAFETY
 	pthread_mutex_lock(&connections_mutex);
-#endif
 
 	if (strcmp(connection_name, "ALL") == 0)
 	{
@@ -737,18 +701,14 @@ ECPGdisconnect(int lineno, const char *connection_name)
 
 		if (!ecpg_init(con, connection_name, lineno))
 		{
-#ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&connections_mutex);
-#endif
 			return false;
 		}
 		else
 			ecpg_finish(con);
 	}
 
-#ifdef ENABLE_THREAD_SAFETY
 	pthread_mutex_unlock(&connections_mutex);
-#endif
 
 	return true;
 }
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
index 883a210a81..ad279e245c 100644
--- a/src/interfaces/ecpg/ecpglib/descriptor.c
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -19,7 +19,6 @@
 static void descriptor_free(struct descriptor *desc);
 
 /* We manage descriptors separately for each thread. */
-#ifdef ENABLE_THREAD_SAFETY
 static pthread_key_t descriptor_key;
 static pthread_once_t descriptor_once = PTHREAD_ONCE_INIT;
 
@@ -49,12 +48,6 @@ set_descriptors(struct descriptor *value)
 {
 	pthread_setspecific(descriptor_key, value);
 }
-#else
-static struct descriptor *all_descriptors = NULL;
-
-#define get_descriptors()		(all_descriptors)
-#define set_descriptors(value)	do { all_descriptors = (value); } while(0)
-#endif
 
 /* old internal convenience function that might go away later */
 static PGresult *
@@ -782,8 +775,6 @@ ECPGdeallocate_desc(int line, const char *name)
 	return false;
 }
 
-#ifdef ENABLE_THREAD_SAFETY
-
 /* Deallocate all descriptors in the list */
 static void
 descriptor_deallocate_all(struct descriptor *list)
@@ -796,7 +787,6 @@ descriptor_deallocate_all(struct descriptor *list)
 		list = next;
 	}
 }
-#endif							/* ENABLE_THREAD_SAFETY */
 
 bool
 ECPGallocate_desc(int line, const char *name)
diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
index 8b8f081f27..01b4309a71 100644
--- a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
+++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
@@ -169,9 +169,7 @@ bool		ecpg_get_data(const PGresult *, int, int, int, enum ECPGttype type,
 						  enum ECPGttype, char *, char *, long, long, long,
 						  enum ARRAY_TYPE, enum COMPAT_MODE, bool);
 
-#ifdef ENABLE_THREAD_SAFETY
 void		ecpg_pthreads_init(void);
-#endif
 struct connection *ecpg_get_connection(const char *connection_name);
 char	   *ecpg_alloc(long size, int lineno);
 char	   *ecpg_auto_alloc(long size, int lineno);
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 93926fd4fb..04d0b40c53 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -1961,9 +1961,7 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
 		return false;
 	}
 
-#ifdef ENABLE_THREAD_SAFETY
 	ecpg_pthreads_init();
-#endif
 
 	con = ecpg_get_connection(connection_name);
 
diff --git a/src/interfaces/ecpg/ecpglib/memory.c b/src/interfaces/ecpg/ecpglib/memory.c
index bd81251054..a83637ac75 100644
--- a/src/interfaces/ecpg/ecpglib/memory.c
+++ b/src/interfaces/ecpg/ecpglib/memory.c
@@ -68,7 +68,6 @@ struct auto_mem
 	struct auto_mem *next;
 };
 
-#ifdef ENABLE_THREAD_SAFETY
 static pthread_key_t auto_mem_key;
 static pthread_once_t auto_mem_once = PTHREAD_ONCE_INIT;
 
@@ -97,12 +96,6 @@ set_auto_allocs(struct auto_mem *am)
 {
 	pthread_setspecific(auto_mem_key, am);
 }
-#else
-static struct auto_mem *auto_allocs = NULL;
-
-#define get_auto_allocs()		(auto_allocs)
-#define set_auto_allocs(am)		do { auto_allocs = (am); } while(0)
-#endif
 
 char *
 ecpg_auto_alloc(long size, int lineno)
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
index 7f75e18733..2b78caeaf5 100644
--- a/src/interfaces/ecpg/ecpglib/misc.c
+++ b/src/interfaces/ecpg/ecpglib/misc.c
@@ -55,42 +55,11 @@ static struct sqlca_t sqlca_init =
 	}
 };
 
-#ifdef ENABLE_THREAD_SAFETY
 static pthread_key_t sqlca_key;
 static pthread_once_t sqlca_key_once = PTHREAD_ONCE_INIT;
-#else
-static struct sqlca_t sqlca =
-{
-	{
-		'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '
-	},
-	sizeof(struct sqlca_t),
-	0,
-	{
-		0,
-		{
-			0
-		}
-	},
-	{
-		'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '
-	},
-	{
-		0, 0, 0, 0, 0, 0
-	},
-	{
-		0, 0, 0, 0, 0, 0, 0, 0
-	},
-	{
-		'0', '0', '0', '0', '0'
-	}
-};
-#endif
 
-#ifdef ENABLE_THREAD_SAFETY
 static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_mutex_t debug_init_mutex = PTHREAD_MUTEX_INITIALIZER;
-#endif
 static int	simple_debug = 0;
 static FILE *debugstream = NULL;
 
@@ -123,7 +92,6 @@ ecpg_init(const struct connection *con, const char *connection_name, const int l
 	return true;
 }
 
-#ifdef ENABLE_THREAD_SAFETY
 static void
 ecpg_sqlca_key_destructor(void *arg)
 {
@@ -135,12 +103,10 @@ ecpg_sqlca_key_init(void)
 {
 	pthread_key_create(&sqlca_key, ecpg_sqlca_key_destructor);
 }
-#endif
 
 struct sqlca_t *
 ECPGget_sqlca(void)
 {
-#ifdef ENABLE_THREAD_SAFETY
 	struct sqlca_t *sqlca;
 
 	pthread_once(&sqlca_key_once, ecpg_sqlca_key_init);
@@ -155,9 +121,6 @@ ECPGget_sqlca(void)
 		pthread_setspecific(sqlca_key, sqlca);
 	}
 	return sqlca;
-#else
-	return &sqlca;
-#endif
 }
 
 bool
@@ -240,9 +203,7 @@ ECPGtrans(int lineno, const char *connection_name, const char *transaction)
 void
 ECPGdebug(int n, FILE *dbgs)
 {
-#ifdef ENABLE_THREAD_SAFETY
 	pthread_mutex_lock(&debug_init_mutex);
-#endif
 
 	if (n > 100)
 	{
@@ -256,9 +217,7 @@ ECPGdebug(int n, FILE *dbgs)
 
 	ecpg_log("ECPGdebug: set to %d\n", simple_debug);
 
-#ifdef ENABLE_THREAD_SAFETY
 	pthread_mutex_unlock(&debug_init_mutex);
-#endif
 }
 
 void
@@ -290,9 +249,7 @@ ecpg_log(const char *format,...)
 	else
 		snprintf(fmt, bufsize, "[%d]: %s", (int) getpid(), intl_format);
 
-#ifdef ENABLE_THREAD_SAFETY
 	pthread_mutex_lock(&debug_mutex);
-#endif
 
 	va_start(ap, format);
 	vfprintf(debugstream, fmt, ap);
@@ -307,9 +264,7 @@ ecpg_log(const char *format,...)
 
 	fflush(debugstream);
 
-#ifdef ENABLE_THREAD_SAFETY
 	pthread_mutex_unlock(&debug_mutex);
-#endif
 
 	free(fmt);
 }
@@ -451,7 +406,6 @@ ECPGis_noind_null(enum ECPGttype type, const void *ptr)
 }
 
 #ifdef WIN32
-#ifdef ENABLE_THREAD_SAFETY
 
 void
 win32_pthread_mutex(volatile pthread_mutex_t *mutex)
@@ -482,7 +436,6 @@ win32_pthread_once(volatile pthread_once_t *once, void (*fn) (void))
 		pthread_mutex_unlock(&win32_pthread_once_lock);
 	}
 }
-#endif							/* ENABLE_THREAD_SAFETY */
 #endif							/* WIN32 */
 
 #ifdef ENABLE_NLS
diff --git a/src/interfaces/ecpg/include/ecpg-pthread-win32.h b/src/interfaces/ecpg/include/ecpg-pthread-win32.h
index 33c897b633..8252a17809 100644
--- a/src/interfaces/ecpg/include/ecpg-pthread-win32.h
+++ b/src/interfaces/ecpg/include/ecpg-pthread-win32.h
@@ -5,8 +5,6 @@
 #ifndef _ECPG_PTHREAD_WIN32_H
 #define _ECPG_PTHREAD_WIN32_H
 
-#ifdef ENABLE_THREAD_SAFETY
-
 #ifndef WIN32
 
 #include <pthread.h>
@@ -53,6 +51,5 @@ void		win32_pthread_once(volatile pthread_once_t *once, void (*fn) (void));
 			win32_pthread_once((once), (fn)); \
 	} while(0)
 #endif							/* WIN32 */
-#endif							/* ENABLE_THREAD_SAFETY */
 
 #endif							/* _ECPG_PTHREAD_WIN32_H */
diff --git a/src/interfaces/ecpg/include/ecpg_config.h.in b/src/interfaces/ecpg/include/ecpg_config.h.in
index cbd24f11a0..6d01608a49 100644
--- a/src/interfaces/ecpg/include/ecpg_config.h.in
+++ b/src/interfaces/ecpg/include/ecpg_config.h.in
@@ -1,7 +1,3 @@
-/* Define to 1 to build client libraries as thread-safe code.
- *    (--enable-thread-safety) */
-#undef ENABLE_THREAD_SAFETY
-
 /* Define to 1 if the system has the type `int64'. */
 #undef HAVE_INT64
 
diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h
index 21a2134483..771761ffe4 100644
--- a/src/interfaces/ecpg/include/ecpglib.h
+++ b/src/interfaces/ecpg/include/ecpglib.h
@@ -98,9 +98,7 @@ void	   *ECPGget_var(int number);
 /* dynamic result allocation */
 void		ECPGfree_auto_mem(void);
 
-#ifdef ENABLE_THREAD_SAFETY
 void		ecpg_pthreads_init(void);
-#endif
 
 #ifdef __cplusplus
 }
diff --git a/src/interfaces/ecpg/include/meson.build b/src/interfaces/ecpg/include/meson.build
index 2278f0d305..543e48fd6e 100644
--- a/src/interfaces/ecpg/include/meson.build
+++ b/src/interfaces/ecpg/include/meson.build
@@ -3,7 +3,6 @@
 ecpg_inc = include_directories('.')
 
 ecpg_conf_keys = [
-  'ENABLE_THREAD_SAFETY',
   'HAVE_INT64',
   'HAVE_LONG_INT_64',
   'HAVE_LONG_LONG_INT_64',
diff --git a/src/interfaces/ecpg/test/expected/thread-alloc.c b/src/interfaces/ecpg/test/expected/thread-alloc.c
index 37ef44ed94..3b31d27fd3 100644
--- a/src/interfaces/ecpg/test/expected/thread-alloc.c
+++ b/src/interfaces/ecpg/test/expected/thread-alloc.c
@@ -11,14 +11,6 @@
 #include <stdlib.h>
 #include "ecpg_config.h"
 
-#ifndef ENABLE_THREAD_SAFETY
-int
-main(void)
-{
-	printf("No threading enabled.\n");
-	return 0;
-}
-#else
 #ifdef WIN32
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -101,7 +93,7 @@ struct sqlca_t *ECPGget_sqlca(void);
 
 #endif
 
-#line 26 "alloc.pgc"
+#line 18 "alloc.pgc"

 #line 1 "regression.h"
@@ -111,14 +103,14 @@ struct sqlca_t *ECPGget_sqlca(void);

-#line 27 "alloc.pgc"
+#line 19 "alloc.pgc"

 /* exec sql whenever sqlerror  sqlprint ; */
-#line 29 "alloc.pgc"
+#line 21 "alloc.pgc"
 
 /* exec sql whenever not found  sqlprint ; */
-#line 30 "alloc.pgc"
+#line 22 "alloc.pgc"

 #ifdef WIN32
@@ -134,54 +126,54 @@ static void* fn(void* arg)

-#line 41 "alloc.pgc"
+#line 33 "alloc.pgc"
  int value ;
  
-#line 42 "alloc.pgc"
+#line 34 "alloc.pgc"
  char name [ 100 ] ;
  
-#line 43 "alloc.pgc"
+#line 35 "alloc.pgc"
  char ** r = NULL ;
 /* exec sql end declare section */
-#line 44 "alloc.pgc"
+#line 36 "alloc.pgc"

 	value = (intptr_t) arg;
 	sprintf(name, "Connection: %d", value);
 
 	{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , name, 0); 
-#line 49 "alloc.pgc"
+#line 41 "alloc.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 49 "alloc.pgc"
+#line 41 "alloc.pgc"
 
 	{ ECPGsetcommit(__LINE__, "on", NULL);
-#line 50 "alloc.pgc"
+#line 42 "alloc.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 50 "alloc.pgc"
+#line 42 "alloc.pgc"
 
 	for (i = 1; i <= REPEATS; ++i)
 	{
 		{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select relname from pg_class where relname = 'pg_class'", ECPGt_EOIT, 
 	ECPGt_char,&(r),(long)0,(long)0,(1)*sizeof(char), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
-#line 53 "alloc.pgc"
+#line 45 "alloc.pgc"
 
 if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 53 "alloc.pgc"
+#line 45 "alloc.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 53 "alloc.pgc"
+#line 45 "alloc.pgc"
 
 		free(r);
 		r = NULL;
 	}
 	{ ECPGdisconnect(__LINE__, name);
-#line 57 "alloc.pgc"
+#line 49 "alloc.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 57 "alloc.pgc"
+#line 49 "alloc.pgc"

 	return 0;
@@ -215,4 +207,3 @@ int main ()
 
 	return 0;
 }
-#endif
diff --git a/src/interfaces/ecpg/test/expected/thread-descriptor.c b/src/interfaces/ecpg/test/expected/thread-descriptor.c
index f56cc25ab0..e34f4708d1 100644
--- a/src/interfaces/ecpg/test/expected/thread-descriptor.c
+++ b/src/interfaces/ecpg/test/expected/thread-descriptor.c
@@ -7,7 +7,6 @@
 #define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))
 
 #line 1 "descriptor.pgc"
-#ifdef ENABLE_THREAD_SAFETY
 #ifdef WIN32
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -16,7 +15,6 @@
 #else
 #include <pthread.h>
 #endif
-#endif
 #include <stdio.h>
 
 #define THREADS		16
@@ -91,16 +89,16 @@ struct sqlca_t *ECPGget_sqlca(void);
 
 #endif
 
-#line 16 "descriptor.pgc"
+#line 14 "descriptor.pgc"
 
 /* exec sql whenever sqlerror  sqlprint ; */
-#line 17 "descriptor.pgc"
+#line 15 "descriptor.pgc"
 
 /* exec sql whenever not found  sqlprint ; */
-#line 18 "descriptor.pgc"
+#line 16 "descriptor.pgc"

-#if defined(ENABLE_THREAD_SAFETY) && defined(WIN32)
+#if defined(WIN32)
 static unsigned __stdcall fn(void* arg)
 #else
 static void* fn(void* arg)
@@ -111,16 +109,16 @@ static void* fn(void* arg)
 	for (i = 1; i <= REPEATS; ++i)
 	{
 		ECPGallocate_desc(__LINE__, "mydesc");
-#line 30 "descriptor.pgc"
+#line 28 "descriptor.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();
-#line 30 "descriptor.pgc"
+#line 28 "descriptor.pgc"
 
 		ECPGdeallocate_desc(__LINE__, "mydesc");
-#line 31 "descriptor.pgc"
+#line 29 "descriptor.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();
-#line 31 "descriptor.pgc"
+#line 29 "descriptor.pgc"
 
 	}
 
@@ -129,7 +127,6 @@ if (sqlca.sqlcode < 0) sqlprint();
 
 int main ()
 {
-#ifdef ENABLE_THREAD_SAFETY
 	int i;
 #ifdef WIN32
 	HANDLE threads[THREADS];
@@ -153,9 +150,6 @@ int main ()
 	for (i = 0; i < THREADS; ++i)
 		pthread_join(threads[i], NULL);
 #endif
-#else
-	fn(NULL);
-#endif
 
 	return 0;
 }
diff --git a/src/interfaces/ecpg/test/expected/thread-prep.c b/src/interfaces/ecpg/test/expected/thread-prep.c
index 7cdf2505d3..052e27b634 100644
--- a/src/interfaces/ecpg/test/expected/thread-prep.c
+++ b/src/interfaces/ecpg/test/expected/thread-prep.c
@@ -11,14 +11,6 @@
 #include <stdlib.h>
 #include "ecpg_config.h"
 
-#ifndef ENABLE_THREAD_SAFETY
-int
-main(void)
-{
-	printf("No threading enabled.\n");
-	return 0;
-}
-#else
 #ifdef WIN32
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -101,7 +93,7 @@ struct sqlca_t *ECPGget_sqlca(void);
 
 #endif
 
-#line 26 "prep.pgc"
+#line 18 "prep.pgc"

 #line 1 "regression.h"
@@ -111,14 +103,14 @@ struct sqlca_t *ECPGget_sqlca(void);

-#line 27 "prep.pgc"
+#line 19 "prep.pgc"

 /* exec sql whenever sqlerror  sqlprint ; */
-#line 29 "prep.pgc"
+#line 21 "prep.pgc"
 
 /* exec sql whenever not found  sqlprint ; */
-#line 30 "prep.pgc"
+#line 22 "prep.pgc"

 #ifdef WIN32
@@ -134,64 +126,64 @@ static void* fn(void* arg)

-#line 41 "prep.pgc"
+#line 33 "prep.pgc"
  int value ;
  
-#line 42 "prep.pgc"
+#line 34 "prep.pgc"
  char name [ 100 ] ;
  
-#line 43 "prep.pgc"
+#line 35 "prep.pgc"
  char query [ 256 ] = "INSERT INTO T VALUES ( ? )" ;
 /* exec sql end declare section */
-#line 44 "prep.pgc"
+#line 36 "prep.pgc"

 	value = (intptr_t) arg;
 	sprintf(name, "Connection: %d", value);
 
 	{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , name, 0); 
-#line 49 "prep.pgc"
+#line 41 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 49 "prep.pgc"
+#line 41 "prep.pgc"
 
 	{ ECPGsetcommit(__LINE__, "on", NULL);
-#line 50 "prep.pgc"
+#line 42 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 50 "prep.pgc"
+#line 42 "prep.pgc"
 
 	for (i = 1; i <= REPEATS; ++i)
 	{
 		{ ECPGprepare(__LINE__, NULL, 0, "i", query);
-#line 53 "prep.pgc"
+#line 45 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 53 "prep.pgc"
+#line 45 "prep.pgc"
 
 		{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "i", 
 	ECPGt_int,&(value),(long)1,(long)1,sizeof(int), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
-#line 54 "prep.pgc"
+#line 46 "prep.pgc"
 
 if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 54 "prep.pgc"
+#line 46 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 54 "prep.pgc"
+#line 46 "prep.pgc"
 
 	}
 	{ ECPGdeallocate(__LINE__, 0, NULL, "i");
-#line 56 "prep.pgc"
+#line 48 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 56 "prep.pgc"
+#line 48 "prep.pgc"
 
 	{ ECPGdisconnect(__LINE__, name);
-#line 57 "prep.pgc"
+#line 49 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 57 "prep.pgc"
+#line 49 "prep.pgc"

 	return 0;
@@ -207,34 +199,34 @@ int main ()
 #endif
 
 	{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); 
-#line 71 "prep.pgc"
+#line 63 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 71 "prep.pgc"
+#line 63 "prep.pgc"
 
 	{ ECPGsetcommit(__LINE__, "on", NULL);
-#line 72 "prep.pgc"
+#line 64 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 72 "prep.pgc"
+#line 64 "prep.pgc"
 
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table if exists T", ECPGt_EOIT, ECPGt_EORT);
-#line 73 "prep.pgc"
+#line 65 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 73 "prep.pgc"
+#line 65 "prep.pgc"
 
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table T ( i int )", ECPGt_EOIT, ECPGt_EORT);
-#line 74 "prep.pgc"
+#line 66 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 74 "prep.pgc"
+#line 66 "prep.pgc"
 
 	{ ECPGdisconnect(__LINE__, "CURRENT");
-#line 75 "prep.pgc"
+#line 67 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 75 "prep.pgc"
+#line 67 "prep.pgc"

 #ifdef WIN32
@@ -256,4 +248,3 @@ if (sqlca.sqlcode < 0) sqlprint();}
 
 	return 0;
 }
-#endif
diff --git a/src/interfaces/ecpg/test/expected/thread-thread.c b/src/interfaces/ecpg/test/expected/thread-thread.c
index 0e75c47fab..95faa223c2 100644
--- a/src/interfaces/ecpg/test/expected/thread-thread.c
+++ b/src/interfaces/ecpg/test/expected/thread-thread.c
@@ -15,14 +15,6 @@
 #include <stdlib.h>
 #include "ecpg_config.h"
 
-#ifndef ENABLE_THREAD_SAFETY
-int
-main(void)
-{
-	printf("No threading enabled.\n");
-	return 0;
-}
-#else
 #ifndef WIN32
 #include <pthread.h>
 #else
@@ -38,7 +30,7 @@ main(void)

-#line 24 "thread.pgc"
+#line 16 "thread.pgc"

 void *test_thread(void *arg);
@@ -57,10 +49,10 @@ int main()
   /* exec sql begin declare section */

-#line 40 "thread.pgc"
+#line 32 "thread.pgc"
  int l_rows ;
 /* exec sql end declare section */
-#line 41 "thread.pgc"
+#line 33 "thread.pgc"

  /* Do not switch on debug output for regression tests. The threads get executed in
@@ -69,22 +61,22 @@ int main()
 
   /* setup test_thread table */
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 48 "thread.pgc"
+#line 40 "thread.pgc"
 
   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test_thread", ECPGt_EOIT, ECPGt_EORT);}
-#line 49 "thread.pgc"
+#line 41 "thread.pgc"
  /* DROP might fail */
   { ECPGtrans(__LINE__, NULL, "commit");}
-#line 50 "thread.pgc"
+#line 42 "thread.pgc"
 
   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table test_thread ( tstamp timestamp not null default cast ( timeofday ( ) as timestamp ) , thread text not null , iteration integer not null , primary key ( thread , iteration ) )", ECPGt_EOIT, ECPGt_EORT);}
-#line 55 "thread.pgc"
+#line 47 "thread.pgc"
 
   { ECPGtrans(__LINE__, NULL, "commit");}
-#line 56 "thread.pgc"
+#line 48 "thread.pgc"
 
   { ECPGdisconnect(__LINE__, "CURRENT");}
-#line 57 "thread.pgc"
+#line 49 "thread.pgc"

   /* create, and start, threads */
@@ -116,18 +108,18 @@ int main()
 
   /* and check results */
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 87 "thread.pgc"
+#line 79 "thread.pgc"
 
   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from test_thread", ECPGt_EOIT, 
 	ECPGt_int,&(l_rows),(long)1,(long)1,sizeof(int), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
-#line 88 "thread.pgc"
+#line 80 "thread.pgc"
 
   { ECPGtrans(__LINE__, NULL, "commit");}
-#line 89 "thread.pgc"
+#line 81 "thread.pgc"
 
   { ECPGdisconnect(__LINE__, "CURRENT");}
-#line 90 "thread.pgc"
+#line 82 "thread.pgc"
 
   if( l_rows == (nthreads * iterations) )
     printf("Success.\n");
@@ -145,13 +137,13 @@ void *test_thread(void *arg)

-#line 104 "thread.pgc"
+#line 96 "thread.pgc"
  int l_i ;
  
-#line 105 "thread.pgc"
+#line 97 "thread.pgc"
  char l_connection [ 128 ] ;
 /* exec sql end declare section */
-#line 106 "thread.pgc"
+#line 98 "thread.pgc"

   /* build up connection name, and connect to database */
@@ -161,13 +153,13 @@ void *test_thread(void *arg)
   _snprintf(l_connection, sizeof(l_connection), "thread_%03ld", threadnum);
 #endif
   /* exec sql whenever sqlerror  sqlprint ; */
-#line 114 "thread.pgc"
+#line 106 "thread.pgc"
 
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , l_connection, 0); 
-#line 115 "thread.pgc"
+#line 107 "thread.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 115 "thread.pgc"
+#line 107 "thread.pgc"
 
   if( sqlca.sqlcode != 0 )
     {
@@ -175,10 +167,10 @@ if (sqlca.sqlcode < 0) sqlprint();}
       return NULL;
     }
   { ECPGtrans(__LINE__, l_connection, "begin");
-#line 121 "thread.pgc"
+#line 113 "thread.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 121 "thread.pgc"
+#line 113 "thread.pgc"

   /* insert into test_thread table */
@@ -189,10 +181,10 @@ if (sqlca.sqlcode < 0) sqlprint();}
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
 	ECPGt_int,&(l_i),(long)1,(long)1,sizeof(int), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
-#line 126 "thread.pgc"
+#line 118 "thread.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 126 "thread.pgc"
+#line 118 "thread.pgc"
 
       if( sqlca.sqlcode != 0 )
 	printf("%s: ERROR: insert failed!\n", l_connection);
@@ -200,17 +192,16 @@ if (sqlca.sqlcode < 0) sqlprint();}
 
   /* all done */
   { ECPGtrans(__LINE__, l_connection, "commit");
-#line 132 "thread.pgc"
+#line 124 "thread.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 132 "thread.pgc"
+#line 124 "thread.pgc"
 
   { ECPGdisconnect(__LINE__, l_connection);
-#line 133 "thread.pgc"
+#line 125 "thread.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 133 "thread.pgc"
+#line 125 "thread.pgc"
 
   return NULL;
 }
-#endif /* ENABLE_THREAD_SAFETY */
diff --git a/src/interfaces/ecpg/test/expected/thread-thread_implicit.c b/src/interfaces/ecpg/test/expected/thread-thread_implicit.c
index 0df2794530..7ac0297a23 100644
--- a/src/interfaces/ecpg/test/expected/thread-thread_implicit.c
+++ b/src/interfaces/ecpg/test/expected/thread-thread_implicit.c
@@ -15,14 +15,6 @@
 #include <stdlib.h>
 #include "ecpg_config.h"
 
-#ifndef ENABLE_THREAD_SAFETY
-int
-main(void)
-{
-	printf("No threading enabled.\n");
-	return 0;
-}
-#else
 #ifndef WIN32
 #include <pthread.h>
 #else
@@ -38,7 +30,7 @@ main(void)

-#line 24 "thread_implicit.pgc"
+#line 16 "thread_implicit.pgc"

 void *test_thread(void *arg);
@@ -57,10 +49,10 @@ int main()
   /* exec sql begin declare section */

-#line 40 "thread_implicit.pgc"
+#line 32 "thread_implicit.pgc"
  int l_rows ;
 /* exec sql end declare section */
-#line 41 "thread_implicit.pgc"
+#line 33 "thread_implicit.pgc"

  /* Do not switch on debug output for regression tests. The threads get executed in
@@ -69,22 +61,22 @@ int main()
 
   /* setup test_thread table */
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 48 "thread_implicit.pgc"
+#line 40 "thread_implicit.pgc"
 
   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test_thread", ECPGt_EOIT, ECPGt_EORT);}
-#line 49 "thread_implicit.pgc"
+#line 41 "thread_implicit.pgc"
  /* DROP might fail */
   { ECPGtrans(__LINE__, NULL, "commit");}
-#line 50 "thread_implicit.pgc"
+#line 42 "thread_implicit.pgc"
 
   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table test_thread ( tstamp timestamp not null default cast ( timeofday ( ) as timestamp ) , thread text not null , iteration integer not null , primary key ( thread , iteration ) )", ECPGt_EOIT, ECPGt_EORT);}
-#line 55 "thread_implicit.pgc"
+#line 47 "thread_implicit.pgc"
 
   { ECPGtrans(__LINE__, NULL, "commit");}
-#line 56 "thread_implicit.pgc"
+#line 48 "thread_implicit.pgc"
 
   { ECPGdisconnect(__LINE__, "CURRENT");}
-#line 57 "thread_implicit.pgc"
+#line 49 "thread_implicit.pgc"

   /* create, and start, threads */
@@ -116,18 +108,18 @@ int main()
 
   /* and check results */
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 87 "thread_implicit.pgc"
+#line 79 "thread_implicit.pgc"
 
   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from test_thread", ECPGt_EOIT, 
 	ECPGt_int,&(l_rows),(long)1,(long)1,sizeof(int), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
-#line 88 "thread_implicit.pgc"
+#line 80 "thread_implicit.pgc"
 
   { ECPGtrans(__LINE__, NULL, "commit");}
-#line 89 "thread_implicit.pgc"
+#line 81 "thread_implicit.pgc"
 
   { ECPGdisconnect(__LINE__, "CURRENT");}
-#line 90 "thread_implicit.pgc"
+#line 82 "thread_implicit.pgc"
 
   if( l_rows == (nthreads * iterations) )
     printf("Success.\n");
@@ -145,13 +137,13 @@ void *test_thread(void *arg)

-#line 104 "thread_implicit.pgc"
+#line 96 "thread_implicit.pgc"
  int l_i ;
  
-#line 105 "thread_implicit.pgc"
+#line 97 "thread_implicit.pgc"
  char l_connection [ 128 ] ;
 /* exec sql end declare section */
-#line 106 "thread_implicit.pgc"
+#line 98 "thread_implicit.pgc"

   /* build up connection name, and connect to database */
@@ -161,13 +153,13 @@ void *test_thread(void *arg)
   _snprintf(l_connection, sizeof(l_connection), "thread_%03ld", threadnum);
 #endif
   /* exec sql whenever sqlerror  sqlprint ; */
-#line 114 "thread_implicit.pgc"
+#line 106 "thread_implicit.pgc"
 
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , l_connection, 0); 
-#line 115 "thread_implicit.pgc"
+#line 107 "thread_implicit.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 115 "thread_implicit.pgc"
+#line 107 "thread_implicit.pgc"
 
   if( sqlca.sqlcode != 0 )
     {
@@ -175,10 +167,10 @@ if (sqlca.sqlcode < 0) sqlprint();}
       return NULL;
     }
   { ECPGtrans(__LINE__, NULL, "begin");
-#line 121 "thread_implicit.pgc"
+#line 113 "thread_implicit.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 121 "thread_implicit.pgc"
+#line 113 "thread_implicit.pgc"

   /* insert into test_thread table */
@@ -189,10 +181,10 @@ if (sqlca.sqlcode < 0) sqlprint();}
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
 	ECPGt_int,&(l_i),(long)1,(long)1,sizeof(int), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
-#line 126 "thread_implicit.pgc"
+#line 118 "thread_implicit.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 126 "thread_implicit.pgc"
+#line 118 "thread_implicit.pgc"
 
       if( sqlca.sqlcode != 0 )
 	printf("%s: ERROR: insert failed!\n", l_connection);
@@ -200,17 +192,16 @@ if (sqlca.sqlcode < 0) sqlprint();}
 
   /* all done */
   { ECPGtrans(__LINE__, NULL, "commit");
-#line 132 "thread_implicit.pgc"
+#line 124 "thread_implicit.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 132 "thread_implicit.pgc"
+#line 124 "thread_implicit.pgc"
 
   { ECPGdisconnect(__LINE__, l_connection);
-#line 133 "thread_implicit.pgc"
+#line 125 "thread_implicit.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 133 "thread_implicit.pgc"
+#line 125 "thread_implicit.pgc"
 
   return NULL;
 }
-#endif /* ENABLE_THREAD_SAFETY */
diff --git a/src/interfaces/ecpg/test/thread/alloc.pgc b/src/interfaces/ecpg/test/thread/alloc.pgc
index c0021a737e..d3d35493bf 100644
--- a/src/interfaces/ecpg/test/thread/alloc.pgc
+++ b/src/interfaces/ecpg/test/thread/alloc.pgc
@@ -2,14 +2,6 @@
 #include <stdlib.h>
 #include "ecpg_config.h"
 
-#ifndef ENABLE_THREAD_SAFETY
-int
-main(void)
-{
-	printf("No threading enabled.\n");
-	return 0;
-}
-#else
 #ifdef WIN32
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -87,4 +79,3 @@ int main ()
 
 	return 0;
 }
-#endif
diff --git a/src/interfaces/ecpg/test/thread/descriptor.pgc b/src/interfaces/ecpg/test/thread/descriptor.pgc
index 76a7a5dff5..30bce7c87b 100644
--- a/src/interfaces/ecpg/test/thread/descriptor.pgc
+++ b/src/interfaces/ecpg/test/thread/descriptor.pgc
@@ -1,4 +1,3 @@
-#ifdef ENABLE_THREAD_SAFETY
 #ifdef WIN32
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -7,7 +6,6 @@
 #else
 #include <pthread.h>
 #endif
-#endif
 #include <stdio.h>
 
 #define THREADS		16
@@ -17,7 +15,7 @@ EXEC SQL include sqlca;
 EXEC SQL whenever sqlerror sqlprint;
 EXEC SQL whenever not found sqlprint;
 
-#if defined(ENABLE_THREAD_SAFETY) && defined(WIN32)
+#if defined(WIN32)
 static unsigned __stdcall fn(void* arg)
 #else
 static void* fn(void* arg)
@@ -36,7 +34,6 @@ static void* fn(void* arg)
 
 int main ()
 {
-#ifdef ENABLE_THREAD_SAFETY
 	int i;
 #ifdef WIN32
 	HANDLE threads[THREADS];
@@ -60,9 +57,6 @@ int main ()
 	for (i = 0; i < THREADS; ++i)
 		pthread_join(threads[i], NULL);
 #endif
-#else
-	fn(NULL);
-#endif
 
 	return 0;
 }
diff --git a/src/interfaces/ecpg/test/thread/prep.pgc b/src/interfaces/ecpg/test/thread/prep.pgc
index d7ecfd4855..f61b31ce10 100644
--- a/src/interfaces/ecpg/test/thread/prep.pgc
+++ b/src/interfaces/ecpg/test/thread/prep.pgc
@@ -2,14 +2,6 @@
 #include <stdlib.h>
 #include "ecpg_config.h"
 
-#ifndef ENABLE_THREAD_SAFETY
-int
-main(void)
-{
-	printf("No threading enabled.\n");
-	return 0;
-}
-#else
 #ifdef WIN32
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -93,4 +85,3 @@ int main ()
 
 	return 0;
 }
-#endif
diff --git a/src/interfaces/ecpg/test/thread/thread.pgc b/src/interfaces/ecpg/test/thread/thread.pgc
index e7d8c00af6..b9b9ebb441 100644
--- a/src/interfaces/ecpg/test/thread/thread.pgc
+++ b/src/interfaces/ecpg/test/thread/thread.pgc
@@ -6,14 +6,6 @@
 #include <stdlib.h>
 #include "ecpg_config.h"
 
-#ifndef ENABLE_THREAD_SAFETY
-int
-main(void)
-{
-	printf("No threading enabled.\n");
-	return 0;
-}
-#else
 #ifndef WIN32
 #include <pthread.h>
 #else
@@ -133,4 +125,3 @@ void *test_thread(void *arg)
   EXEC SQL DISCONNECT :l_connection;
   return NULL;
 }
-#endif /* ENABLE_THREAD_SAFETY */
diff --git a/src/interfaces/ecpg/test/thread/thread_implicit.pgc b/src/interfaces/ecpg/test/thread/thread_implicit.pgc
index b4cae7e1ae..ff9b12a943 100644
--- a/src/interfaces/ecpg/test/thread/thread_implicit.pgc
+++ b/src/interfaces/ecpg/test/thread/thread_implicit.pgc
@@ -6,14 +6,6 @@
 #include <stdlib.h>
 #include "ecpg_config.h"
 
-#ifndef ENABLE_THREAD_SAFETY
-int
-main(void)
-{
-	printf("No threading enabled.\n");
-	return 0;
-}
-#else
 #ifndef WIN32
 #include <pthread.h>
 #else
@@ -133,4 +125,3 @@ void *test_thread(void *arg)
   EXEC SQL DISCONNECT :l_connection;
   return NULL;
 }
-#endif /* ENABLE_THREAD_SAFETY */
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 0919d8f32f..46653682b0 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -69,11 +69,8 @@ endif
 
 ifeq ($(PORTNAME), win32)
 OBJS += \
+	pthread-win32.o \
 	win32.o
-
-ifeq ($(enable_thread_safety), yes)
-OBJS += pthread-win32.o
-endif
 endif

diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index a8584d2c68..837c5321aa 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -52,13 +52,11 @@
 #include <netinet/tcp.h>
 #endif
 
-#ifdef ENABLE_THREAD_SAFETY
 #ifdef WIN32
 #include "pthread-win32.h"
 #else
 #include <pthread.h>
 #endif
-#endif
 
 #ifdef USE_LDAP
 #ifdef WIN32
@@ -7784,7 +7782,6 @@ pqGetHomeDirectory(char *buf, int bufsize)
 static void
 default_threadlock(int acquire)
 {
-#ifdef ENABLE_THREAD_SAFETY
 #ifndef WIN32
 	static pthread_mutex_t singlethread_lock = PTHREAD_MUTEX_INITIALIZER;
 #else
@@ -7813,7 +7810,6 @@ default_threadlock(int acquire)
 		if (pthread_mutex_unlock(&singlethread_lock))
 			Assert(false);
 	}
-#endif
 }
 
 pgthreadlock_t
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 14d706efd5..39d10f0eb3 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -3824,11 +3824,7 @@ PQisnonblocking(const PGconn *conn)
 int
 PQisthreadsafe(void)
 {
-#ifdef ENABLE_THREAD_SAFETY
 	return true;
-#else
-	return false;
-#endif
 }

diff --git a/src/interfaces/libpq/fe-print.c b/src/interfaces/libpq/fe-print.c
index 40620b47e9..8af15032be 100644
--- a/src/interfaces/libpq/fe-print.c
+++ b/src/interfaces/libpq/fe-print.c
@@ -88,14 +88,11 @@ PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po)
 		bool		usePipe = false;
 		char	   *pagerenv;
 
-#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
+#if !defined(WIN32)
 		sigset_t	osigset;
 		bool		sigpipe_masked = false;
 		bool		sigpipe_pending;
 #endif
-#if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
-		pqsigfunc	oldsigpipehandler = NULL;
-#endif
 
 #ifdef TIOCGWINSZ
 		struct winsize screen_size;
@@ -186,12 +183,8 @@ PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po)
 				{
 					usePipe = true;
 #ifndef WIN32
-#ifdef ENABLE_THREAD_SAFETY
 					if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0)
 						sigpipe_masked = true;
-#else
-					oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
-#endif							/* ENABLE_THREAD_SAFETY */
 #endif							/* WIN32 */
 				}
 				else
@@ -324,13 +317,9 @@ exit:
 #else
 			pclose(fout);
 
-#ifdef ENABLE_THREAD_SAFETY
 			/* we can't easily verify if EPIPE occurred, so say it did */
 			if (sigpipe_masked)
 				pq_reset_sigpipe(&osigset, sigpipe_pending, true);
-#else
-			pqsignal(SIGPIPE, oldsigpipehandler);
-#endif							/* ENABLE_THREAD_SAFETY */
 #endif							/* WIN32 */
 		}
 	}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 390c888c96..4976d963a2 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -44,13 +44,11 @@
 
 #include <sys/stat.h>
 
-#ifdef ENABLE_THREAD_SAFETY
 #ifdef WIN32
 #include "pthread-win32.h"
 #else
 #include <pthread.h>
 #endif
-#endif
 
 /*
  * These SSL-related #includes must come after all system-provided headers.
@@ -91,7 +89,6 @@ static bool pq_init_crypto_lib = true;
 
 static bool ssl_lib_initialized = false;
 
-#ifdef ENABLE_THREAD_SAFETY
 static long crypto_open_connections = 0;
 
 #ifndef WIN32
@@ -100,7 +97,6 @@ static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_mutex_t ssl_config_mutex = NULL;
 static long win32_ssl_create_mutex = 0;
 #endif
-#endif							/* ENABLE_THREAD_SAFETY */
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
@@ -112,15 +108,12 @@ static int	ssl_protocol_version_to_openssl(const char *protocol);
 void
 pgtls_init_library(bool do_ssl, int do_crypto)
 {
-#ifdef ENABLE_THREAD_SAFETY
-
 	/*
 	 * Disallow changing the flags while we have open connections, else we'd
 	 * get completely confused.
 	 */
 	if (crypto_open_connections != 0)
 		return;
-#endif
 
 	pq_init_ssl_lib = do_ssl;
 	pq_init_crypto_lib = do_crypto;
@@ -720,7 +713,7 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 	return rc;
 }
 
-#if defined(ENABLE_THREAD_SAFETY) && defined(HAVE_CRYPTO_LOCK)
+#if defined(HAVE_CRYPTO_LOCK)
 /*
  *	Callback functions for OpenSSL internal locking.  (OpenSSL 1.1.0
  *	does its own locking, and doesn't need these anymore.  The
@@ -761,7 +754,7 @@ pq_lockingcallback(int mode, int n, const char *file, int line)
 			Assert(false);
 	}
 }
-#endif							/* ENABLE_THREAD_SAFETY && HAVE_CRYPTO_LOCK */
+#endif							/* HAVE_CRYPTO_LOCK */
 
 /*
  * Initialize SSL library.
@@ -776,7 +769,6 @@ pq_lockingcallback(int mode, int n, const char *file, int line)
 int
 pgtls_init(PGconn *conn, bool do_ssl, bool do_crypto)
 {
-#ifdef ENABLE_THREAD_SAFETY
 #ifdef WIN32
 	/* Also see similar code in fe-connect.c, default_threadlock() */
 	if (ssl_config_mutex == NULL)
@@ -842,7 +834,6 @@ pgtls_init(PGconn *conn, bool do_ssl, bool do_crypto)
 		}
 	}
 #endif							/* HAVE_CRYPTO_LOCK */
-#endif							/* ENABLE_THREAD_SAFETY */
 
 	if (!ssl_lib_initialized && do_ssl)
 	{
@@ -859,9 +850,7 @@ pgtls_init(PGconn *conn, bool do_ssl, bool do_crypto)
 		ssl_lib_initialized = true;
 	}
 
-#ifdef ENABLE_THREAD_SAFETY
 	pthread_mutex_unlock(&ssl_config_mutex);
-#endif
 	return 0;
 }
 
@@ -880,7 +869,7 @@ pgtls_init(PGconn *conn, bool do_ssl, bool do_crypto)
 static void
 destroy_ssl_system(void)
 {
-#if defined(ENABLE_THREAD_SAFETY) && defined(HAVE_CRYPTO_LOCK)
+#if defined(HAVE_CRYPTO_LOCK)
 	/* Mutex is created in pgtls_init() */
 	if (pthread_mutex_lock(&ssl_config_mutex))
 		return;
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 8069e38142..bd72a87bbb 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -35,13 +35,11 @@
 
 #include <sys/stat.h>
 
-#ifdef ENABLE_THREAD_SAFETY
 #ifdef WIN32
 #include "pthread-win32.h"
 #else
 #include <pthread.h>
 #endif
-#endif
 
 #include "fe-auth.h"
 #include "libpq-fe.h"
@@ -56,8 +54,6 @@
 
 #define SIGPIPE_MASKED(conn)	((conn)->sigpipe_so || (conn)->sigpipe_flag)
 
-#ifdef ENABLE_THREAD_SAFETY
-
 struct sigpipe_info
 {
 	sigset_t	oldsigmask;
@@ -90,24 +86,6 @@ struct sigpipe_info
 			pq_reset_sigpipe(&(spinfo).oldsigmask, (spinfo).sigpipe_pending, \
 							 (spinfo).got_epipe); \
 	} while (0)
-#else							/* !ENABLE_THREAD_SAFETY */
-
-#define DECLARE_SIGPIPE_INFO(spinfo) pqsigfunc spinfo = NULL
-
-#define DISABLE_SIGPIPE(conn, spinfo, failaction) \
-	do { \
-		if (!SIGPIPE_MASKED(conn)) \
-			spinfo = pqsignal(SIGPIPE, SIG_IGN); \
-	} while (0)
-
-#define REMEMBER_EPIPE(spinfo, cond)
-
-#define RESTORE_SIGPIPE(conn, spinfo) \
-	do { \
-		if (!SIGPIPE_MASKED(conn)) \
-			pqsignal(SIGPIPE, spinfo); \
-	} while (0)
-#endif							/* ENABLE_THREAD_SAFETY */
 #else							/* WIN32 */
 
 #define DECLARE_SIGPIPE_INFO(spinfo)
@@ -524,7 +502,7 @@ PQgssEncInUse(PGconn *conn)
 #endif							/* ENABLE_GSS */

-#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
+#if !defined(WIN32)
 
 /*
  *	Block SIGPIPE for this thread.  This prevents send()/write() from exiting
@@ -608,4 +586,4 @@ pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
 	SOCK_ERRNO_SET(save_errno);
 }
 
-#endif							/* ENABLE_THREAD_SAFETY && !WIN32 */
+#endif							/* !WIN32 */
diff --git a/src/interfaces/libpq/legacy-pqsignal.c b/src/interfaces/libpq/legacy-pqsignal.c
index 790ab5a18c..97baa86a27 100644
--- a/src/interfaces/libpq/legacy-pqsignal.c
+++ b/src/interfaces/libpq/legacy-pqsignal.c
@@ -28,9 +28,7 @@
  * with the semantics it had in 9.2; in particular, this has different
  * behavior for SIGALRM than the version in src/port/pqsignal.c.
  *
- * libpq itself uses this only for SIGPIPE (and even then, only in
- * non-ENABLE_THREAD_SAFETY builds), so the incompatibility isn't
- * troublesome for internal references.
+ * libpq itself does not use this.
  */
 pqsigfunc
 pqsignal(int signo, pqsigfunc func)
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 0045f83cbf..10f20c1175 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -31,14 +31,12 @@
 #include <sys/time.h>
 #endif
 
-#ifdef ENABLE_THREAD_SAFETY
 #ifdef WIN32
 #include "pthread-win32.h"
 #else
 #include <pthread.h>
 #endif
 #include <signal.h>
-#endif
 
 /* include stuff common to fe and be */
 #include "libpq/pqcomm.h"
@@ -681,15 +679,10 @@ extern int	pqPacketSend(PGconn *conn, char pack_type,
 						 const void *buf, size_t buf_len);
 extern bool pqGetHomeDirectory(char *buf, int bufsize);
 
-#ifdef ENABLE_THREAD_SAFETY
 extern pgthreadlock_t pg_g_threadlock;
 
 #define pglock_thread()		pg_g_threadlock(true)
 #define pgunlock_thread()	pg_g_threadlock(false)
-#else
-#define pglock_thread()		((void) 0)
-#define pgunlock_thread()	((void) 0)
-#endif
 
 /* === in fe-exec.c === */
 
@@ -765,7 +758,7 @@ extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
 extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);
 
-#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
+#if !defined(WIN32)
 extern int	pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
 extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
 							 bool got_epipe);
diff --git a/src/makefiles/meson.build b/src/makefiles/meson.build
index 13045cbd6e..be946f7b38 100644
--- a/src/makefiles/meson.build
+++ b/src/makefiles/meson.build
@@ -52,7 +52,6 @@ pgxs_kv = {
   'abs_top_builddir': meson.build_root(),
   'abs_top_srcdir': meson.source_root(),
 
-  'enable_thread_safety': 'yes',
   'enable_rpath': get_option('rpath') ? 'yes' : 'no',
   'enable_nls': libintl.found() ? 'yes' : 'no',
   'enable_tap_tests': tap_tests_enabled ? 'yes' : 'no',
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index b6d31c3583..2f484d89fa 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -219,7 +219,6 @@ sub GenerateFiles
 		DLSUFFIX => '".dll"',
 		ENABLE_GSS => $self->{options}->{gss} ? 1 : undef,
 		ENABLE_NLS => $self->{options}->{nls} ? 1 : undef,
-		ENABLE_THREAD_SAFETY => 1,
 		HAVE_APPEND_HISTORY => undef,
 		HAVE_ASN1_STRING_GET0_DATA => undef,
 		HAVE_ATOMICS => 1,
@@ -1199,7 +1198,7 @@ sub GetFakeConfigure
 {
 	my $self = shift;
 
-	my $cfg = '--enable-thread-safety';
+	my $cfg = '';
 	$cfg .= ' --enable-cassert' if ($self->{options}->{asserts});
 	$cfg .= ' --enable-nls' if ($self->{options}->{nls});
 	$cfg .= ' --enable-tap-tests' if ($self->{options}->{tap_tests});
diff --git a/src/tools/msvc/ecpg_regression.proj b/src/tools/msvc/ecpg_regression.proj
index ec2760b1f6..0ec60a275e 100644
--- a/src/tools/msvc/ecpg_regression.proj
+++ b/src/tools/msvc/ecpg_regression.proj
@@ -54,7 +54,7 @@
 
   <!-- Run ECPG and the Visual C++ compiler on the files. Don't bother with dependency check between the steps -->
   <Exec WorkingDirectory="%(Pgc.RelativeDir)" Command="$(OUTDIR)ecpg\ecpg -I ../../include --regression $(ECPGPARAM) -o %(Pgc.Filename).c %(Pgc.Filename).pgc" />
-  <Exec WorkingDirectory="%(Pgc.RelativeDir)" Command="cl /nologo %(Pgc.FileName).c /TC /MD$(DEBUGLIB) /DENABLE_THREAD_SAFETY /DWIN32 /I. /I..\..\include /I..\..\..\libpq /I..\..\..\..\include /link /defaultlib:$(OUTDIR)libecpg\libecpg.lib /defaultlib:$(OUTDIR)libecpg_compat\libecpg_compat.lib /defaultlib:$(OUTDIR)libpgtypes\libpgtypes.lib" />
+  <Exec WorkingDirectory="%(Pgc.RelativeDir)" Command="cl /nologo %(Pgc.FileName).c /TC /MD$(DEBUGLIB) /DWIN32 /I. /I..\..\include /I..\..\..\libpq /I..\..\..\..\include /link /defaultlib:$(OUTDIR)libecpg\libecpg.lib /defaultlib:$(OUTDIR)libecpg_compat\libecpg_compat.lib /defaultlib:$(OUTDIR)libpgtypes\libpgtypes.lib" />
  </Target>
 
  <!-- Clean up all output files -->
-- 
2.39.2

From ca74df4ff11ce0fd1e51786eccaeca810921fc6d Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email-masked]>
Date: Sat, 10 Jun 2023 09:14:07 +1200
Subject: [PATCH 4/5] Add port/pg_threads.h for a common threading API.

Loosely based on C11's <threads.h>, but with pg_ prefixes, this will
allow us to clean up many places that have to cope with POSIX and
Windows threads.
---
 src/include/port/pg_threads.h    | 252 +++++++++++++++++++++++++++++++
 src/port/Makefile                |   1 +
 src/port/meson.build             |   1 +
 src/port/pg_threads.c            | 117 ++++++++++++++
 src/tools/pgindent/typedefs.list |   7 +
 5 files changed, 378 insertions(+)
 create mode 100644 src/include/port/pg_threads.h
 create mode 100644 src/port/pg_threads.c

diff --git a/src/include/port/pg_threads.h b/src/include/port/pg_threads.h
new file mode 100644
index 0000000000..1706709994
--- /dev/null
+++ b/src/include/port/pg_threads.h
@@ -0,0 +1,252 @@
+/*
+ * A multi-threading API abstraction loosely based on the C11 standard's
+ * <threads.h> header.  The identifiers have a pg_ prefix.  Perhaps one day
+ * we'll use standard C threads directly, and we'll drop the prefixes.
+ *
+ * Exceptions:
+ *  - pg_thrd_barrier_t is not based on C11
+ */
+
+#ifndef PG_THREADS_H
+#define PG_THREADS_H
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <processthreadsapi.h>
+#include <fibersapi.h>
+#include <synchapi.h>
+#else
+#include <errno.h>
+#include "port/pg_pthread.h"
+#endif
+
+#include <stdint.h>
+
+#ifdef WIN32
+typedef HANDLE pg_thrd_t;
+typedef CRITICAL_SECTION pg_mtx_t;
+typedef CONDITION_VARIABLE pg_cnd_t;
+typedef SYNCHRONIZATION_BARRIER pg_thrd_barrier_t;
+typedef DWORD pg_tss_t;
+typedef INIT_ONCE pg_once_flag;
+#define PG_ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT
+#else
+typedef pthread_t pg_thrd_t;
+typedef pthread_mutex_t pg_mtx_t;
+typedef pthread_cond_t pg_cnd_t;
+typedef pthread_barrier_t pg_thrd_barrier_t;
+typedef pthread_key_t pg_tss_t;
+typedef pthread_once_t pg_once_flag;
+#define PG_ONCE_FLAG_INIT PTHREAD_ONCE_INIT
+#endif
+
+typedef int (*pg_thrd_start_t) (void *);
+typedef void (*pg_tss_dtor_t) (void *);
+typedef void (*pg_call_once_function_t) (void);
+
+enum
+{
+	pg_thrd_success = 0,
+	pg_thrd_nomem = 1,
+	pg_thrd_timedout = 2,
+	pg_thrd_busy = 3,
+	pg_thrd_error = 4
+};
+
+enum
+{
+	pg_mtx_plain = 0
+};
+
+extern int	pg_thrd_create(pg_thrd_t *thread, pg_thrd_start_t function, void *argument);
+extern int	pg_thrd_join(pg_thrd_t thread, int *result);
+extern void pg_thrd_exit(int result);
+
+#ifndef WIN32
+static inline int
+pg_thrd_maperror(int error)
+{
+	if (error == 0)
+		return pg_thrd_success;
+	if (error == ENOMEM)
+		return pg_thrd_nomem;
+	return pg_thrd_error;
+}
+#endif
+
+#ifdef WIN32
+BOOL		pg_call_once_trampoline(pg_once_flag *flag, void *parameter, void **context);
+#endif
+
+static inline void
+pg_call_once(pg_once_flag *flag, pg_call_once_function_t function)
+{
+#ifdef WIN32
+	InitOnceExecuteOnce(flag, pg_call_once_trampoline, (void *) function, NULL);
+#else
+	pthread_once(flag, function);
+#endif
+}
+
+static inline int
+pg_thrd_equal(pg_thrd_t lhs, pg_thrd_t rhs)
+{
+#ifdef WIN32
+	return lhs == rhs;
+#else
+	return pthread_equal(lhs, rhs);
+#endif
+}
+
+static inline int
+pg_tss_create(pg_tss_t *key, pg_tss_dtor_t destructor)
+{
+#ifdef WIN32
+	//*key = FlsAlloc(destructor);
+	*key = FlsAlloc(NULL);
+	return pg_thrd_success;
+#else
+	return pg_thrd_maperror(pthread_key_create(key, destructor));
+#endif
+}
+
+static inline void *
+pg_tss_get(pg_tss_t key)
+{
+#ifdef WIN32
+	return FlsGetValue(key);
+#else
+	return pthread_getspecific(key);
+#endif
+}
+
+static inline int
+pg_tss_set(pg_tss_t key, void *value)
+{
+#ifdef WIN32
+	return FlsSetValue(key, value) ? pg_thrd_success : pg_thrd_error;
+#else
+	return pg_thrd_maperror(pthread_setspecific(key, value));
+#endif
+}
+
+static inline int
+pg_mtx_init(pg_mtx_t *mutex, int type)
+{
+#ifdef WIN32
+	InitializeCriticalSection(mutex);
+	return pg_thrd_success;
+#else
+	return pg_thrd_maperror(pthread_mutex_init(mutex, NULL));
+#endif
+}
+
+static inline int
+pg_mtx_lock(pg_mtx_t *mutex)
+{
+#ifdef WIN32
+	EnterCriticalSection(mutex);
+	return pg_thrd_success;
+#else
+	return pg_thrd_maperror(pthread_mutex_lock(mutex));
+#endif
+}
+
+static inline int
+pg_mtx_unlock(pg_mtx_t *mutex)
+{
+#ifdef WIN32
+	LeaveCriticalSection(mutex);
+	return pg_thrd_success;
+#else
+	return pg_thrd_maperror(pthread_mutex_unlock(mutex));
+#endif
+}
+
+static inline int
+pg_mtx_destroy(pg_mtx_t *mutex)
+{
+#ifdef WIN32
+	DeleteCriticalSection(mutex);
+	return pg_thrd_success;
+#else
+	return pg_thrd_maperror(pthread_mutex_destroy(mutex));
+#endif
+}
+
+static inline int
+pg_cnd_init(pg_cnd_t *condvar)
+{
+#ifdef WIN32
+	InitializeConditionVariable(condvar);
+	return pg_thrd_success;
+#else
+	return pg_thrd_maperror(pthread_cond_init(condvar, NULL));
+#endif
+}
+
+static inline int
+pg_cnd_broadcast(pg_cnd_t *condvar)
+{
+#ifdef WIN32
+	WakeAllConditionVariable(condvar);
+	return pg_thrd_success;
+#else
+	return pg_thrd_maperror(pthread_cond_broadcast(condvar));
+#endif
+}
+
+static inline int
+pg_cnd_wait(pg_cnd_t *condvar, pg_mtx_t *mutex)
+{
+#ifdef WIN32
+	SleepConditionVariableCS(condvar, mutex, INFINITE);
+	return pg_thrd_success;
+#else
+	return pg_thrd_maperror(pthread_cond_wait(condvar, mutex));
+#endif
+}
+
+static inline int
+pg_cnd_destroy(pg_cnd_t *condvar)
+{
+#ifdef WIN32
+	return pg_thrd_success;
+#else
+	return pg_thrd_maperror(pthread_cond_destroy(condvar));
+#endif
+}
+
+static inline int
+pg_thrd_barrier_init(pg_thrd_barrier_t *barrier, int count)
+{
+#ifdef WIN32
+	return InitializeSynchronizationBarrier(barrier, count, 0) ? pg_thrd_success : pg_thrd_error;
+#else
+	return pg_thrd_maperror(pthread_barrier_init(barrier, NULL, count));
+#endif
+}
+
+static inline int
+pg_thrd_barrier_wait(pg_thrd_barrier_t *barrier)
+{
+#ifdef WIN32
+	EnterSynchronizationBarrier(barrier, SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY);
+	return pg_thrd_success;
+#else
+	return pg_thrd_maperror(pthread_barrier_wait(barrier));
+#endif
+}
+
+static inline int
+pg_thrd_barrier_destroy(pg_thrd_barrier_t *barrier)
+{
+#ifdef WIN32
+	return pg_thrd_success;
+#else
+	return pg_thrd_maperror(pthread_barrier_destroy(barrier));
+#endif
+}
+
+#endif
diff --git a/src/port/Makefile b/src/port/Makefile
index f205c2c9c5..6e9ec3b35f 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -47,6 +47,7 @@ OBJS = \
 	path.o \
 	pg_bitutils.o \
 	pg_strong_random.o \
+	pg_threads.o \
 	pgcheckdir.o \
 	pgmkdirp.o \
 	pgsleep.o \
diff --git a/src/port/meson.build b/src/port/meson.build
index 9d0cd93c43..3d2277d151 100644
--- a/src/port/meson.build
+++ b/src/port/meson.build
@@ -8,6 +8,7 @@ pgport_sources = [
   'path.c',
   'pg_bitutils.c',
   'pg_strong_random.c',
+  'pg_threads.c',
   'pgcheckdir.c',
   'pgmkdirp.c',
   'pgsleep.c',
diff --git a/src/port/pg_threads.c b/src/port/pg_threads.c
new file mode 100644
index 0000000000..ded62b669d
--- /dev/null
+++ b/src/port/pg_threads.c
@@ -0,0 +1,117 @@
+#include "c.h"
+#include "port/pg_threads.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+/*
+ * There are small differences between the function types in C11, POSIX (return
+ * type) and Windows (return type signedness, calling convention).  The
+ * int return value will survive casting to/from void * and DWORD respectively,
+ * but we still need a small trampoline function to deal with the different
+ * function pointer type.
+ */
+typedef struct pg_thrd_thunk
+{
+	pg_thrd_start_t function;
+	void	   *argument;
+} pg_thrd_thunk;
+
+#ifdef WIN32
+BOOL
+pg_call_once_trampoline(pg_once_flag *flag, void *parameter, void **context)
+{
+	pg_call_once_function_t function = (pg_call_once_function_t) parameter;
+
+	function();
+	return TRUE;
+}
+#endif
+
+#ifdef WIN32
+static DWORD __stdcall
+pg_thrd_trampoline(void *vthunk)
+#else
+static void *
+pg_thrd_trampoline(void *vthunk)
+#endif
+{
+	pg_thrd_thunk *thunk = (pg_thrd_thunk *) vthunk;
+	void	   *argument = thunk->argument;
+	pg_thrd_start_t function = thunk->function;
+	int			result;
+
+	free(vthunk);
+
+	result = function(argument);
+
+#ifdef WIN32
+	return (DWORD) result;
+#else
+	return (void *) (intptr_t) result;
+#endif
+}
+
+int
+pg_thrd_create(pg_thrd_t *thread, pg_thrd_start_t function, void *argument)
+{
+	pg_thrd_thunk *thunk;
+
+	thunk = malloc(sizeof(*thunk));
+	if (thunk == NULL)
+		return pg_thrd_nomem;
+	thunk->function = function;
+	thunk->argument = argument;
+
+#ifdef WIN32
+	*thread = CreateThread(NULL, 0, pg_thrd_trampoline, thunk, 0, 0);
+	if (*thread != NULL)
+		return pg_thrd_success;
+#else
+	if (pthread_create(thread, NULL, pg_thrd_trampoline, thunk) == 0)
+		return pg_thrd_success;
+#endif
+
+	free(thunk);
+	return pg_thrd_error;
+}
+
+int
+pg_thrd_join(pg_thrd_t thread, int *result)
+{
+#ifdef WIN32
+	DWORD	   dword_result;
+
+	if (WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0)
+	{
+		if (result)
+		{
+			if (!GetExitCodeThread(thread, &dword_result))
+				return pg_thrd_error;
+			*result = (int) dword_result;
+		}
+		CloseHandle(thread);
+		return pg_thrd_success;
+	}
+#else
+	void	   *void_star_result;
+
+	if (pthread_join(thread, &void_star_result) == 0)
+	{
+		if (result)
+			*result = (int) (intptr_t) void_star_result;
+		return pg_thrd_success;
+	}
+#endif
+	return pg_thrd_error;
+}
+
+void
+pg_thrd_exit(int result)
+{
+#ifdef WIN32
+	ExitThread((DWORD) result);
+#else
+	pthread_exit((void *) (intptr_t) result);
+#endif
+}
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 260854747b..b4a22e678f 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3515,6 +3515,7 @@ pg_be_sasl_mech
 pg_checksum_context
 pg_checksum_raw_context
 pg_checksum_type
+pg_cnd_t
 pg_compress_algorithm
 pg_compress_specification
 pg_conn_host
@@ -3540,7 +3541,9 @@ pg_local_to_utf_combined
 pg_locale_t
 pg_mb_radix_tree
 pg_md5_ctx
+pg_mtx_t
 pg_on_exit_callback
+pg_once_flag
 pg_prng_state
 pg_re_flags
 pg_saslprep_rc
@@ -3551,8 +3554,12 @@ pg_sha384_ctx
 pg_sha512_ctx
 pg_snapshot
 pg_stack_base_t
+pg_thrd_t
+pg_thrd_thunk
+pg_thrd_barrier_t
 pg_time_t
 pg_time_usec_t
+pg_tss_t
 pg_tz
 pg_tz_cache
 pg_tzenum
-- 
2.39.2

From 2e1281762b56bb6e62f71124611505e5e29e5627 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email-masked]>
Date: Fri, 9 Jun 2023 13:27:42 +1200
Subject: [PATCH 5/5] Replace various ad hoc abstractions with pg_threads.h.

Several places had their own macros and functions to abstract POSIX and
Windows threading APIs.  Use the new centralized API instead.
---
 src/bin/pgbench/pgbench.c                     | 67 +++----------
 src/interfaces/ecpg/ecpglib/connect.c         | 43 ++++----
 src/interfaces/ecpg/ecpglib/descriptor.c      | 14 +--
 src/interfaces/ecpg/ecpglib/memory.c          | 14 +--
 src/interfaces/ecpg/ecpglib/misc.c            | 68 ++++---------
 src/interfaces/ecpg/ecpglib/sqlda.c           |  1 -
 .../ecpg/include/ecpg-pthread-win32.h         | 55 -----------
 src/interfaces/ecpg/include/ecpg_config.h.in  |  3 +
 src/interfaces/ecpg/include/meson.build       |  1 +
 .../ecpg/test/expected/thread-alloc.c         | 73 +++++---------
 .../ecpg/test/expected/thread-descriptor.c    | 52 +++-------
 .../ecpg/test/expected/thread-prep.c          | 99 +++++++------------
 .../ecpg/test/expected/thread-thread.c        | 92 +++++++----------
 .../test/expected/thread-thread_implicit.c    | 92 +++++++----------
 src/interfaces/ecpg/test/thread/alloc.pgc     | 39 +-------
 .../ecpg/test/thread/descriptor.pgc           | 38 +------
 src/interfaces/ecpg/test/thread/prep.pgc      | 37 +------
 src/interfaces/ecpg/test/thread/thread.pgc    | 38 ++-----
 .../ecpg/test/thread/thread_implicit.pgc      | 38 ++-----
 src/interfaces/libpq/Makefile                 |  1 -
 src/interfaces/libpq/fe-connect.c             | 39 +++-----
 src/interfaces/libpq/fe-secure-openssl.c      | 63 +++++-------
 src/interfaces/libpq/fe-secure.c              |  4 +-
 src/interfaces/libpq/libpq-int.h              |  5 -
 src/interfaces/libpq/meson.build              |  4 +-
 src/interfaces/libpq/pthread-win32.c          | 60 -----------
 src/port/pthread-win32.h                      | 22 -----
 src/tools/pginclude/cpluspluscheck            |  1 -
 src/tools/pginclude/headerscheck              |  1 -
 29 files changed, 285 insertions(+), 779 deletions(-)
 delete mode 100644 src/interfaces/ecpg/include/ecpg-pthread-win32.h
 delete mode 100644 src/interfaces/libpq/pthread-win32.c
 delete mode 100644 src/port/pthread-win32.h

diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 683fc5860f..57e3b5fb46 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -66,6 +66,7 @@
 #include "libpq-fe.h"
 #include "pgbench.h"
 #include "port/pg_bitutils.h"
+#include "port/pg_threads.h"
 #include "portability/instr_time.h"
 
 /* X/Open (XSI) requires <math.h> to provide M_PI, but core POSIX does not */
@@ -113,49 +114,6 @@ typedef struct socket_set
 
 #endif							/* POLL_USING_SELECT */
 
-/*
- * Multi-platform thread implementations
- */
-
-#ifdef WIN32
-/* Use Windows threads */
-#include <windows.h>
-#define GETERRNO() (_dosmaperr(GetLastError()), errno)
-#define THREAD_T HANDLE
-#define THREAD_FUNC_RETURN_TYPE unsigned
-#define THREAD_FUNC_RETURN return 0
-#define THREAD_FUNC_CC __stdcall
-#define THREAD_CREATE(handle, function, arg) \
-	((*(handle) = (HANDLE) _beginthreadex(NULL, 0, (function), (arg), 0, NULL)) == 0 ? errno : 0)
-#define THREAD_JOIN(handle) \
-	(WaitForSingleObject(handle, INFINITE) != WAIT_OBJECT_0 ? \
-	GETERRNO() : CloseHandle(handle) ? 0 : GETERRNO())
-#define THREAD_BARRIER_T SYNCHRONIZATION_BARRIER
-#define THREAD_BARRIER_INIT(barrier, n) \
-	(InitializeSynchronizationBarrier((barrier), (n), 0) ? 0 : GETERRNO())
-#define THREAD_BARRIER_WAIT(barrier) \
-	EnterSynchronizationBarrier((barrier), \
-								SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY)
-#define THREAD_BARRIER_DESTROY(barrier)
-#else
-/* Use POSIX threads */
-#include "port/pg_pthread.h"
-#define THREAD_T pthread_t
-#define THREAD_FUNC_RETURN_TYPE void *
-#define THREAD_FUNC_RETURN return NULL
-#define THREAD_FUNC_CC
-#define THREAD_CREATE(handle, function, arg) \
-	pthread_create((handle), NULL, (function), (arg))
-#define THREAD_JOIN(handle) \
-	pthread_join((handle), NULL)
-#define THREAD_BARRIER_T pthread_barrier_t
-#define THREAD_BARRIER_INIT(barrier, n) \
-	pthread_barrier_init((barrier), NULL, (n))
-#define THREAD_BARRIER_WAIT(barrier) pthread_barrier_wait((barrier))
-#define THREAD_BARRIER_DESTROY(barrier) pthread_barrier_destroy((barrier))
-#endif
-
-
 /********************************************************************
  * some configurable parameters */
 
@@ -477,7 +435,7 @@ typedef enum TStatus
 static pg_prng_state base_random_sequence;
 
 /* Synchronization barrier for start and connection */
-static THREAD_BARRIER_T barrier;
+static pg_thrd_barrier_t barrier;
 
 /*
  * Connection state machine states.
@@ -644,7 +602,7 @@ typedef struct
 typedef struct
 {
 	int			tid;			/* thread id */
-	THREAD_T	thread;			/* thread handle */
+	pg_thrd_t	thread;			/* thread handle */
 	CState	   *state;			/* array of CState */
 	int			nstate;			/* length of state[] */
 
@@ -825,7 +783,7 @@ static void doLog(TState *thread, CState *st,
 static void processXactStats(TState *thread, CState *st, pg_time_usec_t *now,
 							 bool skipped, StatsData *agg);
 static void addScript(const ParsedScript *script);
-static THREAD_FUNC_RETURN_TYPE THREAD_FUNC_CC threadRun(void *arg);
+static int threadRun(void *arg);
 static void finishCon(CState *st);
 static void setalarm(int seconds);
 static socket_set *alloc_socket_set(int count);
@@ -7218,7 +7176,7 @@ main(int argc, char **argv)
 	if (duration > 0)
 		setalarm(duration);
 
-	errno = THREAD_BARRIER_INIT(&barrier, nthreads);
+	errno = pg_thrd_barrier_init(&barrier, nthreads);
 	if (errno != 0)
 		pg_fatal("could not initialize barrier: %m");
 
@@ -7228,7 +7186,7 @@ main(int argc, char **argv)
 		TState	   *thread = &threads[i];
 
 		thread->create_time = pg_time_now();
-		errno = THREAD_CREATE(&thread->thread, threadRun, thread);
+		errno = pg_thrd_create(&thread->thread, threadRun, thread);
 
 		if (errno != 0)
 			pg_fatal("could not create thread: %m");
@@ -7251,7 +7209,7 @@ main(int argc, char **argv)
 		TState	   *thread = &threads[i];
 
 		if (i > 0)
-			THREAD_JOIN(thread->thread);
+			pg_thrd_join(thread->thread, NULL);
 
 		for (int j = 0; j < thread->nstate; j++)
 			if (thread->state[j].state != CSTATE_FINISHED)
@@ -7291,7 +7249,7 @@ main(int argc, char **argv)
 	printResults(&stats, pg_time_now() - bench_start, conn_total_duration,
 				 bench_start - start_time, latency_late);
 
-	THREAD_BARRIER_DESTROY(&barrier);
+	pg_thrd_barrier_destroy(&barrier);
 
 	if (exit_code != 0)
 		pg_log_error("Run was aborted; the above results are incomplete.");
@@ -7299,7 +7257,7 @@ main(int argc, char **argv)
 	return exit_code;
 }
 
-static THREAD_FUNC_RETURN_TYPE THREAD_FUNC_CC
+static int
 threadRun(void *arg)
 {
 	TState	   *thread = (TState *) arg;
@@ -7336,7 +7294,7 @@ threadRun(void *arg)
 		state[i].state = CSTATE_CHOOSE_SCRIPT;
 
 	/* READY */
-	THREAD_BARRIER_WAIT(&barrier);
+	pg_thrd_barrier_wait(&barrier);
 
 	thread_start = pg_time_now();
 	thread->started_time = thread_start;
@@ -7360,7 +7318,7 @@ threadRun(void *arg)
 	}
 
 	/* GO */
-	THREAD_BARRIER_WAIT(&barrier);
+	pg_thrd_barrier_wait(&barrier);
 
 	start = pg_time_now();
 	thread->bench_start = start;
@@ -7572,7 +7530,8 @@ done:
 		thread->logfile = NULL;
 	}
 	free_socket_set(sockets);
-	THREAD_FUNC_RETURN;
+
+	return 0;
 }
 
 static void
diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c
index 8afb1f0a26..61ca9a0829 100644
--- a/src/interfaces/ecpg/ecpglib/connect.c
+++ b/src/interfaces/ecpg/ecpglib/connect.c
@@ -2,8 +2,8 @@
 
 #define POSTGRES_ECPG_INTERNAL
 #include "postgres_fe.h"
+#include "port/pg_threads.h"
 
-#include "ecpg-pthread-win32.h"
 #include "ecpgerrno.h"
 #include "ecpglib.h"
 #include "ecpglib_extern.h"
@@ -14,22 +14,23 @@
 locale_t	ecpg_clocale = (locale_t) 0;
 #endif
 
-static pthread_mutex_t connections_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_key_t actual_connection_key;
-static pthread_once_t actual_connection_key_once = PTHREAD_ONCE_INIT;
+static pg_mtx_t connections_mutex;
+static pg_tss_t actual_connection_key;
+static pg_once_flag actual_connection_key_once = PG_ONCE_FLAG_INIT;
 static struct connection *actual_connection = NULL;
 static struct connection *all_connections = NULL;
 
 static void
 ecpg_actual_connection_init(void)
 {
-	pthread_key_create(&actual_connection_key, NULL);
+	pg_mtx_init(&connections_mutex, pg_mtx_plain);
+	pg_tss_create(&actual_connection_key, NULL);
 }
 
 void
 ecpg_pthreads_init(void)
 {
-	pthread_once(&actual_connection_key_once, ecpg_actual_connection_init);
+	pg_call_once(&actual_connection_key_once, ecpg_actual_connection_init);
 }
 
 static struct connection *
@@ -41,7 +42,7 @@ ecpg_get_connection_nr(const char *connection_name)
 	{
 		ecpg_pthreads_init();	/* ensure actual_connection_key is valid */
 
-		ret = pthread_getspecific(actual_connection_key);
+		ret = pg_tss_get(actual_connection_key);
 
 		/*
 		 * if no connection in TSD for this thread, get the global default
@@ -76,7 +77,7 @@ ecpg_get_connection(const char *connection_name)
 	{
 		ecpg_pthreads_init();	/* ensure actual_connection_key is valid */
 
-		ret = pthread_getspecific(actual_connection_key);
+		ret = pg_tss_get(actual_connection_key);
 
 		/*
 		 * if no connection in TSD for this thread, get the global default
@@ -89,11 +90,11 @@ ecpg_get_connection(const char *connection_name)
 	}
 	else
 	{
-		pthread_mutex_lock(&connections_mutex);
+		pg_mtx_lock(&connections_mutex);
 
 		ret = ecpg_get_connection_nr(connection_name);
 
-		pthread_mutex_unlock(&connections_mutex);
+		pg_mtx_unlock(&connections_mutex);
 	}
 
 	return ret;
@@ -127,8 +128,8 @@ ecpg_finish(struct connection *act)
 				con->next = act->next;
 		}
 
-		if (pthread_getspecific(actual_connection_key) == act)
-			pthread_setspecific(actual_connection_key, all_connections);
+		if (pg_tss_get(actual_connection_key) == act)
+			pg_tss_set(actual_connection_key, all_connections);
 		if (actual_connection == act)
 			actual_connection = all_connections;
 
@@ -194,7 +195,7 @@ ECPGsetconn(int lineno, const char *connection_name)
 	if (!ecpg_init(con, connection_name, lineno))
 		return false;
 
-	pthread_setspecific(actual_connection_key, con);
+	pg_tss_set(actual_connection_key, con);
 	return true;
 }
 
@@ -481,7 +482,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 	}
 
 	/* add connection to our list */
-	pthread_mutex_lock(&connections_mutex);
+	pg_mtx_lock(&connections_mutex);
 
 	/*
 	 * ... but first, make certain we have created ecpg_clocale.  Rely on
@@ -493,7 +494,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 		ecpg_clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
 		if (!ecpg_clocale)
 		{
-			pthread_mutex_unlock(&connections_mutex);
+			pg_mtx_unlock(&connections_mutex);
 			ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
 					   ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
 			if (host)
@@ -530,7 +531,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 		this->next = all_connections;
 
 	all_connections = this;
-	pthread_setspecific(actual_connection_key, all_connections);
+	pg_tss_set(actual_connection_key, all_connections);
 	actual_connection = all_connections;
 
 	ecpg_log("ECPGconnect: opening database %s on %s port %s %s%s %s%s\n",
@@ -648,7 +649,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 		ecpg_log("ECPGconnect: %s", errmsg);
 
 		ecpg_finish(this);
-		pthread_mutex_unlock(&connections_mutex);
+		pg_mtx_unlock(&connections_mutex);
 
 		ecpg_raise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, db);
 		if (realname)
@@ -660,7 +661,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 	if (realname)
 		ecpg_free(realname);
 
-	pthread_mutex_unlock(&connections_mutex);
+	pg_mtx_unlock(&connections_mutex);
 
 	this->autocommit = autocommit;
 
@@ -682,7 +683,7 @@ ECPGdisconnect(int lineno, const char *connection_name)
 		return false;
 	}
 
-	pthread_mutex_lock(&connections_mutex);
+	pg_mtx_lock(&connections_mutex);
 
 	if (strcmp(connection_name, "ALL") == 0)
 	{
@@ -701,14 +702,14 @@ ECPGdisconnect(int lineno, const char *connection_name)
 
 		if (!ecpg_init(con, connection_name, lineno))
 		{
-			pthread_mutex_unlock(&connections_mutex);
+			pg_mtx_unlock(&connections_mutex);
 			return false;
 		}
 		else
 			ecpg_finish(con);
 	}
 
-	pthread_mutex_unlock(&connections_mutex);
+	pg_mtx_unlock(&connections_mutex);
 
 	return true;
 }
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
index ad279e245c..b57e065672 100644
--- a/src/interfaces/ecpg/ecpglib/descriptor.c
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -7,11 +7,11 @@
 #include "postgres_fe.h"
 
 #include "catalog/pg_type_d.h"
-#include "ecpg-pthread-win32.h"
 #include "ecpgerrno.h"
 #include "ecpglib.h"
 #include "ecpglib_extern.h"
 #include "ecpgtype.h"
+#include "port/pg_threads.h"
 #include "sql3types.h"
 #include "sqlca.h"
 #include "sqlda.h"
@@ -19,8 +19,8 @@
 static void descriptor_free(struct descriptor *desc);
 
 /* We manage descriptors separately for each thread. */
-static pthread_key_t descriptor_key;
-static pthread_once_t descriptor_once = PTHREAD_ONCE_INIT;
+static pg_tss_t descriptor_key;
+static pg_once_flag descriptor_once = PG_ONCE_FLAG_INIT;
 
 static void descriptor_deallocate_all(struct descriptor *list);
 
@@ -33,20 +33,20 @@ descriptor_destructor(void *arg)
 static void
 descriptor_key_init(void)
 {
-	pthread_key_create(&descriptor_key, descriptor_destructor);
+	pg_tss_create(&descriptor_key, descriptor_destructor);
 }
 
 static struct descriptor *
 get_descriptors(void)
 {
-	pthread_once(&descriptor_once, descriptor_key_init);
-	return (struct descriptor *) pthread_getspecific(descriptor_key);
+	pg_call_once(&descriptor_once, descriptor_key_init);
+	return (struct descriptor *) pg_tss_get(descriptor_key);
 }
 
 static void
 set_descriptors(struct descriptor *value)
 {
-	pthread_setspecific(descriptor_key, value);
+	pg_tss_set(descriptor_key, value);
 }
 
 /* old internal convenience function that might go away later */
diff --git a/src/interfaces/ecpg/ecpglib/memory.c b/src/interfaces/ecpg/ecpglib/memory.c
index a83637ac75..985f05087d 100644
--- a/src/interfaces/ecpg/ecpglib/memory.c
+++ b/src/interfaces/ecpg/ecpglib/memory.c
@@ -3,11 +3,11 @@
 #define POSTGRES_ECPG_INTERNAL
 #include "postgres_fe.h"
 
-#include "ecpg-pthread-win32.h"
 #include "ecpgerrno.h"
 #include "ecpglib.h"
 #include "ecpglib_extern.h"
 #include "ecpgtype.h"
+#include "port/pg_threads.h"
 
 void
 ecpg_free(void *ptr)
@@ -68,8 +68,8 @@ struct auto_mem
 	struct auto_mem *next;
 };
 
-static pthread_key_t auto_mem_key;
-static pthread_once_t auto_mem_once = PTHREAD_ONCE_INIT;
+static pg_tss_t auto_mem_key;
+static pg_once_flag auto_mem_once = PG_ONCE_FLAG_INIT;
 
 static void
 auto_mem_destructor(void *arg)
@@ -81,20 +81,20 @@ auto_mem_destructor(void *arg)
 static void
 auto_mem_key_init(void)
 {
-	pthread_key_create(&auto_mem_key, auto_mem_destructor);
+	pg_tss_create(&auto_mem_key, auto_mem_destructor);
 }
 
 static struct auto_mem *
 get_auto_allocs(void)
 {
-	pthread_once(&auto_mem_once, auto_mem_key_init);
-	return (struct auto_mem *) pthread_getspecific(auto_mem_key);
+	pg_call_once(&auto_mem_once, auto_mem_key_init);
+	return (struct auto_mem *) pg_tss_get(auto_mem_key);
 }
 
 static void
 set_auto_allocs(struct auto_mem *am)
 {
-	pthread_setspecific(auto_mem_key, am);
+	pg_tss_set(auto_mem_key, am);
 }
 
 char *
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
index 2b78caeaf5..b3d997e749 100644
--- a/src/interfaces/ecpg/ecpglib/misc.c
+++ b/src/interfaces/ecpg/ecpglib/misc.c
@@ -6,7 +6,6 @@
 #include <limits.h>
 #include <unistd.h>
 
-#include "ecpg-pthread-win32.h"
 #include "ecpgerrno.h"
 #include "ecpglib.h"
 #include "ecpglib_extern.h"
@@ -16,6 +15,7 @@
 #include "pgtypes_interval.h"
 #include "pgtypes_numeric.h"
 #include "pgtypes_timestamp.h"
+#include "port/pg_threads.h"
 #include "sqlca.h"
 
 #ifndef LONG_LONG_MIN
@@ -55,11 +55,10 @@ static struct sqlca_t sqlca_init =
 	}
 };
 
-static pthread_key_t sqlca_key;
-static pthread_once_t sqlca_key_once = PTHREAD_ONCE_INIT;
-
-static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t debug_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pg_tss_t sqlca_key;
+static pg_once_flag ecpg_once = PG_ONCE_FLAG_INIT;
+static pg_mtx_t debug_mutex;
+static pg_mtx_t debug_init_mutex;
 static int	simple_debug = 0;
 static FILE *debugstream = NULL;
 
@@ -99,9 +98,11 @@ ecpg_sqlca_key_destructor(void *arg)
 }
 
 static void
-ecpg_sqlca_key_init(void)
+ecpg_init_once(void)
 {
-	pthread_key_create(&sqlca_key, ecpg_sqlca_key_destructor);
+	pg_tss_create(&sqlca_key, ecpg_sqlca_key_destructor);
+	pg_mtx_init(&debug_mutex, pg_mtx_plain);
+	pg_mtx_init(&debug_init_mutex, pg_mtx_plain);
 }
 
 struct sqlca_t *
@@ -109,16 +110,16 @@ ECPGget_sqlca(void)
 {
 	struct sqlca_t *sqlca;
 
-	pthread_once(&sqlca_key_once, ecpg_sqlca_key_init);
+	pg_call_once(&ecpg_once, ecpg_init_once);
 
-	sqlca = pthread_getspecific(sqlca_key);
+	sqlca = pg_tss_get(sqlca_key);
 	if (sqlca == NULL)
 	{
 		sqlca = malloc(sizeof(struct sqlca_t));
 		if (sqlca == NULL)
 			return NULL;
 		ecpg_init_sqlca(sqlca);
-		pthread_setspecific(sqlca_key, sqlca);
+		pg_tss_set(sqlca_key, sqlca);
 	}
 	return sqlca;
 }
@@ -203,7 +204,9 @@ ECPGtrans(int lineno, const char *connection_name, const char *transaction)
 void
 ECPGdebug(int n, FILE *dbgs)
 {
-	pthread_mutex_lock(&debug_init_mutex);
+	pg_call_once(&ecpg_once, ecpg_init_once);
+
+	pg_mtx_lock(&debug_init_mutex);
 
 	if (n > 100)
 	{
@@ -217,7 +220,7 @@ ECPGdebug(int n, FILE *dbgs)
 
 	ecpg_log("ECPGdebug: set to %d\n", simple_debug);
 
-	pthread_mutex_unlock(&debug_init_mutex);
+	pg_mtx_unlock(&debug_init_mutex);
 }
 
 void
@@ -249,7 +252,9 @@ ecpg_log(const char *format,...)
 	else
 		snprintf(fmt, bufsize, "[%d]: %s", (int) getpid(), intl_format);
 
-	pthread_mutex_lock(&debug_mutex);
+	pg_call_once(&ecpg_once, ecpg_init_once);
+
+	pg_mtx_lock(&debug_mutex);
 
 	va_start(ap, format);
 	vfprintf(debugstream, fmt, ap);
@@ -264,7 +269,7 @@ ecpg_log(const char *format,...)
 
 	fflush(debugstream);
 
-	pthread_mutex_unlock(&debug_mutex);
+	pg_mtx_unlock(&debug_mutex);
 
 	free(fmt);
 }
@@ -405,39 +410,6 @@ ECPGis_noind_null(enum ECPGttype type, const void *ptr)
 	return false;
 }
 
-#ifdef WIN32
-
-void
-win32_pthread_mutex(volatile pthread_mutex_t *mutex)
-{
-	if (mutex->handle == NULL)
-	{
-		while (InterlockedExchange((LONG *) &mutex->initlock, 1) == 1)
-			Sleep(0);
-		if (mutex->handle == NULL)
-			mutex->handle = CreateMutex(NULL, FALSE, NULL);
-		InterlockedExchange((LONG *) &mutex->initlock, 0);
-	}
-}
-
-static pthread_mutex_t win32_pthread_once_lock = PTHREAD_MUTEX_INITIALIZER;
-
-void
-win32_pthread_once(volatile pthread_once_t *once, void (*fn) (void))
-{
-	if (!*once)
-	{
-		pthread_mutex_lock(&win32_pthread_once_lock);
-		if (!*once)
-		{
-			fn();
-			*once = true;
-		}
-		pthread_mutex_unlock(&win32_pthread_once_lock);
-	}
-}
-#endif							/* WIN32 */
-
 #ifdef ENABLE_NLS
 
 char *
diff --git a/src/interfaces/ecpg/ecpglib/sqlda.c b/src/interfaces/ecpg/ecpglib/sqlda.c
index 081e32666f..7231cc4a6f 100644
--- a/src/interfaces/ecpg/ecpglib/sqlda.c
+++ b/src/interfaces/ecpg/ecpglib/sqlda.c
@@ -11,7 +11,6 @@
 
 #include "catalog/pg_type_d.h"
 #include "decimal.h"
-#include "ecpg-pthread-win32.h"
 #include "ecpgerrno.h"
 #include "ecpglib.h"
 #include "ecpglib_extern.h"
diff --git a/src/interfaces/ecpg/include/ecpg-pthread-win32.h b/src/interfaces/ecpg/include/ecpg-pthread-win32.h
deleted file mode 100644
index 8252a17809..0000000000
--- a/src/interfaces/ecpg/include/ecpg-pthread-win32.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* src/interfaces/ecpg/include/ecpg-pthread-win32.h */
-/*
- * pthread mapping macros for win32 native thread implementation
- */
-#ifndef _ECPG_PTHREAD_WIN32_H
-#define _ECPG_PTHREAD_WIN32_H
-
-#ifndef WIN32
-
-#include <pthread.h>
-#else
-
-typedef struct pthread_mutex_t
-{
-	HANDLE		handle;
-	LONG		initlock;
-} pthread_mutex_t;
-
-typedef DWORD pthread_key_t;
-typedef bool pthread_once_t;
-
-#define PTHREAD_MUTEX_INITIALIZER	{ NULL, 0 }
-#define PTHREAD_ONCE_INIT			false
-
-void		win32_pthread_mutex(volatile pthread_mutex_t *mutex);
-void		win32_pthread_once(volatile pthread_once_t *once, void (*fn) (void));
-
-#define pthread_mutex_lock(mutex) \
-	do { \
-		if ((mutex)->handle == NULL) \
-			win32_pthread_mutex((mutex)); \
-		WaitForSingleObject((mutex)->handle, INFINITE); \
-	} while(0)
-
-#define pthread_mutex_unlock(mutex) \
-	ReleaseMutex((mutex)->handle)
-
-#define pthread_getspecific(key) \
-	TlsGetValue((key))
-
-#define pthread_setspecific(key, value) \
-	TlsSetValue((key), (value))
-
-/* FIXME: destructor is never called in Win32. */
-#define pthread_key_create(key, destructor) \
-	do { *(key) = TlsAlloc(); ((void)(destructor)); } while(0)
-
-#define pthread_once(once, fn) \
-	do { \
-		if (!*(once)) \
-			win32_pthread_once((once), (fn)); \
-	} while(0)
-#endif							/* WIN32 */
-
-#endif							/* _ECPG_PTHREAD_WIN32_H */
diff --git a/src/interfaces/ecpg/include/ecpg_config.h.in b/src/interfaces/ecpg/include/ecpg_config.h.in
index 6d01608a49..e693c9074f 100644
--- a/src/interfaces/ecpg/include/ecpg_config.h.in
+++ b/src/interfaces/ecpg/include/ecpg_config.h.in
@@ -10,5 +10,8 @@
 /* Define to 1 if `long long int' works and is 64 bits. */
 #undef HAVE_LONG_LONG_INT_64
 
+/* Define to 1 if you have the `pthread_barrier_wait' function. */
+#undef HAVE_PTHREAD_BARRIER_WAIT
+
 /* Define to 1 to use <stdbool.h> to define type bool. */
 #undef PG_USE_STDBOOL
diff --git a/src/interfaces/ecpg/include/meson.build b/src/interfaces/ecpg/include/meson.build
index 543e48fd6e..2e93efde98 100644
--- a/src/interfaces/ecpg/include/meson.build
+++ b/src/interfaces/ecpg/include/meson.build
@@ -6,6 +6,7 @@ ecpg_conf_keys = [
   'HAVE_INT64',
   'HAVE_LONG_INT_64',
   'HAVE_LONG_LONG_INT_64',
+  'HAVE_PTHREAD_BARRIER_WAIT',
   'PG_USE_STDBOOL',
 ]
 
diff --git a/src/interfaces/ecpg/test/expected/thread-alloc.c b/src/interfaces/ecpg/test/expected/thread-alloc.c
index 3b31d27fd3..97ed928ef1 100644
--- a/src/interfaces/ecpg/test/expected/thread-alloc.c
+++ b/src/interfaces/ecpg/test/expected/thread-alloc.c
@@ -10,16 +10,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include "ecpg_config.h"
-
-#ifdef WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <process.h>
-#include <locale.h>
-#else
-#include <pthread.h>
-#endif
-#include <stdio.h>
+#include "port/pg_threads.h"
 
 #define THREADS		16
 #define REPEATS		50
@@ -93,7 +84,7 @@ struct sqlca_t *ECPGget_sqlca(void);
 
 #endif
 
-#line 18 "alloc.pgc"
+#line 9 "alloc.pgc"

 #line 1 "regression.h"
@@ -103,21 +94,17 @@ struct sqlca_t *ECPGget_sqlca(void);

-#line 19 "alloc.pgc"
+#line 10 "alloc.pgc"

 /* exec sql whenever sqlerror  sqlprint ; */
-#line 21 "alloc.pgc"
+#line 12 "alloc.pgc"
 
 /* exec sql whenever not found  sqlprint ; */
-#line 22 "alloc.pgc"
+#line 13 "alloc.pgc"

-#ifdef WIN32
-static unsigned __stdcall fn(void* arg)
-#else
-static void* fn(void* arg)
-#endif
+static int fn(void* arg)
 {
 	int i;
 
@@ -126,54 +113,54 @@ static void* fn(void* arg)

-#line 33 "alloc.pgc"
+#line 20 "alloc.pgc"
  int value ;
  
-#line 34 "alloc.pgc"
+#line 21 "alloc.pgc"
  char name [ 100 ] ;
  
-#line 35 "alloc.pgc"
+#line 22 "alloc.pgc"
  char ** r = NULL ;
 /* exec sql end declare section */
-#line 36 "alloc.pgc"
+#line 23 "alloc.pgc"

 	value = (intptr_t) arg;
 	sprintf(name, "Connection: %d", value);
 
 	{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , name, 0); 
-#line 41 "alloc.pgc"
+#line 28 "alloc.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 41 "alloc.pgc"
+#line 28 "alloc.pgc"
 
 	{ ECPGsetcommit(__LINE__, "on", NULL);
-#line 42 "alloc.pgc"
+#line 29 "alloc.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 42 "alloc.pgc"
+#line 29 "alloc.pgc"
 
 	for (i = 1; i <= REPEATS; ++i)
 	{
 		{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select relname from pg_class where relname = 'pg_class'", ECPGt_EOIT, 
 	ECPGt_char,&(r),(long)0,(long)0,(1)*sizeof(char), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
-#line 45 "alloc.pgc"
+#line 32 "alloc.pgc"
 
 if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 45 "alloc.pgc"
+#line 32 "alloc.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 45 "alloc.pgc"
+#line 32 "alloc.pgc"
 
 		free(r);
 		r = NULL;
 	}
 	{ ECPGdisconnect(__LINE__, name);
-#line 49 "alloc.pgc"
+#line 36 "alloc.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 49 "alloc.pgc"
+#line 36 "alloc.pgc"

 	return 0;
@@ -182,28 +169,12 @@ if (sqlca.sqlcode < 0) sqlprint();}
 int main ()
 {
 	intptr_t i;
-#ifdef WIN32
-	HANDLE threads[THREADS];
-#else
-	pthread_t threads[THREADS];
-#endif
-
-#ifdef WIN32
-	for (i = 0; i < THREADS; ++i)
-	{
-		unsigned id;
-		threads[i] = (HANDLE)_beginthreadex(NULL, 0, fn, (void*)i, 0, &id);
-	}
+	pg_thrd_t threads[THREADS];
 
-	WaitForMultipleObjects(THREADS, threads, TRUE, INFINITE);
 	for (i = 0; i < THREADS; ++i)
-		CloseHandle(threads[i]);
-#else
+		pg_thrd_create(&threads[i], fn, (void *) i);
 	for (i = 0; i < THREADS; ++i)
-		pthread_create(&threads[i], NULL, fn, (void *) i);
-	for (i = 0; i < THREADS; ++i)
-		pthread_join(threads[i], NULL);
-#endif
+		pg_thrd_join(threads[i], NULL);
 
 	return 0;
 }
diff --git a/src/interfaces/ecpg/test/expected/thread-descriptor.c b/src/interfaces/ecpg/test/expected/thread-descriptor.c
index e34f4708d1..50cdbf1b2f 100644
--- a/src/interfaces/ecpg/test/expected/thread-descriptor.c
+++ b/src/interfaces/ecpg/test/expected/thread-descriptor.c
@@ -7,15 +7,7 @@
 #define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))
 
 #line 1 "descriptor.pgc"
-#ifdef WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <process.h>
-#include <locale.h>
-#else
-#include <pthread.h>
-#endif
-#include <stdio.h>
+#include "port/pg_threads.h"
 
 #define THREADS		16
 #define REPEATS		50000
@@ -89,36 +81,32 @@ struct sqlca_t *ECPGget_sqlca(void);
 
 #endif
 
-#line 14 "descriptor.pgc"
+#line 6 "descriptor.pgc"
 
 /* exec sql whenever sqlerror  sqlprint ; */
-#line 15 "descriptor.pgc"
+#line 7 "descriptor.pgc"
 
 /* exec sql whenever not found  sqlprint ; */
-#line 16 "descriptor.pgc"
+#line 8 "descriptor.pgc"

-#if defined(WIN32)
-static unsigned __stdcall fn(void* arg)
-#else
-static void* fn(void* arg)
-#endif
+static int fn(void* arg)
 {
 	int i;
 
 	for (i = 1; i <= REPEATS; ++i)
 	{
 		ECPGallocate_desc(__LINE__, "mydesc");
-#line 28 "descriptor.pgc"
+#line 16 "descriptor.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();
-#line 28 "descriptor.pgc"
+#line 16 "descriptor.pgc"
 
 		ECPGdeallocate_desc(__LINE__, "mydesc");
-#line 29 "descriptor.pgc"
+#line 17 "descriptor.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();
-#line 29 "descriptor.pgc"
+#line 17 "descriptor.pgc"
 
 	}
 
@@ -128,28 +116,12 @@ if (sqlca.sqlcode < 0) sqlprint();
 int main ()
 {
 	int i;
-#ifdef WIN32
-	HANDLE threads[THREADS];
-#else
-	pthread_t threads[THREADS];
-#endif
-
-#ifdef WIN32
-	for (i = 0; i < THREADS; ++i)
-	{
-		unsigned id;
-		threads[i] = (HANDLE)_beginthreadex(NULL, 0, fn, NULL, 0, &id);
-	}
+	pg_thrd_t threads[THREADS];
 
-	WaitForMultipleObjects(THREADS, threads, TRUE, INFINITE);
-	for (i = 0; i < THREADS; ++i)
-		CloseHandle(threads[i]);
-#else
 	for (i = 0; i < THREADS; ++i)
-		pthread_create(&threads[i], NULL, fn, NULL);
+		pg_thrd_create(&threads[i], fn, NULL);
 	for (i = 0; i < THREADS; ++i)
-		pthread_join(threads[i], NULL);
-#endif
+		pg_thrd_join(threads[i], NULL);
 
 	return 0;
 }
diff --git a/src/interfaces/ecpg/test/expected/thread-prep.c b/src/interfaces/ecpg/test/expected/thread-prep.c
index 052e27b634..c5b60d18cb 100644
--- a/src/interfaces/ecpg/test/expected/thread-prep.c
+++ b/src/interfaces/ecpg/test/expected/thread-prep.c
@@ -10,15 +10,8 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include "ecpg_config.h"
+#include "port/pg_threads.h"
 
-#ifdef WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <process.h>
-#include <locale.h>
-#else
-#include <pthread.h>
-#endif
 #include <stdio.h>
 
 #define THREADS		16
@@ -93,7 +86,7 @@ struct sqlca_t *ECPGget_sqlca(void);
 
 #endif
 
-#line 18 "prep.pgc"
+#line 11 "prep.pgc"

 #line 1 "regression.h"
@@ -103,21 +96,17 @@ struct sqlca_t *ECPGget_sqlca(void);

-#line 19 "prep.pgc"
+#line 12 "prep.pgc"

 /* exec sql whenever sqlerror  sqlprint ; */
-#line 21 "prep.pgc"
+#line 14 "prep.pgc"
 
 /* exec sql whenever not found  sqlprint ; */
-#line 22 "prep.pgc"
+#line 15 "prep.pgc"

-#ifdef WIN32
-static unsigned __stdcall fn(void* arg)
-#else
-static void* fn(void* arg)
-#endif
+static int fn(void* arg)
 {
 	int i;
 
@@ -126,64 +115,64 @@ static void* fn(void* arg)

-#line 33 "prep.pgc"
+#line 22 "prep.pgc"
  int value ;
  
-#line 34 "prep.pgc"
+#line 23 "prep.pgc"
  char name [ 100 ] ;
  
-#line 35 "prep.pgc"
+#line 24 "prep.pgc"
  char query [ 256 ] = "INSERT INTO T VALUES ( ? )" ;
 /* exec sql end declare section */
-#line 36 "prep.pgc"
+#line 25 "prep.pgc"

 	value = (intptr_t) arg;
 	sprintf(name, "Connection: %d", value);
 
 	{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , name, 0); 
-#line 41 "prep.pgc"
+#line 30 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 41 "prep.pgc"
+#line 30 "prep.pgc"
 
 	{ ECPGsetcommit(__LINE__, "on", NULL);
-#line 42 "prep.pgc"
+#line 31 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 42 "prep.pgc"
+#line 31 "prep.pgc"
 
 	for (i = 1; i <= REPEATS; ++i)
 	{
 		{ ECPGprepare(__LINE__, NULL, 0, "i", query);
-#line 45 "prep.pgc"
+#line 34 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 45 "prep.pgc"
+#line 34 "prep.pgc"
 
 		{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "i", 
 	ECPGt_int,&(value),(long)1,(long)1,sizeof(int), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
-#line 46 "prep.pgc"
+#line 35 "prep.pgc"
 
 if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 46 "prep.pgc"
+#line 35 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 46 "prep.pgc"
+#line 35 "prep.pgc"
 
 	}
 	{ ECPGdeallocate(__LINE__, 0, NULL, "i");
-#line 48 "prep.pgc"
+#line 37 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 48 "prep.pgc"
+#line 37 "prep.pgc"
 
 	{ ECPGdisconnect(__LINE__, name);
-#line 49 "prep.pgc"
+#line 38 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 49 "prep.pgc"
+#line 38 "prep.pgc"

 	return 0;
@@ -192,59 +181,43 @@ if (sqlca.sqlcode < 0) sqlprint();}
 int main ()
 {
 	intptr_t i;
-#ifdef WIN32
-	HANDLE threads[THREADS];
-#else
-	pthread_t threads[THREADS];
-#endif
+	pg_thrd_t threads[THREADS];
 
 	{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); 
-#line 63 "prep.pgc"
+#line 48 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 63 "prep.pgc"
+#line 48 "prep.pgc"
 
 	{ ECPGsetcommit(__LINE__, "on", NULL);
-#line 64 "prep.pgc"
+#line 49 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 64 "prep.pgc"
+#line 49 "prep.pgc"
 
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table if exists T", ECPGt_EOIT, ECPGt_EORT);
-#line 65 "prep.pgc"
+#line 50 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 65 "prep.pgc"
+#line 50 "prep.pgc"
 
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table T ( i int )", ECPGt_EOIT, ECPGt_EORT);
-#line 66 "prep.pgc"
+#line 51 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 66 "prep.pgc"
+#line 51 "prep.pgc"
 
 	{ ECPGdisconnect(__LINE__, "CURRENT");
-#line 67 "prep.pgc"
+#line 52 "prep.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 67 "prep.pgc"
+#line 52 "prep.pgc"

-#ifdef WIN32
 	for (i = 0; i < THREADS; ++i)
-	{
-		unsigned id;
-		threads[i] = (HANDLE)_beginthreadex(NULL, 0, fn, (void*)i, 0, &id);
-	}
-
-	WaitForMultipleObjects(THREADS, threads, TRUE, INFINITE);
+		pg_thrd_create(&threads[i], fn, (void *) i);
 	for (i = 0; i < THREADS; ++i)
-		CloseHandle(threads[i]);
-#else
-	for (i = 0; i < THREADS; ++i)
-		pthread_create(&threads[i], NULL, fn, (void *) i);
-	for (i = 0; i < THREADS; ++i)
-		pthread_join(threads[i], NULL);
-#endif
+		pg_thrd_join(threads[i], NULL);
 
 	return 0;
 }
diff --git a/src/interfaces/ecpg/test/expected/thread-thread.c b/src/interfaces/ecpg/test/expected/thread-thread.c
index 95faa223c2..03c7169995 100644
--- a/src/interfaces/ecpg/test/expected/thread-thread.c
+++ b/src/interfaces/ecpg/test/expected/thread-thread.c
@@ -14,13 +14,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include "ecpg_config.h"
-
-#ifndef WIN32
-#include <pthread.h>
-#else
-#include <windows.h>
-#include <locale.h>
-#endif
+#include "port/pg_threads.h"

 #line 1 "regression.h"
@@ -30,29 +24,25 @@

-#line 16 "thread.pgc"
+#line 10 "thread.pgc"

-void *test_thread(void *arg);
+int test_thread(void *arg);
 
 int nthreads   = 10;
 int iterations = 20;
 
 int main()
 {
-#ifndef WIN32
-  pthread_t *threads;
-#else
-  HANDLE *threads;
-#endif
+  pg_thrd_t *threads;
   intptr_t n;
   /* exec sql begin declare section */

-#line 32 "thread.pgc"
+#line 22 "thread.pgc"
  int l_rows ;
 /* exec sql end declare section */
-#line 33 "thread.pgc"
+#line 23 "thread.pgc"

  /* Do not switch on debug output for regression tests. The threads get executed in
@@ -61,22 +51,22 @@ int main()
 
   /* setup test_thread table */
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 40 "thread.pgc"
+#line 30 "thread.pgc"
 
   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test_thread", ECPGt_EOIT, ECPGt_EORT);}
-#line 41 "thread.pgc"
+#line 31 "thread.pgc"
  /* DROP might fail */
   { ECPGtrans(__LINE__, NULL, "commit");}
-#line 42 "thread.pgc"
+#line 32 "thread.pgc"
 
   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table test_thread ( tstamp timestamp not null default cast ( timeofday ( ) as timestamp ) , thread text not null , iteration integer not null , primary key ( thread , iteration ) )", ECPGt_EOIT, ECPGt_EORT);}
-#line 47 "thread.pgc"
+#line 37 "thread.pgc"
 
   { ECPGtrans(__LINE__, NULL, "commit");}
-#line 48 "thread.pgc"
+#line 38 "thread.pgc"
 
   { ECPGdisconnect(__LINE__, "CURRENT");}
-#line 49 "thread.pgc"
+#line 39 "thread.pgc"

   /* create, and start, threads */
@@ -87,39 +77,27 @@ int main()
       return 1;
     }
   for( n = 0; n < nthreads; n++ )
-    {
-#ifndef WIN32
-      pthread_create(&threads[n], NULL, test_thread, (void *) (n + 1));
-#else
-      threads[n] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) (void (*) (void)) test_thread, (void *) (n + 1), 0, NULL);
-#endif
-    }
+      pg_thrd_create(&threads[n], test_thread, (void *) (n + 1));
 
   /* wait for thread completion */
-#ifndef WIN32
   for( n = 0; n < nthreads; n++ )
-    {
-      pthread_join(threads[n], NULL);
-    }
-#else
-  WaitForMultipleObjects(nthreads, threads, TRUE, INFINITE);
-#endif
+      pg_thrd_join(threads[n], NULL);
   free(threads);
 
   /* and check results */
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 79 "thread.pgc"
+#line 57 "thread.pgc"
 
   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from test_thread", ECPGt_EOIT, 
 	ECPGt_int,&(l_rows),(long)1,(long)1,sizeof(int), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
-#line 80 "thread.pgc"
+#line 58 "thread.pgc"
 
   { ECPGtrans(__LINE__, NULL, "commit");}
-#line 81 "thread.pgc"
+#line 59 "thread.pgc"
 
   { ECPGdisconnect(__LINE__, "CURRENT");}
-#line 82 "thread.pgc"
+#line 60 "thread.pgc"
 
   if( l_rows == (nthreads * iterations) )
     printf("Success.\n");
@@ -129,7 +107,7 @@ int main()
   return 0;
 }
 
-void *test_thread(void *arg)
+int test_thread(void *arg)
 {
   long threadnum = (intptr_t) arg;
 
@@ -137,13 +115,13 @@ void *test_thread(void *arg)

-#line 96 "thread.pgc"
+#line 74 "thread.pgc"
  int l_i ;
  
-#line 97 "thread.pgc"
+#line 75 "thread.pgc"
  char l_connection [ 128 ] ;
 /* exec sql end declare section */
-#line 98 "thread.pgc"
+#line 76 "thread.pgc"

   /* build up connection name, and connect to database */
@@ -153,24 +131,24 @@ void *test_thread(void *arg)
   _snprintf(l_connection, sizeof(l_connection), "thread_%03ld", threadnum);
 #endif
   /* exec sql whenever sqlerror  sqlprint ; */
-#line 106 "thread.pgc"
+#line 84 "thread.pgc"
 
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , l_connection, 0); 
-#line 107 "thread.pgc"
+#line 85 "thread.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 107 "thread.pgc"
+#line 85 "thread.pgc"
 
   if( sqlca.sqlcode != 0 )
     {
       printf("%s: ERROR: cannot connect to database!\n", l_connection);
-      return NULL;
+      return 0;
     }
   { ECPGtrans(__LINE__, l_connection, "begin");
-#line 113 "thread.pgc"
+#line 91 "thread.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 113 "thread.pgc"
+#line 91 "thread.pgc"

   /* insert into test_thread table */
@@ -181,10 +159,10 @@ if (sqlca.sqlcode < 0) sqlprint();}
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
 	ECPGt_int,&(l_i),(long)1,(long)1,sizeof(int), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
-#line 118 "thread.pgc"
+#line 96 "thread.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 118 "thread.pgc"
+#line 96 "thread.pgc"
 
       if( sqlca.sqlcode != 0 )
 	printf("%s: ERROR: insert failed!\n", l_connection);
@@ -192,16 +170,16 @@ if (sqlca.sqlcode < 0) sqlprint();}
 
   /* all done */
   { ECPGtrans(__LINE__, l_connection, "commit");
-#line 124 "thread.pgc"
+#line 102 "thread.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 124 "thread.pgc"
+#line 102 "thread.pgc"
 
   { ECPGdisconnect(__LINE__, l_connection);
-#line 125 "thread.pgc"
+#line 103 "thread.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 125 "thread.pgc"
+#line 103 "thread.pgc"
 
-  return NULL;
+  return 0;
 }
diff --git a/src/interfaces/ecpg/test/expected/thread-thread_implicit.c b/src/interfaces/ecpg/test/expected/thread-thread_implicit.c
index 7ac0297a23..c859012f50 100644
--- a/src/interfaces/ecpg/test/expected/thread-thread_implicit.c
+++ b/src/interfaces/ecpg/test/expected/thread-thread_implicit.c
@@ -14,13 +14,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include "ecpg_config.h"
-
-#ifndef WIN32
-#include <pthread.h>
-#else
-#include <windows.h>
-#include <locale.h>
-#endif
+#include "port/pg_threads.h"

 #line 1 "regression.h"
@@ -30,29 +24,25 @@

-#line 16 "thread_implicit.pgc"
+#line 10 "thread_implicit.pgc"

-void *test_thread(void *arg);
+int test_thread(void *arg);
 
 int nthreads   = 10;
 int iterations = 20;
 
 int main()
 {
-#ifndef WIN32
-  pthread_t *threads;
-#else
-  HANDLE *threads;
-#endif
+  pg_thrd_t *threads;
   intptr_t n;
   /* exec sql begin declare section */

-#line 32 "thread_implicit.pgc"
+#line 22 "thread_implicit.pgc"
  int l_rows ;
 /* exec sql end declare section */
-#line 33 "thread_implicit.pgc"
+#line 23 "thread_implicit.pgc"

  /* Do not switch on debug output for regression tests. The threads get executed in
@@ -61,22 +51,22 @@ int main()
 
   /* setup test_thread table */
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 40 "thread_implicit.pgc"
+#line 30 "thread_implicit.pgc"
 
   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test_thread", ECPGt_EOIT, ECPGt_EORT);}
-#line 41 "thread_implicit.pgc"
+#line 31 "thread_implicit.pgc"
  /* DROP might fail */
   { ECPGtrans(__LINE__, NULL, "commit");}
-#line 42 "thread_implicit.pgc"
+#line 32 "thread_implicit.pgc"
 
   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table test_thread ( tstamp timestamp not null default cast ( timeofday ( ) as timestamp ) , thread text not null , iteration integer not null , primary key ( thread , iteration ) )", ECPGt_EOIT, ECPGt_EORT);}
-#line 47 "thread_implicit.pgc"
+#line 37 "thread_implicit.pgc"
 
   { ECPGtrans(__LINE__, NULL, "commit");}
-#line 48 "thread_implicit.pgc"
+#line 38 "thread_implicit.pgc"
 
   { ECPGdisconnect(__LINE__, "CURRENT");}
-#line 49 "thread_implicit.pgc"
+#line 39 "thread_implicit.pgc"

   /* create, and start, threads */
@@ -87,39 +77,27 @@ int main()
       return 1;
     }
   for( n = 0; n < nthreads; n++ )
-    {
-#ifndef WIN32
-      pthread_create(&threads[n], NULL, test_thread, (void *) (n + 1));
-#else
-      threads[n] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) (void (*) (void)) test_thread, (void *) (n+1), 0, NULL);
-#endif
-    }
+      pg_thrd_create(&threads[n], test_thread, (void *) (n + 1));
 
   /* wait for thread completion */
-#ifndef WIN32
   for( n = 0; n < nthreads; n++ )
-    {
-      pthread_join(threads[n], NULL);
-    }
-#else
-  WaitForMultipleObjects(nthreads, threads, TRUE, INFINITE);
-#endif
+      pg_thrd_join(threads[n], NULL);
   free(threads);
 
   /* and check results */
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 79 "thread_implicit.pgc"
+#line 57 "thread_implicit.pgc"
 
   { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from test_thread", ECPGt_EOIT, 
 	ECPGt_int,&(l_rows),(long)1,(long)1,sizeof(int), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
-#line 80 "thread_implicit.pgc"
+#line 58 "thread_implicit.pgc"
 
   { ECPGtrans(__LINE__, NULL, "commit");}
-#line 81 "thread_implicit.pgc"
+#line 59 "thread_implicit.pgc"
 
   { ECPGdisconnect(__LINE__, "CURRENT");}
-#line 82 "thread_implicit.pgc"
+#line 60 "thread_implicit.pgc"
 
   if( l_rows == (nthreads * iterations) )
     printf("Success.\n");
@@ -129,7 +107,7 @@ int main()
   return 0;
 }
 
-void *test_thread(void *arg)
+int test_thread(void *arg)
 {
   long threadnum = (intptr_t) arg;
 
@@ -137,13 +115,13 @@ void *test_thread(void *arg)

-#line 96 "thread_implicit.pgc"
+#line 74 "thread_implicit.pgc"
  int l_i ;
  
-#line 97 "thread_implicit.pgc"
+#line 75 "thread_implicit.pgc"
  char l_connection [ 128 ] ;
 /* exec sql end declare section */
-#line 98 "thread_implicit.pgc"
+#line 76 "thread_implicit.pgc"

   /* build up connection name, and connect to database */
@@ -153,24 +131,24 @@ void *test_thread(void *arg)
   _snprintf(l_connection, sizeof(l_connection), "thread_%03ld", threadnum);
 #endif
   /* exec sql whenever sqlerror  sqlprint ; */
-#line 106 "thread_implicit.pgc"
+#line 84 "thread_implicit.pgc"
 
   { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , l_connection, 0); 
-#line 107 "thread_implicit.pgc"
+#line 85 "thread_implicit.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 107 "thread_implicit.pgc"
+#line 85 "thread_implicit.pgc"
 
   if( sqlca.sqlcode != 0 )
     {
       printf("%s: ERROR: cannot connect to database!\n", l_connection);
-      return NULL;
+      return 0;
     }
   { ECPGtrans(__LINE__, NULL, "begin");
-#line 113 "thread_implicit.pgc"
+#line 91 "thread_implicit.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 113 "thread_implicit.pgc"
+#line 91 "thread_implicit.pgc"

   /* insert into test_thread table */
@@ -181,10 +159,10 @@ if (sqlca.sqlcode < 0) sqlprint();}
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
 	ECPGt_int,&(l_i),(long)1,(long)1,sizeof(int), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
-#line 118 "thread_implicit.pgc"
+#line 96 "thread_implicit.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 118 "thread_implicit.pgc"
+#line 96 "thread_implicit.pgc"
 
       if( sqlca.sqlcode != 0 )
 	printf("%s: ERROR: insert failed!\n", l_connection);
@@ -192,16 +170,16 @@ if (sqlca.sqlcode < 0) sqlprint();}
 
   /* all done */
   { ECPGtrans(__LINE__, NULL, "commit");
-#line 124 "thread_implicit.pgc"
+#line 102 "thread_implicit.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 124 "thread_implicit.pgc"
+#line 102 "thread_implicit.pgc"
 
   { ECPGdisconnect(__LINE__, l_connection);
-#line 125 "thread_implicit.pgc"
+#line 103 "thread_implicit.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
-#line 125 "thread_implicit.pgc"
+#line 103 "thread_implicit.pgc"
 
-  return NULL;
+  return 0;
 }
diff --git a/src/interfaces/ecpg/test/thread/alloc.pgc b/src/interfaces/ecpg/test/thread/alloc.pgc
index d3d35493bf..248a29447c 100644
--- a/src/interfaces/ecpg/test/thread/alloc.pgc
+++ b/src/interfaces/ecpg/test/thread/alloc.pgc
@@ -1,16 +1,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include "ecpg_config.h"
-
-#ifdef WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <process.h>
-#include <locale.h>
-#else
-#include <pthread.h>
-#endif
-#include <stdio.h>
+#include "port/pg_threads.h"
 
 #define THREADS		16
 #define REPEATS		50
@@ -21,11 +12,7 @@ exec sql include ../regression;
 exec sql whenever sqlerror sqlprint;
 exec sql whenever not found sqlprint;
 
-#ifdef WIN32
-static unsigned __stdcall fn(void* arg)
-#else
-static void* fn(void* arg)
-#endif
+static int fn(void* arg)
 {
 	int i;
 
@@ -54,28 +41,12 @@ static void* fn(void* arg)
 int main ()
 {
 	intptr_t i;
-#ifdef WIN32
-	HANDLE threads[THREADS];
-#else
-	pthread_t threads[THREADS];
-#endif
-
-#ifdef WIN32
-	for (i = 0; i < THREADS; ++i)
-	{
-		unsigned id;
-		threads[i] = (HANDLE)_beginthreadex(NULL, 0, fn, (void*)i, 0, &id);
-	}
+	pg_thrd_t threads[THREADS];
 
-	WaitForMultipleObjects(THREADS, threads, TRUE, INFINITE);
-	for (i = 0; i < THREADS; ++i)
-		CloseHandle(threads[i]);
-#else
 	for (i = 0; i < THREADS; ++i)
-		pthread_create(&threads[i], NULL, fn, (void *) i);
+		pg_thrd_create(&threads[i], fn, (void *) i);
 	for (i = 0; i < THREADS; ++i)
-		pthread_join(threads[i], NULL);
-#endif
+		pg_thrd_join(threads[i], NULL);
 
 	return 0;
 }
diff --git a/src/interfaces/ecpg/test/thread/descriptor.pgc b/src/interfaces/ecpg/test/thread/descriptor.pgc
index 30bce7c87b..faa9a0a656 100644
--- a/src/interfaces/ecpg/test/thread/descriptor.pgc
+++ b/src/interfaces/ecpg/test/thread/descriptor.pgc
@@ -1,12 +1,4 @@
-#ifdef WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <process.h>
-#include <locale.h>
-#else
-#include <pthread.h>
-#endif
-#include <stdio.h>
+#include "port/pg_threads.h"
 
 #define THREADS		16
 #define REPEATS		50000
@@ -15,11 +7,7 @@ EXEC SQL include sqlca;
 EXEC SQL whenever sqlerror sqlprint;
 EXEC SQL whenever not found sqlprint;
 
-#if defined(WIN32)
-static unsigned __stdcall fn(void* arg)
-#else
-static void* fn(void* arg)
-#endif
+static int fn(void* arg)
 {
 	int i;
 
@@ -35,28 +23,12 @@ static void* fn(void* arg)
 int main ()
 {
 	int i;
-#ifdef WIN32
-	HANDLE threads[THREADS];
-#else
-	pthread_t threads[THREADS];
-#endif
+	pg_thrd_t threads[THREADS];
 
-#ifdef WIN32
 	for (i = 0; i < THREADS; ++i)
-	{
-		unsigned id;
-		threads[i] = (HANDLE)_beginthreadex(NULL, 0, fn, NULL, 0, &id);
-	}
-
-	WaitForMultipleObjects(THREADS, threads, TRUE, INFINITE);
-	for (i = 0; i < THREADS; ++i)
-		CloseHandle(threads[i]);
-#else
-	for (i = 0; i < THREADS; ++i)
-		pthread_create(&threads[i], NULL, fn, NULL);
+		pg_thrd_create(&threads[i], fn, NULL);
 	for (i = 0; i < THREADS; ++i)
-		pthread_join(threads[i], NULL);
-#endif
+		pg_thrd_join(threads[i], NULL);
 
 	return 0;
 }
diff --git a/src/interfaces/ecpg/test/thread/prep.pgc b/src/interfaces/ecpg/test/thread/prep.pgc
index f61b31ce10..f5a875cc28 100644
--- a/src/interfaces/ecpg/test/thread/prep.pgc
+++ b/src/interfaces/ecpg/test/thread/prep.pgc
@@ -1,15 +1,8 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include "ecpg_config.h"
+#include "port/pg_threads.h"
 
-#ifdef WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <process.h>
-#include <locale.h>
-#else
-#include <pthread.h>
-#endif
 #include <stdio.h>
 
 #define THREADS		16
@@ -21,11 +14,7 @@ exec sql include ../regression;
 exec sql whenever sqlerror sqlprint;
 exec sql whenever not found sqlprint;
 
-#ifdef WIN32
-static unsigned __stdcall fn(void* arg)
-#else
-static void* fn(void* arg)
-#endif
+static int fn(void* arg)
 {
 	int i;
 
@@ -54,11 +43,7 @@ static void* fn(void* arg)
 int main ()
 {
 	intptr_t i;
-#ifdef WIN32
-	HANDLE threads[THREADS];
-#else
-	pthread_t threads[THREADS];
-#endif
+	pg_thrd_t threads[THREADS];
 
 	EXEC SQL CONNECT TO REGRESSDB1;
 	EXEC SQL SET AUTOCOMMIT TO ON;
@@ -66,22 +51,10 @@ int main ()
 	EXEC SQL CREATE TABLE T ( i int );
 	EXEC SQL DISCONNECT;
 
-#ifdef WIN32
 	for (i = 0; i < THREADS; ++i)
-	{
-		unsigned id;
-		threads[i] = (HANDLE)_beginthreadex(NULL, 0, fn, (void*)i, 0, &id);
-	}
-
-	WaitForMultipleObjects(THREADS, threads, TRUE, INFINITE);
-	for (i = 0; i < THREADS; ++i)
-		CloseHandle(threads[i]);
-#else
-	for (i = 0; i < THREADS; ++i)
-		pthread_create(&threads[i], NULL, fn, (void *) i);
+		pg_thrd_create(&threads[i], fn, (void *) i);
 	for (i = 0; i < THREADS; ++i)
-		pthread_join(threads[i], NULL);
-#endif
+		pg_thrd_join(threads[i], NULL);
 
 	return 0;
 }
diff --git a/src/interfaces/ecpg/test/thread/thread.pgc b/src/interfaces/ecpg/test/thread/thread.pgc
index b9b9ebb441..bc967b9da8 100644
--- a/src/interfaces/ecpg/test/thread/thread.pgc
+++ b/src/interfaces/ecpg/test/thread/thread.pgc
@@ -5,28 +5,18 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include "ecpg_config.h"
-
-#ifndef WIN32
-#include <pthread.h>
-#else
-#include <windows.h>
-#include <locale.h>
-#endif
+#include "port/pg_threads.h"
 
 exec sql include ../regression;
 
-void *test_thread(void *arg);
+int test_thread(void *arg);
 
 int nthreads   = 10;
 int iterations = 20;
 
 int main()
 {
-#ifndef WIN32
-  pthread_t *threads;
-#else
-  HANDLE *threads;
-#endif
+  pg_thrd_t *threads;
   intptr_t n;
   EXEC SQL BEGIN DECLARE SECTION;
   int l_rows;
@@ -56,23 +46,11 @@ int main()
       return 1;
     }
   for( n = 0; n < nthreads; n++ )
-    {
-#ifndef WIN32
-      pthread_create(&threads[n], NULL, test_thread, (void *) (n + 1));
-#else
-      threads[n] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) (void (*) (void)) test_thread, (void *) (n + 1), 0, NULL);
-#endif
-    }
+      pg_thrd_create(&threads[n], test_thread, (void *) (n + 1));
 
   /* wait for thread completion */
-#ifndef WIN32
   for( n = 0; n < nthreads; n++ )
-    {
-      pthread_join(threads[n], NULL);
-    }
-#else
-  WaitForMultipleObjects(nthreads, threads, TRUE, INFINITE);
-#endif
+      pg_thrd_join(threads[n], NULL);
   free(threads);
 
   /* and check results */
@@ -88,7 +66,7 @@ int main()
   return 0;
 }
 
-void *test_thread(void *arg)
+int test_thread(void *arg)
 {
   long threadnum = (intptr_t) arg;
 
@@ -108,7 +86,7 @@ void *test_thread(void *arg)
   if( sqlca.sqlcode != 0 )
     {
       printf("%s: ERROR: cannot connect to database!\n", l_connection);
-      return NULL;
+      return 0;
     }
   EXEC SQL AT :l_connection BEGIN;
 
@@ -123,5 +101,5 @@ void *test_thread(void *arg)
   /* all done */
   EXEC SQL AT :l_connection COMMIT;
   EXEC SQL DISCONNECT :l_connection;
-  return NULL;
+  return 0;
 }
diff --git a/src/interfaces/ecpg/test/thread/thread_implicit.pgc b/src/interfaces/ecpg/test/thread/thread_implicit.pgc
index ff9b12a943..7a793dc7f8 100644
--- a/src/interfaces/ecpg/test/thread/thread_implicit.pgc
+++ b/src/interfaces/ecpg/test/thread/thread_implicit.pgc
@@ -5,28 +5,18 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include "ecpg_config.h"
-
-#ifndef WIN32
-#include <pthread.h>
-#else
-#include <windows.h>
-#include <locale.h>
-#endif
+#include "port/pg_threads.h"
 
 exec sql include ../regression;
 
-void *test_thread(void *arg);
+int test_thread(void *arg);
 
 int nthreads   = 10;
 int iterations = 20;
 
 int main()
 {
-#ifndef WIN32
-  pthread_t *threads;
-#else
-  HANDLE *threads;
-#endif
+  pg_thrd_t *threads;
   intptr_t n;
   EXEC SQL BEGIN DECLARE SECTION;
   int l_rows;
@@ -56,23 +46,11 @@ int main()
       return 1;
     }
   for( n = 0; n < nthreads; n++ )
-    {
-#ifndef WIN32
-      pthread_create(&threads[n], NULL, test_thread, (void *) (n + 1));
-#else
-      threads[n] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) (void (*) (void)) test_thread, (void *) (n+1), 0, NULL);
-#endif
-    }
+      pg_thrd_create(&threads[n], test_thread, (void *) (n + 1));
 
   /* wait for thread completion */
-#ifndef WIN32
   for( n = 0; n < nthreads; n++ )
-    {
-      pthread_join(threads[n], NULL);
-    }
-#else
-  WaitForMultipleObjects(nthreads, threads, TRUE, INFINITE);
-#endif
+      pg_thrd_join(threads[n], NULL);
   free(threads);
 
   /* and check results */
@@ -88,7 +66,7 @@ int main()
   return 0;
 }
 
-void *test_thread(void *arg)
+int test_thread(void *arg)
 {
   long threadnum = (intptr_t) arg;
 
@@ -108,7 +86,7 @@ void *test_thread(void *arg)
   if( sqlca.sqlcode != 0 )
     {
       printf("%s: ERROR: cannot connect to database!\n", l_connection);
-      return NULL;
+      return 0;
     }
   EXEC SQL BEGIN;
 
@@ -123,5 +101,5 @@ void *test_thread(void *arg)
   /* all done */
   EXEC SQL COMMIT;
   EXEC SQL DISCONNECT :l_connection;
-  return NULL;
+  return 0;
 }
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 46653682b0..62269ddbf4 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -69,7 +69,6 @@ endif
 
 ifeq ($(PORTNAME), win32)
 OBJS += \
-	pthread-win32.o \
 	win32.o
 endif
 
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 837c5321aa..7806f67879 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -32,6 +32,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"
@@ -52,12 +53,6 @@
 #include <netinet/tcp.h>
 #endif
 
-#ifdef WIN32
-#include "pthread-win32.h"
-#else
-#include <pthread.h>
-#endif
-
 #ifdef USE_LDAP
 #ifdef WIN32
 #include <winldap.h>
@@ -7779,35 +7774,29 @@ pqGetHomeDirectory(char *buf, int bufsize)
  * the field.
  */
 
+static pg_mtx_t singlethread_lock;
+
+static void
+singlethread_lock_init(void)
+{
+	pg_mtx_init(&singlethread_lock, pg_mtx_plain);
+}
+
 static void
 default_threadlock(int acquire)
 {
-#ifndef WIN32
-	static pthread_mutex_t singlethread_lock = PTHREAD_MUTEX_INITIALIZER;
-#else
-	static pthread_mutex_t singlethread_lock = NULL;
-	static long mutex_initlock = 0;
+	static pg_once_flag singlethread_lock_once = PG_ONCE_FLAG_INIT;
+
+	pg_call_once(&singlethread_lock_once, singlethread_lock_init);
 
-	if (singlethread_lock == NULL)
-	{
-		while (InterlockedExchange(&mutex_initlock, 1) == 1)
-			 /* loop, another thread own the lock */ ;
-		if (singlethread_lock == NULL)
-		{
-			if (pthread_mutex_init(&singlethread_lock, NULL))
-				Assert(false);
-		}
-		InterlockedExchange(&mutex_initlock, 0);
-	}
-#endif
 	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-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 4976d963a2..dcbab932ee 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'
@@ -91,12 +86,8 @@ static bool ssl_lib_initialized = false;
 
 static long crypto_open_connections = 0;
 
-#ifndef WIN32
-static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
-#else
-static pthread_mutex_t ssl_config_mutex = NULL;
-static long win32_ssl_create_mutex = 0;
-#endif
+static pg_mtx_t ssl_config_mutex;
+static pg_once_flag ssl_config_mutex_once = PG_ONCE_FLAG_INIT;
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
@@ -726,14 +717,14 @@ static unsigned long
 pq_threadidcallback(void)
 {
 	/*
-	 * This is not standards-compliant.  pthread_self() returns pthread_t, and
+	 * This is not standards-compliant.  thrd_currnt() returns thrd_t, and
 	 * shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires
 	 * it, so we have to do it.
 	 */
-	return (unsigned long) pthread_self();
+	return (unsigned long) thrd_current();
 }
 
-static pthread_mutex_t *pq_lockarray;
+static pg_mtx_t *pq_lockarray;
 
 static void
 pq_lockingcallback(int mode, int n, const char *file, int line)
@@ -745,17 +736,23 @@ pq_lockingcallback(int mode, int n, const char *file, int line)
 	 */
 	if (mode & CRYPTO_LOCK)
 	{
-		if (pthread_mutex_lock(&pq_lockarray[n]))
+		if (pg_mtx_lock(&pq_lockarray[n]) != pg_thrd_success)
 			Assert(false);
 	}
 	else
 	{
-		if (pthread_mutex_unlock(&pq_lockarray[n]))
+		if (pg_mtx_unlock(&pq_lockarray[n]) != pg_thrd_success)
 			Assert(false);
 	}
 }
 #endif							/* HAVE_CRYPTO_LOCK */
 
+static void
+ssl_config_mutex_init(void)
+{
+	pg_mtx_init(&ssl_config_mutex, pg_mtx_plain);
+}
+
 /*
  * Initialize SSL library.
  *
@@ -769,21 +766,9 @@ pq_lockingcallback(int mode, int n, const char *file, int line)
 int
 pgtls_init(PGconn *conn, bool do_ssl, bool do_crypto)
 {
-#ifdef WIN32
-	/* Also see similar code in fe-connect.c, default_threadlock() */
-	if (ssl_config_mutex == NULL)
-	{
-		while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
-			 /* loop, another thread own the lock */ ;
-		if (ssl_config_mutex == NULL)
-		{
-			if (pthread_mutex_init(&ssl_config_mutex, NULL))
-				return -1;
-		}
-		InterlockedExchange(&win32_ssl_create_mutex, 0);
-	}
-#endif
-	if (pthread_mutex_lock(&ssl_config_mutex))
+	pg_call_once(&ssl_config_mutex_once, ssl_config_mutex_init);
+
+	if (pg_mtx_lock(&ssl_config_mutex) != pg_thrd_success)
 		return -1;
 
 #ifdef HAVE_CRYPTO_LOCK
@@ -797,19 +782,19 @@ pgtls_init(PGconn *conn, bool do_ssl, bool do_crypto)
 		{
 			int			i;
 
-			pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
+			pq_lockarray = malloc(sizeof(pg_mtx_t) * CRYPTO_num_locks());
 			if (!pq_lockarray)
 			{
-				pthread_mutex_unlock(&ssl_config_mutex);
+				pg_mtx_unlock(&ssl_config_mutex);
 				return -1;
 			}
 			for (i = 0; i < CRYPTO_num_locks(); i++)
 			{
-				if (pthread_mutex_init(&pq_lockarray[i], NULL))
+				if (pg_mtx_init(&pq_lockarray[i], NULL) != pg_thrd_success)
 				{
 					free(pq_lockarray);
 					pq_lockarray = NULL;
-					pthread_mutex_unlock(&ssl_config_mutex);
+					pg_mtx_unlock(&ssl_config_mutex);
 					return -1;
 				}
 			}
@@ -850,7 +835,7 @@ pgtls_init(PGconn *conn, bool do_ssl, bool do_crypto)
 		ssl_lib_initialized = true;
 	}
 
-	pthread_mutex_unlock(&ssl_config_mutex);
+	pg_mtx_unlock(&ssl_config_mutex);
 	return 0;
 }
 
@@ -871,7 +856,7 @@ destroy_ssl_system(void)
 {
 #if defined(HAVE_CRYPTO_LOCK)
 	/* Mutex is created in pgtls_init() */
-	if (pthread_mutex_lock(&ssl_config_mutex))
+	if (pg_mtx_lock(&ssl_config_mutex) != pg_thrd_success)
 		return;
 
 	if (pq_init_crypto_lib && crypto_open_connections > 0)
@@ -897,7 +882,7 @@ destroy_ssl_system(void)
 		 */
 	}
 
-	pthread_mutex_unlock(&ssl_config_mutex);
+	pg_mtx_unlock(&ssl_config_mutex);
 #endif
 }
 
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index bd72a87bbb..804b5e30aa 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -35,9 +35,7 @@
 
 #include <sys/stat.h>
 
-#ifdef WIN32
-#include "pthread-win32.h"
-#else
+#ifndef WIN32
 #include <pthread.h>
 #endif
 
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 10f20c1175..870db73841 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 80e6a15adf..6aa0276209 100644
--- a/src/interfaces/libpq/meson.build
+++ b/src/interfaces/libpq/meson.build
@@ -21,7 +21,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',])
@@ -43,7 +43,7 @@ export_file = custom_target('libpq.exports',
   kwargs: gen_export_kwargs,
 )
 
-# port needs to be in include path due to pthread-win32.h
+# XXX why do we need this?
 libpq_inc = include_directories('.', '../../port')
 libpq_c_args = ['-DSO_MAJOR_VERSION=5']
 
diff --git a/src/interfaces/libpq/pthread-win32.c b/src/interfaces/libpq/pthread-win32.c
deleted file mode 100644
index 8e65637387..0000000000
--- a/src/interfaces/libpq/pthread-win32.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*-------------------------------------------------------------------------
-*
-* pthread-win32.c
-*	 partial pthread implementation for win32
-*
-* Copyright (c) 2004-2023, 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 = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION));
-	if (!*mp)
-		return 1;
-	InitializeCriticalSection(*mp);
-	return 0;
-}
-
-int
-pthread_mutex_lock(pthread_mutex_t *mp)
-{
-	if (!*mp)
-		return 1;
-	EnterCriticalSection(*mp);
-	return 0;
-}
-
-int
-pthread_mutex_unlock(pthread_mutex_t *mp)
-{
-	if (!*mp)
-		return 1;
-	LeaveCriticalSection(*mp);
-	return 0;
-}
diff --git a/src/port/pthread-win32.h b/src/port/pthread-win32.h
deleted file mode 100644
index 97ccc17a12..0000000000
--- a/src/port/pthread-win32.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * src/port/pthread-win32.h
- */
-#ifndef __PTHREAD_H
-#define __PTHREAD_H
-
-typedef ULONG pthread_key_t;
-typedef CRITICAL_SECTION *pthread_mutex_t;
-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/cpluspluscheck b/src/tools/pginclude/cpluspluscheck
index 4e09c4686b..121c7a2684 100755
--- a/src/tools/pginclude/cpluspluscheck
+++ b/src/tools/pginclude/cpluspluscheck
@@ -79,7 +79,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.
diff --git a/src/tools/pginclude/headerscheck b/src/tools/pginclude/headerscheck
index 8dee1b5670..b42467dfc6 100755
--- a/src/tools/pginclude/headerscheck
+++ b/src/tools/pginclude/headerscheck
@@ -74,7 +74,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.39.2