From 9b5dafb641c841c94e42af1dfab24be40ab52ed9 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 24 Jun 2026 17:19:25 +1200
Subject: [PATCH v1 05/14] ecpg: Use pg_threads.h.

* adopt pg_threads.h for threads, mutexes
* use plain thread_local for thread-local objects
* use pg_thrd_atexit for cleanup
* remove local threading abstraction

The underlying mechanism for pg_thrd_atexit is the same as before (it
uses a single thread-specific destructor), but now works on Windows too,
fixing the known leak of auto-allocated memory, descriptors and SQL
state objects on that OS.

Discussion:
Reviewed-by:
---
 src/interfaces/ecpg/ecpglib/connect.c         |  55 +++----
 src/interfaces/ecpg/ecpglib/descriptor.c      |  46 ++----
 src/interfaces/ecpg/ecpglib/ecpglib_extern.h  |   3 +-
 src/interfaces/ecpg/ecpglib/execute.c         |   2 -
 src/interfaces/ecpg/ecpglib/memory.c          |  76 ++-------
 src/interfaces/ecpg/ecpglib/misc.c            | 148 ++++++++----------
 src/interfaces/ecpg/ecpglib/sqlda.c           |   2 +-
 .../ecpg/include/ecpg-pthread-win32.h         |  49 ------
 src/interfaces/ecpg/include/ecpglib.h         |   2 -
 .../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 +----
 19 files changed, 285 insertions(+), 696 deletions(-)
 delete mode 100644 src/interfaces/ecpg/include/ecpg-pthread-win32.h

diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c
index 36b97fcdba0..01af16c8625 100644
--- a/src/interfaces/ecpg/ecpglib/connect.c
+++ b/src/interfaces/ecpg/ecpglib/connect.c
@@ -3,35 +3,22 @@
 #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"
 #include "sqlca.h"
 
 #ifdef HAVE_USELOCALE
 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 = PG_MTX_INIT;
+static thread_local struct connection *actual_connection_thread_local;
 static struct connection *actual_connection_global = NULL;
 static struct connection *all_connections = NULL;
 
-static void
-ecpg_actual_connection_init(void)
-{
-	pthread_key_create(&actual_connection_key, NULL);
-}
-
-void
-ecpg_pthreads_init(void)
-{
-	pthread_once(&actual_connection_key_once, ecpg_actual_connection_init);
-}
-
 static struct connection *
 ecpg_get_connection_nr(const char *connection_name)
 {
@@ -39,9 +26,7 @@ ecpg_get_connection_nr(const char *connection_name)
 
 	if ((connection_name == NULL) || (strcmp(connection_name, "CURRENT") == 0))
 	{
-		ecpg_pthreads_init();	/* ensure actual_connection_key is valid */
-
-		ret = pthread_getspecific(actual_connection_key);
+		ret = actual_connection_thread_local;
 
 		/*
 		 * if no connection in TSD for this thread, get the global default
@@ -79,9 +64,7 @@ ecpg_get_connection(const char *connection_name)
 
 	if ((connection_name == NULL) || (strcmp(connection_name, "CURRENT") == 0))
 	{
-		ecpg_pthreads_init();	/* ensure actual_connection_key is valid */
-
-		ret = pthread_getspecific(actual_connection_key);
+		ret = actual_connection_thread_local;
 
 		/*
 		 * if no connection in TSD for this thread, get the global default
@@ -94,11 +77,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;
@@ -132,8 +115,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 (actual_connection_thread_local == act)
+			actual_connection_thread_local = all_connections;
 		if (actual_connection_global == act)
 			actual_connection_global = all_connections;
 
@@ -199,7 +182,7 @@ ECPGsetconn(int lineno, const char *connection_name)
 	if (!ecpg_init(con, connection_name, lineno))
 		return false;
 
-	pthread_setspecific(actual_connection_key, con);
+	actual_connection_thread_local = con;
 	return true;
 }
 
@@ -311,8 +294,6 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 	if (dbname == NULL && connection_name == NULL)
 		connection_name = "DEFAULT";
 
-	ecpg_pthreads_init();
-
 	/* check if the identifier is unique */
 	if (ecpg_get_connection(connection_name))
 	{
@@ -501,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
@@ -513,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)
@@ -547,7 +528,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);
+	actual_connection_thread_local = all_connections;
 	actual_connection_global = all_connections;
 
 	ecpg_log("ECPGconnect: opening database %s on %s port %s %s%s %s%s\n",
@@ -668,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)
@@ -680,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;
 
@@ -702,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)
 	{
@@ -721,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 4e816879617..2caa6e9b4e1 100644
--- a/src/interfaces/ecpg/ecpglib/descriptor.c
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -8,11 +8,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"
@@ -20,36 +20,10 @@
 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 thread_local struct descriptor *descriptors_thread_local;
 
 static void descriptor_deallocate_all(struct descriptor *list);
 
-static void
-descriptor_destructor(void *arg)
-{
-	descriptor_deallocate_all(arg);
-}
-
-static void
-descriptor_key_init(void)
-{
-	pthread_key_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);
-}
-
-static void
-set_descriptors(struct descriptor *value)
-{
-	pthread_setspecific(descriptor_key, value);
-}
-
 /* old internal convenience function that might go away later */
 static PGresult *
 ecpg_result_by_descriptor(int line, const char *name)
@@ -776,14 +750,14 @@ ECPGdeallocate_desc(int line, const char *name)
 	}
 
 	ecpg_init_sqlca(sqlca);
-	for (desc = get_descriptors(), prev = NULL; desc; prev = desc, desc = desc->next)
+	for (desc = descriptors_thread_local, prev = NULL; desc; prev = desc, desc = desc->next)
 	{
 		if (strcmp(name, desc->name) == 0)
 		{
 			if (prev)
 				prev->next = desc->next;
 			else
-				set_descriptors(desc->next);
+				descriptors_thread_local = desc->next;
 			descriptor_free(desc);
 			return true;
 		}
@@ -822,7 +796,7 @@ ECPGallocate_desc(int line, const char *name)
 	new = (struct descriptor *) ecpg_alloc(sizeof(struct descriptor), line);
 	if (!new)
 		return false;
-	new->next = get_descriptors();
+	new->next = descriptors_thread_local;
 	new->name = ecpg_alloc(strlen(name) + 1, line);
 	if (!new->name)
 	{
@@ -840,7 +814,7 @@ ECPGallocate_desc(int line, const char *name)
 		return false;
 	}
 	strcpy(new->name, name);
-	set_descriptors(new);
+	descriptors_thread_local = new;
 	return true;
 }
 
@@ -850,7 +824,7 @@ ecpg_find_desc(int line, const char *name)
 {
 	struct descriptor *desc;
 
-	for (desc = get_descriptors(); desc; desc = desc->next)
+	for (desc = descriptors_thread_local; desc; desc = desc->next)
 	{
 		if (strcmp(name, desc->name) == 0)
 			return desc;
@@ -1006,3 +980,9 @@ ECPGdescribe(int line, int compat, bool input, const char *connection_name, cons
 
 	return ret;
 }
+
+void
+ecpg_descriptor_on_thread_exit(void)
+{
+	descriptor_deallocate_all(descriptors_thread_local);
+}
diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
index 0b8cea6196d..487a771015f 100644
--- a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
+++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
@@ -166,7 +166,6 @@ 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);
 
-void		ecpg_pthreads_init(void);
 struct connection *ecpg_get_connection(const char *connection_name);
 char	   *ecpg_alloc(long size, int lineno);
 char	   *ecpg_auto_alloc(long size, int lineno);
@@ -233,6 +232,8 @@ unsigned	ecpg_hex_dec_len(unsigned srclen);
 unsigned	ecpg_hex_enc_len(unsigned srclen);
 unsigned	ecpg_hex_encode(const char *src, unsigned len, char *dst);
 
+void		ecpg_descriptor_on_thread_exit(void);
+
 #ifdef ENABLE_NLS
 extern char *ecpg_gettext(const char *msgid) pg_attribute_format_arg(1);
 #else
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 02380dafd9d..2085b62ec4b 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -1963,8 +1963,6 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
 		return false;
 	}
 
-	ecpg_pthreads_init();
-
 	con = ecpg_get_connection(connection_name);
 
 	if (!ecpg_init(con, connection_name, lineno))
diff --git a/src/interfaces/ecpg/ecpglib/memory.c b/src/interfaces/ecpg/ecpglib/memory.c
index 236029c73e2..1277d2e2d11 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)
@@ -77,35 +77,7 @@ 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 void ecpg_clear_auto_mem_list(struct auto_mem *list);
-
-static void
-auto_mem_destructor(void *arg)
-{
-	ecpg_clear_auto_mem_list((struct auto_mem *) arg);
-}
-
-static void
-auto_mem_key_init(void)
-{
-	pthread_key_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);
-}
-
-static void
-set_auto_allocs(struct auto_mem *am)
-{
-	pthread_setspecific(auto_mem_key, am);
-}
+static thread_local struct auto_mem *auto_mem_thread_local;
 
 char *
 ecpg_auto_alloc(long size, int lineno)
@@ -132,50 +104,36 @@ ecpg_add_mem(void *ptr, int lineno)
 		return false;
 
 	am->pointer = ptr;
-	am->next = get_auto_allocs();
-	set_auto_allocs(am);
+	am->next = auto_mem_thread_local;
+	auto_mem_thread_local = am;
 	return true;
 }
 
 void
 ECPGfree_auto_mem(void)
 {
-	struct auto_mem *am = get_auto_allocs();
-
 	/* free all memory we have allocated for the user */
-	if (am)
+	while (auto_mem_thread_local)
 	{
-		do
-		{
-			struct auto_mem *act = am;
-
-			am = am->next;
-			ecpg_free(act->pointer);
-			ecpg_free(act);
-		} while (am);
-		set_auto_allocs(NULL);
+		struct auto_mem *act = auto_mem_thread_local;
+		struct auto_mem *next = act->next;
+
+		ecpg_free(act->pointer);
+		ecpg_free(act);
+		auto_mem_thread_local = next;
 	}
 }
 
 void
 ecpg_clear_auto_mem(void)
-{
-	ecpg_clear_auto_mem_list(get_auto_allocs());
-}
-
-static void
-ecpg_clear_auto_mem_list(struct auto_mem *am)
 {
 	/* only free our own structure */
-	if (am)
+	while (auto_mem_thread_local)
 	{
-		do
-		{
-			struct auto_mem *act = am;
-
-			am = am->next;
-			ecpg_free(act);
-		} while (am);
-		set_auto_allocs(NULL);
+		struct auto_mem *act = auto_mem_thread_local;
+		struct auto_mem *next = act->next;
+
+		ecpg_free(act);
+		auto_mem_thread_local = next;
 	}
 }
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
index 354a2e731fc..dbe1c919822 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,13 @@ static struct sqlca_t sqlca_init =
 	}
 };
 
-static pthread_key_t sqlca_key;
-static pthread_once_t sqlca_key_once = PTHREAD_ONCE_INIT;
+static thread_local struct sqlca_t *sqlca_thread_local;
 
-static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t debug_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pg_thrd_atexit_t ecpg_on_thread_exit_id;
+static bool ecpg_on_thread_exit_initialized;
+
+static pg_mtx_t debug_mutex = PG_MTX_INIT;
+static pg_mtx_t debug_init_mutex = PG_MTX_INIT;
 static volatile int simple_debug = 0;
 static FILE *debugstream = NULL;
 
@@ -93,34 +95,64 @@ ecpg_init(const struct connection *con, const char *connection_name, const int l
 }
 
 static void
-ecpg_sqlca_key_destructor(void *arg)
+ecpg_on_thread_exit(void)
 {
-	free(arg);					/* sqlca structure allocated in ECPGget_sqlca */
+	/* Clean up thread-local data in memory.c. */
+	ecpg_clear_auto_mem();
+
+	/* Clean up thread-local data in descriptor.h. */
+	ecpg_descriptor_on_thread_exit();
+
+	/* Clean up thread-local SQL state object. */
+	if (sqlca_thread_local)
+		free(sqlca_thread_local);
 }
 
-static void
-ecpg_sqlca_key_init(void)
+static bool
+ecpg_on_thread_exit_initialize(void)
 {
-	pthread_key_create(&sqlca_key, ecpg_sqlca_key_destructor);
+	bool		result = false;
+	static pg_mtx_t mutex = PG_MTX_INIT;
+
+	/*
+	 * This is only reached when sqlca_thread_local is not already set, ie
+	 * once per thread (unless it failed last time and we're trying again), so
+	 * keep things simple with a big lock.
+	 */
+	pg_mtx_lock(&mutex);
+	if (ecpg_on_thread_exit_initialized)
+	{
+		result = true;
+	}
+	else if (pg_thrd_atexit_create(&ecpg_on_thread_exit_id) == pg_thrd_success)
+	{
+		result = true;
+		ecpg_on_thread_exit_initialized = true;
+	}
+	pg_mtx_unlock(&mutex);
+
+	/* Make sure the cleanup function is enabled in this thread. */
+	if (result)
+		pg_thrd_atexit_enable(ecpg_on_thread_exit_id, ecpg_on_thread_exit);
+
+	return result;
 }
 
 struct sqlca_t *
 ECPGget_sqlca(void)
 {
-	struct sqlca_t *sqlca;
+	if (sqlca_thread_local)
+		return sqlca_thread_local;
 
-	pthread_once(&sqlca_key_once, ecpg_sqlca_key_init);
+	if (!ecpg_on_thread_exit_initialize())
+		return NULL;
 
-	sqlca = pthread_getspecific(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);
-	}
-	return sqlca;
+	sqlca_thread_local = malloc(sizeof(struct sqlca_t));
+	if (sqlca_thread_local == NULL)
+		return NULL;
+	ecpg_init_sqlca(sqlca_thread_local);
+
+	return sqlca_thread_local;
 }
 
 bool
@@ -204,10 +236,10 @@ void
 ECPGdebug(int n, FILE *dbgs)
 {
 	/* Interlock against concurrent executions of ECPGdebug() */
-	pthread_mutex_lock(&debug_init_mutex);
+	pg_mtx_lock(&debug_init_mutex);
 
 	/* Prevent ecpg_log() from printing while we change settings */
-	pthread_mutex_lock(&debug_mutex);
+	pg_mtx_lock(&debug_mutex);
 
 	if (n > 100)
 	{
@@ -220,12 +252,12 @@ ECPGdebug(int n, FILE *dbgs)
 	debugstream = dbgs;
 
 	/* We must release debug_mutex before invoking ecpg_log() ... */
-	pthread_mutex_unlock(&debug_mutex);
+	pg_mtx_unlock(&debug_mutex);
 
 	/* ... but keep holding debug_init_mutex to avoid racy printout */
 	ecpg_log("ECPGdebug: set to %d\n", simple_debug);
 
-	pthread_mutex_unlock(&debug_init_mutex);
+	pg_mtx_unlock(&debug_init_mutex);
 }
 
 void
@@ -264,7 +296,7 @@ ecpg_log(const char *format, ...)
 
 	sqlca = ECPGget_sqlca();
 
-	pthread_mutex_lock(&debug_mutex);
+	pg_mtx_lock(&debug_mutex);
 
 	/* Now that we hold the mutex, recheck simple_debug */
 	if (simple_debug)
@@ -283,7 +315,7 @@ ecpg_log(const char *format, ...)
 		fflush(debugstream);
 	}
 
-	pthread_mutex_unlock(&debug_mutex);
+	pg_mtx_unlock(&debug_mutex);
 
 	free(fmt);
 }
@@ -424,60 +456,6 @@ ECPGis_noind_null(enum ECPGttype type, const void *ptr)
 	return false;
 }
 
-#ifdef WIN32
-
-int
-pthread_mutex_init(pthread_mutex_t *mp, void *attr)
-{
-	mp->initstate = 0;
-	return 0;
-}
-
-int
-pthread_mutex_lock(pthread_mutex_t *mp)
-{
-	/* Initialize the csection if not already done */
-	if (mp->initstate != 1)
-	{
-		LONG		istate;
-
-		while ((istate = InterlockedExchange(&mp->initstate, 2)) == 2)
-			Sleep(0);			/* wait, another thread is doing this */
-		if (istate != 1)
-			InitializeCriticalSection(&mp->csection);
-		InterlockedExchange(&mp->initstate, 1);
-	}
-	EnterCriticalSection(&mp->csection);
-	return 0;
-}
-
-int
-pthread_mutex_unlock(pthread_mutex_t *mp)
-{
-	if (mp->initstate != 1)
-		return EINVAL;
-	LeaveCriticalSection(&mp->csection);
-	return 0;
-}
-
-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 *
@@ -491,7 +469,7 @@ ecpg_gettext(const char *msgid)
 	 * might as well do it the same way everywhere.
 	 */
 	static volatile bool already_bound = false;
-	static pthread_mutex_t binddomain_mutex = PTHREAD_MUTEX_INITIALIZER;
+	static pg_mtx_t binddomain_mutex = PG_MTX_INIT;
 
 	if (!already_bound)
 	{
@@ -502,7 +480,7 @@ ecpg_gettext(const char *msgid)
 		int			save_errno = errno;
 #endif
 
-		(void) pthread_mutex_lock(&binddomain_mutex);
+		pg_mtx_lock(&binddomain_mutex);
 
 		if (!already_bound)
 		{
@@ -519,7 +497,7 @@ ecpg_gettext(const char *msgid)
 			already_bound = true;
 		}
 
-		(void) pthread_mutex_unlock(&binddomain_mutex);
+		pg_mtx_unlock(&binddomain_mutex);
 
 #ifdef WIN32
 		SetLastError(save_errno);
diff --git a/src/interfaces/ecpg/ecpglib/sqlda.c b/src/interfaces/ecpg/ecpglib/sqlda.c
index 081e32666f6..97914299111 100644
--- a/src/interfaces/ecpg/ecpglib/sqlda.c
+++ b/src/interfaces/ecpg/ecpglib/sqlda.c
@@ -11,11 +11,11 @@
 
 #include "catalog/pg_type_d.h"
 #include "decimal.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 "sqlca.h"
 #include "sqlda-compat.h"
 #include "sqlda-native.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 7b6ba46b349..00000000000
--- a/src/interfaces/ecpg/include/ecpg-pthread-win32.h
+++ /dev/null
@@ -1,49 +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
-{
-	/* initstate = 0: not initialized; 1: init done; 2: init in progress */
-	LONG		initstate;
-	CRITICAL_SECTION csection;
-} pthread_mutex_t;
-
-typedef DWORD pthread_key_t;
-typedef bool pthread_once_t;
-
-#define PTHREAD_MUTEX_INITIALIZER	{ 0 }
-#define PTHREAD_ONCE_INIT			false
-
-int			pthread_mutex_init(pthread_mutex_t *, void *attr);
-int			pthread_mutex_lock(pthread_mutex_t *);
-int			pthread_mutex_unlock(pthread_mutex_t *);
-
-void		win32_pthread_once(volatile pthread_once_t *once, void (*fn) (void));
-
-#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/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h
index 1d971d5893d..ea87d343d9d 100644
--- a/src/interfaces/ecpg/include/ecpglib.h
+++ b/src/interfaces/ecpg/include/ecpglib.h
@@ -70,8 +70,6 @@ void	   *ECPGget_var(int number);
 /* dynamic result allocation */
 void		ECPGfree_auto_mem(void);
 
-void		ecpg_pthreads_init(void);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/interfaces/ecpg/test/expected/thread-alloc.c b/src/interfaces/ecpg/test/expected/thread-alloc.c
index 912d94c134c..5225967db44 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(void)
 {
 	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 f9322b3e9bb..37630d39b8a 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(void)
 {
 	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 4fb19e3fd1c..96e30e9e680 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(void)
 {
 	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 71da6f2e07d..5a981b23ba7 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(void)
 {
-#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(void)
 
   /* 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(void)
       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(void)
   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 259fc09dad6..588e1c2ef31 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(void)
 {
-#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(void)
 
   /* 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(void)
       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(void)
   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 39faa6dc2ef..92097866ba4 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(void)
 {
 	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 b1485ac2a5c..1fd41dd666f 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(void)
 {
 	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 a389a6ae39e..c4119a236a8 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(void)
 {
 	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(void)
 	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 36ff91fe816..9e90ffc2643 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(void)
 {
-#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(void)
       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(void)
   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 711a8e26cc6..a0cbe07abc2 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(void)
 {
-#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(void)
       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(void)
   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;
 }
-- 
2.47.3

