v1-0002-port-Provide-minimal-pg_threads.h-API.patch
text/x-patch
Filename: v1-0002-port-Provide-minimal-pg_threads.h-API.patch
Type: text/x-patch
Part: 1
Message:
pg_threads.h take II
Patch
Format: format-patch
Series: patch v1-0002
Subject: port: Provide minimal pg_threads.h API.
| File | + | − |
|---|---|---|
| src/include/port/pg_threads.h | 628 | 0 |
| src/port/Makefile | 1 | 0 |
| src/port/meson.build | 1 | 0 |
| src/port/pg_threads.c | 203 | 0 |
| src/tools/pgindent/typedefs.list | 10 | 0 |
From a7f8d052ceeef060bd3830484bc19150bd08146c Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 24 Jun 2026 17:05:51 +1200
Subject: [PATCH v1 02/14] port: Provide minimal pg_threads.h API.
This is based on a subset of the C11 <threads.h> API. Identifiers carry
a pg_ prefix and a few extra facilities are added:
* PG_MTX_INIT macro for static mutex initialization
* pg_rwlock_t for read/write locks
* pg_barrier for thread synchronization (needed by pgbench)
With the above exceptions, this API could eventually be redirected to
C11 <threads.h> facilities instead of POSIX/Windows, but support is not
yet available on all systems we target.
The C11 "thread_local" macro is also defined in pg_threads.h by analogy
with <threads.h>, without a pg_ prefix. That could arguably live in c.h
instead, since it's a language feature that can be used independently of
<threads.h> library features (and was promoted to a built-in keyword in
C23, as in C++), but defining the macro in pg_threads.h makes it
available to ecpg code that doesn't include c.h.
Later commits will make use of this header as a centralized replacement
for several module-local thread API abstractions.
Discussion:
Reviewed-by:
---
src/include/port/pg_threads.h | 628 +++++++++++++++++++++++++++++++
src/port/Makefile | 1 +
src/port/meson.build | 1 +
src/port/pg_threads.c | 203 ++++++++++
src/tools/pgindent/typedefs.list | 10 +
5 files changed, 843 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 00000000000..5afc94156d8
--- /dev/null
+++ b/src/include/port/pg_threads.h
@@ -0,0 +1,628 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_threads.h
+ * Portable multi-threading API.
+ *
+ * A multi-threading API abstraction based on a subset of C11 <threads.h>,
+ * with some extensions. The identifiers have a pg_ prefix but otherwise
+ * follow C11 naming.
+ *
+ * Extensions not present in C11:
+ *
+ * - pg_mtx_t has initialization value PG_MTX_INIT
+ * - pg_rwlock_t for read/write locks
+ * - pg_barrier_t for synchronization (like pthread_barrier_t)
+ * - pg_thrd_atexit_t
+ *
+ * On POSIX systems, clients can assume that this includes <pthread.h>, that
+ * pthread_t is the same type as pg_thrd_t (mirroring POSIX's requirement that
+ * thrd_t is pthread_t), and that pthread_sigmask() is available.
+ *
+ * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/include/port/pg_threads.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PG_THREADS_H
+#define PG_THREADS_H
+
+#if defined(WIN32)
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Thread-local storage.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * In C23 and C++11, thread_local is a storage class keyword so we don't need
+ * to do anything to use it.
+ *
+ * In C11, _Thread_local is the keyword and <thread.h> is supposed to define
+ * thread_local as a macro. Do the same here if necessary, since some C11
+ * systems don't have that header yet.
+ */
+#if !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) && !defined(__cplusplus)
+#ifndef thread_local
+#define thread_local _Thread_local
+#endif
+#endif
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Return values.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* Like C11 thrd_error_t. */
+typedef enum pg_thrd_error_t
+{
+ pg_thrd_success = 0,
+ pg_thrd_nomem = 1,
+ pg_thrd_timedout = 2,
+ pg_thrd_busy = 3,
+ pg_thrd_error = 4,
+} pg_thrd_error_t;
+
+/* Convert native error to pg_thrd_error_t. */
+static inline int
+pg_thrd_maperror(int error)
+{
+#ifdef WIN32
+ return error ? pg_thrd_success : pg_thrd_error;
+#else
+ return error == 0 ? pg_thrd_success : pg_thrd_error;
+#endif
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Threads.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* Like C11 thrd_t. Uses native thread handle directly. */
+#ifdef WIN32
+typedef HANDLE pg_thrd_t;
+#else
+typedef pthread_t pg_thrd_t;
+#endif
+
+/* Like C11 thrd_start_t. */
+typedef int (*pg_thrd_start_t) (void *);
+
+/* Like C11 thrd_create(). */
+extern int pg_thrd_create(pg_thrd_t *thread, pg_thrd_start_t function, void *argument);
+
+/* Like C11 thrd_join(). */
+extern int pg_thrd_join(pg_thrd_t thread, int *result);
+
+#ifdef WIN32
+/* Helper for Windows, whose threads don't know their own handle. */
+extern pg_thrd_t pg_thrd_current_win32(void);
+#endif
+
+/* Like C11 thrd_current(). */
+static inline pg_thrd_t
+pg_thrd_current(void)
+{
+#ifdef WIN32
+ return pg_thrd_current_win32();
+#else
+ return pthread_self();
+#endif
+}
+
+/* Like C11 thrd_equal(). Return value zero means not equal. */
+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
+}
+
+/* Like C11 thrd_exit(). */
+static inline void
+pg_thrd_exit(int result)
+{
+#ifdef WIN32
+ ExitThread((DWORD) result);
+#else
+ pthread_exit((void *) (intptr_t) result);
+#endif
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * User-supplied callback on thread exit. Not in C11.
+ *
+ * This is built on top of OS-supplied "explicit" thread local storage APIs,
+ * and works even for threads not created by pg_threads.h.
+ *
+ * XXX We could also take a user data pointer, but we'd have to allocate a
+ * small object to hold the function pointer + data. Not currently needed.
+ *
+ * XXX We could provide a more general tss_t API with per-thread-local-slot
+ * destructor support if there is a real need, but language-supported
+ * "thread_local" is generally much easier to use, and this narrow abstraction
+ * avoids some portability complications.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* A thread-exit callback ID. */
+#ifdef WIN32
+typedef DWORD pg_thrd_atexit_t;
+#else
+typedef pthread_key_t pg_thrd_atexit_t;
+#endif
+
+/* The type of user-supplied thread-exit functions. */
+typedef void (*pg_thrd_atexit_function_t) (void);
+
+/* Windows' FLS callback needs non-default calling convention. */
+#ifdef WIN32
+#define PG_THRD_ATEXIT_INVOKE_CC CALLBACK
+#else
+#define PG_THRD_ATEXIT_INVOKE_CC
+#endif
+
+/* Function called by OS on thread exit with per-thread user function as value. */
+extern void PG_THRD_ATEXIT_INVOKE_CC pg_thrd_atexit_invoke(void *value);
+
+/*
+ * Create a process-wide thread-exit callback ID.
+ *
+ * XXX In theory we might also want pg_thrd_atexit_destroy(), but we'd have to
+ * come up with a way to stop Windows from calling pg_thrd_atexit_invoke()
+ * once for each thread, from the calling thread.
+ */
+static inline pg_thrd_error_t
+pg_thrd_atexit_create(pg_thrd_atexit_t *atexit_id)
+{
+#ifdef WIN32
+ if ((*atexit_id = FlsAlloc(pg_thrd_atexit_invoke)) == FLS_OUT_OF_INDEXES)
+ return pg_thrd_error;
+#else
+ if (pthread_key_create(atexit_id, pg_thrd_atexit_invoke) != 0)
+ return pg_thrd_error;
+#endif
+ return pg_thrd_success;
+}
+
+/*
+ * Set the thread-exit callback function for a given ID in the calling thread,
+ * replacing any previously installed callback for the same ID in this thread.
+ * The ID must have been created by any thread in the same process with
+ * pg_thrd_atexit_create() first.
+ */
+static inline void
+pg_thrd_atexit_enable(pg_thrd_atexit_t atexit_id,
+ pg_thrd_atexit_function_t function)
+{
+#ifdef WIN32
+ FlsSetValue(atexit_id, function);
+#else
+ pthread_setspecific(atexit_id, (const void *) function);
+#endif
+}
+
+/*
+ * Remove the thread-exit callback function for a given ID in the calling
+ * thread.
+ */
+static inline void
+pg_thrd_atexit_disable(pg_thrd_atexit_t atexit_id)
+{
+ pg_thrd_atexit_enable(atexit_id, NULL);
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * "Once"-style initialization functions.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* Like C11 once_flag and ONCE_FLAG_INIT. */
+#ifdef WIN32
+typedef INIT_ONCE pg_once_flag;
+#define PG_ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT
+#else
+typedef pthread_once_t pg_once_flag;
+#define PG_ONCE_FLAG_INIT PTHREAD_ONCE_INIT
+#endif
+
+/* Like C11 once_function_t. */
+typedef void (*pg_call_once_function_t) (void);
+
+#ifdef WIN32
+/* Windows helper that deals with function type mismatch. */
+extern BOOL CALLBACK pg_call_once_invoke(pg_once_flag *flag,
+ void *parameter,
+ void **context);
+#endif
+
+/* Like C11 call_once(). */
+static inline void
+pg_call_once(pg_once_flag *flag, pg_call_once_function_t function)
+{
+#ifdef WIN32
+ InitOnceExecuteOnce(flag, pg_call_once_invoke, function, NULL);
+#else
+ pthread_once(flag, function);
+#endif
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Read/write locks. Not in C11.
+ *
+ * Unfortunately Windows makes you say whether you're unlocking a read lock or
+ * a write lock, so we have to expose that here too. POSIX already knows.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef WIN32
+typedef SRWLOCK pg_rwlock_t;
+#define PG_RWLOCK_INIT SRWLOCK_INIT
+#else
+typedef pthread_rwlock_t pg_rwlock_t;
+#define PG_RWLOCK_INIT PTHREAD_RWLOCK_INITIALIZER
+#endif
+
+static inline int
+pg_rwlock_init(pg_rwlock_t *lock)
+{
+#ifdef WIN32
+ InitializeSRWLock(lock);
+ return pg_thrd_success;
+#else
+ return pg_thrd_maperror(pthread_rwlock_init(lock, NULL));
+#endif
+}
+
+static inline int
+pg_rwlock_rlock(pg_rwlock_t *lock)
+{
+#ifdef WIN32
+ AcquireSRWLockShared(lock);
+ return pg_thrd_success;
+#else
+ return pg_thrd_maperror(pthread_rwlock_rdlock(lock));
+#endif
+}
+
+static inline int
+pg_rwlock_wlock(pg_rwlock_t *lock)
+{
+#ifdef WIN32
+ AcquireSRWLockExclusive(lock);
+ return pg_thrd_success;
+#else
+ return pg_thrd_maperror(pthread_rwlock_wrlock(lock));
+#endif
+}
+
+static inline int
+pg_rwlock_wunlock(pg_rwlock_t *lock)
+{
+#ifdef WIN32
+ ReleaseSRWLockExclusive(lock);
+ return pg_thrd_success;
+#else
+ return pg_thrd_maperror(pthread_rwlock_unlock(lock));
+#endif
+}
+
+static inline int
+pg_rwlock_runlock(pg_rwlock_t *lock)
+{
+#ifdef WIN32
+ ReleaseSRWLockShared(lock);
+ return pg_thrd_success;
+#else
+ return pg_thrd_maperror(pthread_rwlock_unlock(lock));
+#endif
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Simple mutexes.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * C11 doesn't define a static initializer, but it is very convenient to have
+ * one.
+ */
+#ifdef WIN32
+/*
+ * CRITICAL_SECTION might be the most obvious Windows mechanism for pg_mtx_t,
+ * but SRWLock is reported to be at least as fast when used only in exclusive
+ * mode, and has the advantage of a static initializer (CRITICAL_SECTION must
+ * be initialized and destroyed explicitly because it allocates kernel
+ * resource). So we'll just point pg_mtx_t to pg_rwlock_t on Windows. (Visual
+ * Studio 2022's <threads.h> takes the same approach.)
+ */
+typedef pg_rwlock_t pg_mtx_t;
+#define PG_MTX_INIT PG_RWLOCK_INIT
+#else
+typedef pthread_mutex_t pg_mtx_t;
+#define PG_MTX_INIT PTHREAD_MUTEX_INITIALIZER
+#endif
+
+/* Like C11 mtx_type_t. */
+typedef enum pg_mtx_type_t
+{
+ pg_mtx_plain = 0
+} pg_mtx_type_t;
+
+/* Like C11 mtx_init(). */
+static inline int
+pg_mtx_init(pg_mtx_t *mutex, int type)
+{
+#ifdef WIN32
+ return pg_rwlock_init(mutex);
+#else
+ return pg_thrd_maperror(pthread_mutex_init(mutex, NULL));
+#endif
+}
+
+/* Like C11 mtx_lock(). */
+static inline int
+pg_mtx_lock(pg_mtx_t *mutex)
+{
+#ifdef WIN32
+ return pg_rwlock_wlock(mutex);
+#else
+ return pg_thrd_maperror(pthread_mutex_lock(mutex));
+#endif
+}
+
+/* Like C11 mtx_unlock(). */
+static inline int
+pg_mtx_unlock(pg_mtx_t *mutex)
+{
+#ifdef WIN32
+ return pg_rwlock_wunlock(mutex);
+#else
+ return pg_thrd_maperror(pthread_mutex_unlock(mutex));
+#endif
+}
+
+/* Like C11 mtx_destroy(). */
+static inline int
+pg_mtx_destroy(pg_mtx_t *mutex)
+{
+#ifdef WIN32
+ return pg_thrd_success;
+#else
+ return pg_thrd_maperror(pthread_mutex_destroy(mutex));
+#endif
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Condition variables.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* Like C11 cnd_t. */
+#ifdef WIN32
+typedef CONDITION_VARIABLE pg_cnd_t;
+#else
+typedef pthread_cond_t pg_cnd_t;
+#endif
+
+/* Like C11 cnd_init(). */
+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
+}
+
+/* Like C11 cnd_broadcast(). */
+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
+}
+
+/* Like C11 cnd_wait(). */
+static inline int
+pg_cnd_wait(pg_cnd_t *condvar, pg_mtx_t *mutex)
+{
+#ifdef WIN32
+ SleepConditionVariableSRW(condvar, mutex, INFINITE, 0);
+ return pg_thrd_success;
+#else
+ return pg_thrd_maperror(pthread_cond_wait(condvar, mutex));
+#endif
+}
+
+/* Like C11 cnd_destroy(). */
+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
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Barriers. Not in C11.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * Define USE_PG_BARRIER_FALLBACK manually to test the fallback code. It is
+ * normally only used on macOS.
+ */
+#if !defined(WIN32) && !defined(HAVE_PTHREAD_BARRIER_WAIT)
+#define USE_PG_BARRIER_FALLBACK
+#endif
+
+/* A thread synchronization barrier. */
+#ifdef USE_PG_BARRIER_FALLBACK
+typedef struct pg_barrier_t
+{
+ bool sense;
+ int expected;
+ int arrived;
+ pg_mtx_t mutex;
+ pg_cnd_t cond;
+} pg_barrier_t;
+#elif defined(WIN32)
+typedef SYNCHRONIZATION_BARRIER pg_barrier_t;
+#else
+typedef pthread_barrier_t pg_barrier_t;
+#endif
+
+/*
+ * Initialize a thread synchronization barrier that waits for 'count' threads
+ * when pg_barrier_wait() is called.
+ */
+static inline int
+pg_barrier_init(pg_barrier_t *barrier, int count)
+{
+#ifdef USE_PG_BARRIER_FALLBACK
+ barrier->sense = false;
+ barrier->expected = count;
+ barrier->arrived = 0;
+ if (pg_cnd_init(&barrier->cond) != pg_thrd_success)
+ return pg_thrd_error;
+ if (pg_mtx_init(&barrier->mutex, pg_mtx_plain) != pg_thrd_success)
+ {
+ pg_cnd_destroy(&barrier->cond);
+ return pg_thrd_error;
+ }
+ return pg_thrd_success;
+#elif defined(WIN32)
+ return pg_thrd_maperror(InitializeSynchronizationBarrier(barrier, count, 0));
+#else
+ return pg_thrd_maperror(pthread_barrier_init(barrier, NULL, count));
+#endif
+}
+
+/*
+ * Wait for all expected threads to arrive at the barrier, and elect one
+ * arbitrary thread to perform a phase of computation serially. Sets
+ * *elected_thread to true in the elected thread, and false in all others.
+ */
+static inline int
+pg_barrier_wait_and_elect(pg_barrier_t *barrier, bool *elected_thread)
+{
+#ifdef USE_PG_BARRIER_FALLBACK
+ bool initial_sense;
+
+ pg_mtx_lock(&barrier->mutex);
+ barrier->arrived++;
+ if (barrier->arrived == barrier->expected)
+ {
+ barrier->arrived = 0;
+ barrier->sense = !barrier->sense;
+ pg_mtx_unlock(&barrier->mutex);
+ pg_cnd_broadcast(&barrier->cond);
+ *elected_thread = true;
+ return pg_thrd_success;
+ }
+ initial_sense = barrier->sense;
+ do
+ {
+ pg_cnd_wait(&barrier->cond, &barrier->mutex);
+ } while (barrier->sense == initial_sense);
+ pg_mtx_unlock(&barrier->mutex);
+ *elected_thread = false;
+ return pg_thrd_success;
+#elif defined(WIN32)
+ if (EnterSynchronizationBarrier(barrier,
+ SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY))
+ *elected_thread = true;
+ else
+ *elected_thread = false;
+ return pg_thrd_success;
+#else
+ int error = pthread_barrier_wait(barrier);
+
+ if (error == 0)
+ {
+ *elected_thread = false;
+ return pg_thrd_success;
+ }
+ else if (error == PTHREAD_BARRIER_SERIAL_THREAD)
+ {
+ *elected_thread = true;
+ return pg_thrd_success;
+ }
+ else
+ {
+ return pg_thrd_error;
+ }
+#endif
+}
+
+/* Wait for all threads to arrive at the barrier. */
+static inline int
+pg_barrier_wait(pg_barrier_t *barrier)
+{
+ bool elected_thread;
+
+ return pg_barrier_wait_and_elect(barrier, &elected_thread);
+}
+
+/* Destroy a barrier. */
+static inline int
+pg_barrier_destroy(pg_barrier_t *barrier)
+{
+#ifdef USE_PG_BARRIER_FALLBACK
+ pg_mtx_destroy(&barrier->mutex);
+ pg_cnd_destroy(&barrier->cond);
+ return pg_thrd_success;
+#elif defined(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 7e9b5877652..29734bb0d35 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -51,6 +51,7 @@ OBJS = \
pg_popcount_aarch64.o \
pg_popcount_x86.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 922b3f64676..a6ea79d3421 100644
--- a/src/port/meson.build
+++ b/src/port/meson.build
@@ -14,6 +14,7 @@ pgport_sources = [
'pg_popcount_aarch64.c',
'pg_popcount_x86.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 00000000000..edf13471001
--- /dev/null
+++ b/src/port/pg_threads.c
@@ -0,0 +1,203 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_threads.c
+ * Out-of-line parts of portable multi-threading API.
+ *
+ * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/port/pg_threads.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+#include "port/pg_threads.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Threads.
+ *
+ * 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_start_info
+{
+ pg_thrd_start_t function;
+ void *argument;
+
+#ifdef WIN32
+ /* Space to pass the thread's handle, for use by pg_thrd_current(). */
+ pg_thrd_t self;
+#endif
+} pg_thrd_start_info;
+
+#ifdef WIN32
+static thread_local pg_thrd_t my_thrd_handle;
+#endif
+
+/*
+ * A trampoline function, to handle calling convention and parameter
+ * variations in the native APIs.
+ */
+#ifdef WIN32
+static DWORD __stdcall
+pg_thrd_invoke(void *start_info_v)
+#else
+static void *
+pg_thrd_invoke(void *start_info_v)
+#endif
+{
+ pg_thrd_start_info *start_info = start_info_v;
+ pg_thrd_start_t function = start_info->function;
+ void *argument = start_info->argument;
+ int result;
+
+#ifdef WIN32
+
+ /*
+ * Retrieve handle passed here by pg_thrd_create() before allowing this
+ * thread to run. (pg_thrd_current() can't use CurrentThread(), because
+ * that returns a pseudo-handle with the same value in all threads.)
+ */
+ Assert(start_info->self);
+ my_thrd_handle = start_info->self;
+#endif
+
+ free(start_info);
+
+ 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_start_info *start_info;
+
+ start_info = malloc(sizeof(*start_info));
+ if (start_info == NULL)
+ return pg_thrd_nomem;
+ start_info->function = function;
+ start_info->argument = argument;
+
+#ifdef WIN32
+ *thread = CreateThread(NULL, 0, pg_thrd_invoke, start_info,
+ CREATE_SUSPENDED, 0);
+ if (*thread != NULL)
+ {
+ /*
+ * Give the thread its own handle so that pg_thrd_current() works,
+ * before it is allowed to start running.
+ */
+ start_info->self = *thread;
+ ResumeThread(*thread);
+
+ return pg_thrd_success;
+ }
+#else
+ if (pthread_create(thread, NULL, pg_thrd_invoke, start_info) == 0)
+ return pg_thrd_success;
+#endif
+
+ free(start_info);
+ 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;
+}
+
+#ifdef WIN32
+pg_thrd_t
+pg_thrd_current_win32(void)
+{
+ /*
+ * This function is in .c file to avoid potential cross-DLL complications
+ * if a load from thread_local is inlined.
+ */
+ return my_thrd_handle;
+}
+#endif
+
+
+/*-------------------------------------------------------------------------
+ *
+ * User-supplied callback on thread exit.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+void PG_THRD_ATEXIT_INVOKE_CC
+pg_thrd_atexit_invoke(void *value)
+{
+ pg_thrd_atexit_function_t function = value;
+
+ /*
+ * Shouldn't be called with a NULL value on POSIX, but it's not well
+ * documented on Windows so let's be paranoid.
+ */
+ if (function)
+ function();
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * "Once"-style initialization functions.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef WIN32
+/* Only needed on Windows, because POSIX has the right prototype already. */
+BOOL CALLBACK
+pg_call_once_invoke(pg_once_flag *flag, void *parameter, void **context)
+{
+ pg_call_once_function_t function = parameter;
+
+ function();
+ return true;
+}
+#endif
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index ffb413ab612..338e5578979 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -4025,11 +4025,13 @@ pgParameterStatus
pg_atomic_flag
pg_atomic_uint32
pg_atomic_uint64
+pg_barrier_t
pg_be_sasl_mech
pg_category_range
pg_checksum_context
pg_checksum_raw_context
pg_checksum_type
+pg_cnd_t
pg_compress_algorithm
pg_compress_specification
pg_conn_host
@@ -4054,13 +4056,17 @@ pg_local_to_utf_combined
pg_locale_t
pg_mb_radix_tree
pg_md5_ctx
+pg_mtx_t
+pg_mtx_type_t
pg_on_exit_callback
pg_plan_advice_advisor_hook
+pg_once_flag
pg_prng_state
pg_re_flags
pg_regex_t
pg_regmatch_t
pg_regoff_t
+pg_rwlock_t
pg_saslprep_rc
pg_saslprep_test_context
pg_sha1_ctx
@@ -4073,6 +4079,10 @@ pg_snapshot
pg_special_case
pg_stack_base_t
pg_ternary
+pg_thrd_atexit_t
+pg_thrd_error_t
+pg_thrd_start_info
+pg_thrd_t
pg_time_t
pg_time_usec_t
pg_tz
--
2.47.3