pg_threads.h take II

Started by Thomas Munro3 months ago10 messageshackers
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

won't retrysuccessCI history

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t252998
psql -h localhost -U postgres

Built from patchset v9 (message #9), August 20, 2026 at 08:16 AM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t252998_9 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t252998_9 && git checkout t252998_9

Patchset v9 (message #9) is on t252998_9

Jump to latest
#1Thomas Munro
thomas.munro@gmail.com

Hi,

Here's another go at port/pg_threads.h, a C11 <threads.h>-like
interface, with some patches to use it (some just sketch-quality).
Previous work:

/messages/by-id/CA+hUKGLtmexrpMtxBRLCVePqV_dtWG-ZsEbyPrYc+NBB2TkNsw@mail.gmail.com

We still can't require C11 <threads.h> yet, though the interface has a
clear future upgrade/remap path to do that eventually. Since I last
wrote about this topic, we gained the ability to count on C11
thread_local being available (it's a language/compiler feature and
doesn't need a <threads.h> header or library), and partly because of
that, but also because I don't think anyone really likes it, I ripped
out that tss_t stuff. I came up with a narrower API to get
thread-exit cleanup callbacks if you need them for your thread_local
resources, same pthread_key_t/FlsAlloc stuff underneath it but without
having to get lost in the weeds of <threads.h> conformance.

v1-0001-ecpg-Fix-auto_mem-cleanup-on-thread-exit.patch

Already discussed in another thread, a couple of existing ECPG
thread-exit cleanup bugs need to be fixed (bugs that cancel each other
out in master, but leak, and with my patches the leak was fixed in
passing but then a double-free would crash):

/messages/by-id/CA+hUKGLv202ndx=kP1fZJapNcbfnpq-rezHNyt+cQ4pdxr1akg@mail.gmail.com

v1-0002-port-Provide-minimal-pg_threads.h-API.patch

The main thing I'm wondering about is how to make the error reporting
a bit nicer. Neither pthreads nor C11 threads set errno, but it's
nice if you can use %m. Hmm.

v1-0003-port-Use-pg_threads.h-API-for-pg_localconv_r.patch

Trivial.

v1-0004-ecpg-Improve-variable-name.patch
v1-0005-ecpg-Use-pg_threads.h.patch

These demonstrate the replacement of pthread_key_t with thread_local,
and where needed, pg_thrd_atexit_t. That's the narrower thing I came
up with to replace pthread_t/tss_t with something that more directly
models what we want, I think.

I could do pg_tss_t, but it takes a lot of extra book-keeping code to
make Windows FlsAlloc match the spec.

v1-0006-pgbench-Use-pg_threads.h.patch

Kills another local thread-porting abstraction.

v1-0007-libpq-Use-pg_threads.h.patch

Kills another local thread-porting abstraction.

v1-0008-pg_basebackup-Use-pg_threads.h.patch

Kills the fork() mode used for Unix, harmonises Unix and Windows.

v1-0009-pg_dump-Remove-TerminateThread-call.patch

This one already has a discussion thread (I figured it might be
another back-patchable issue, IDK), and it looks like the solution I
used here isn't what we want to go with for the future, but to do it
the way Heikki and Jelte are suggesting will probably require deciding
how to access atomics or at least compiler barrires in front-end code,
which I'll look into this week.

Consider this approach a stand-in for now, as I needed to get rid of a
TerminateThread() call somehow. We can't have that in pg_threads.h
for various reasons (it's all kinds of undefined).

/messages/by-id/CA+hUKGJgO=o-vLFahGdR2WesuX3h1-0j=a8z72fChc-MG1Hveg@mail.gmail.com

v1-0010-pg_dump-Block-signals-during-signal-handler.patch

Not very interesting, I was just triggered by steampunk Unix
anachronisms in passing... an experimental patch further down
replaces all of this anyway.

v1-0011-pg_dump-Use-pg_threads.h-and-remove-fork-path.patch

Kills the fork() mode used for Unix, harmonises Unix and Windows.
After this patch, the cancellation stuff admittedly looks pretty
shaky, but I wanted to separate the fork()-ectomy from any larger
refactoring, so bear with me...

Next I was planning to rip out the pipes that fork()-mode needed and
use an in-memory work queue, and probably make a reusable pg_thrd_pool
instead of open coding it here, but it turned out that Bryan was
already way ahead of me there:

/messages/by-id/8c712d76-ecf7-4749-a6d8-dddc01f298ec@gmail.com

Luckily it looks like he started working at that end of the problem
and I started with this fork()-ectomy/harmonisation piece, so I
think/hope that we can fit those efforts together quite easily... I
will look at his patches this week.

v1-0012-pg_signal_processor-Infrastructure-for-cleanup.patch
v1-0013-fe_utils-Provide-cancel_set-for-fast-quit-paths.patch
v1-0014-pg_dump-Refactor-connection-cancel-management.patch

I considered lots of different ways to make pg_dump cleanup better,
and this is the approach that *seemed* to be roughly along the lines
that Jelte suggested he wanted for various reasons, over in the
TerminateThread() thread. Namely, make it so that Unix and Windows
both run your cleanup code on a thread, but also force Windows to
serialise the handlers, so that the semantics match and we can escape
from the constraints of async-signal-safe signal handlers. On Unix it
has a thread sitting there waiting for you to hit ^C, just looping
over sigwait() and calling handlers serially, that are then free to
use arbitrarily complex code before _exit() turns out the lights.

Warning/disclaimer: 0012-0014 are only experimental sketches trying
stuff out, and might not work that well in various ways, I just wanted
to get your take on this general direction. Could skin this in lots
of other ways too and since you guys are actively hacking on this area
you might already have another idea in mind or even patches?

After these patches, there are a few places left that use raw Windows
threading APIs, but they're places that are creating threads to solve
signaling and I/O problems that have better solutions, topics for
another email (but briefly I mean: the interrupts/latches refactoring
patch can kill "our" signal-emulation threads, and support for
overlapped I/O can allow multiplexing pipe I/O with latches without
intermediate data-pumping threads (cf Run::IPC which also has that
kind of Windows-hates-select() problem), and support for process-exit
WaitEvents can kill another helper thread, ... yada yada).

Attachments:

t252998_1
v1-0001-ecpg-Fix-auto_mem-cleanup-on-thread-exit.patchtext/x-patch; charset=US-ASCII; name=v1-0001-ecpg-Fix-auto_mem-cleanup-on-thread-exit.patchDownload+8-4
v1-0002-port-Provide-minimal-pg_threads.h-API.patchtext/x-patch; charset=US-ASCII; name=v1-0002-port-Provide-minimal-pg_threads.h-API.patchDownload+843-1
v1-0003-port-Use-pg_threads.h-API-for-pg_localconv_r.patchtext/x-patch; charset=US-ASCII; name=v1-0003-port-Use-pg_threads.h-API-for-pg_localconv_r.patchDownload+6-6
v1-0004-ecpg-Improve-variable-name.patchtext/x-patch; charset=US-ASCII; name=v1-0004-ecpg-Improve-variable-name.patchDownload+6-7
v1-0005-ecpg-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v1-0005-ecpg-Use-pg_threads.h.patchDownload+285-697
v1-0006-pgbench-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v1-0006-pgbench-Use-pg_threads.h.patchDownload+15-195
v1-0007-libpq-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v1-0007-libpq-Use-pg_threads.h.patchDownload+21-140
v1-0008-pg_basebackup-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v1-0008-pg_basebackup-Use-pg_threads.h.patchDownload+49-208
v1-0009-pg_dump-Remove-TerminateThread-call.patchtext/x-patch; charset=US-ASCII; name=v1-0009-pg_dump-Remove-TerminateThread-call.patchDownload+31-33
v1-0010-pg_dump-Block-signals-during-signal-handler.patchtext/x-patch; charset=US-ASCII; name=v1-0010-pg_dump-Block-signals-during-signal-handler.patchDownload+17-14
v1-0011-pg_dump-Use-pg_threads.h-and-remove-fork-path.patchtext/x-patch; charset=US-ASCII; name=v1-0011-pg_dump-Use-pg_threads.h-and-remove-fork-path.patchDownload+167-460
v1-0012-pg_signal_processor-Infrastructure-for-cleanup.patchtext/x-patch; charset=US-ASCII; name=v1-0012-pg_signal_processor-Infrastructure-for-cleanup.patchDownload+206-1
v1-0013-fe_utils-Provide-cancel_set-for-fast-quit-paths.patchtext/x-patch; charset=US-ASCII; name=v1-0013-fe_utils-Provide-cancel_set-for-fast-quit-paths.patchDownload+182-1
v1-0014-pg_dump-Refactor-connection-cancel-management.patchtext/x-patch; charset=US-ASCII; name=v1-0014-pg_dump-Refactor-connection-cancel-management.patchDownload+165-391
#2Peter Eisentraut
peter_e@gmx.net
In reply to: Thomas Munro (#1)
Re: pg_threads.h take II

On 06.07.26 14:09, Thomas Munro wrote:

We still can't require C11 <threads.h> yet, though the interface has a
clear future upgrade/remap path to do that eventually. Since I last
wrote about this topic, we gained the ability to count on C11
thread_local being available

We still support platforms/compilers where it's not available, but

/messages/by-id/2a965ac6-fa42-4054-bee0-b1618e7729d6@eisentraut.org

which is ready to be committed, will de-support those, so by the time
this patch set is ready, we'll be fine.

#3Thomas Munro
thomas.munro@gmail.com
In reply to: Thomas Munro (#1)
Re: pg_threads.h take II

On Tue, Jul 7, 2026 at 12:09 AM Thomas Munro <thomas.munro@gmail.com> wrote:

used here isn't what we want to go with for the future, but to do it
the way Heikki and Jelte are suggesting will probably require deciding
how to access atomics or at least compiler barrires in front-end code,
which I'll look into this week.

Correction: at least memory barriers

#4Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Thomas Munro (#1)
Re: pg_threads.h take II

On 06/07/2026 15:09, Thomas Munro wrote:

Hi,

Here's another go at port/pg_threads.h, a C11 <threads.h>-like
interface, with some patches to use it (some just sketch-quality).
Previous work:

/messages/by-id/CA+hUKGLtmexrpMtxBRLCVePqV_dtWG-ZsEbyPrYc+NBB2TkNsw@mail.gmail.com

We still can't require C11 <threads.h> yet, though the interface has a
clear future upgrade/remap path to do that eventually. Since I last
wrote about this topic, we gained the ability to count on C11
thread_local being available (it's a language/compiler feature and
doesn't need a <threads.h> header or library), and partly because of
that, but also because I don't think anyone really likes it, I ripped
out that tss_t stuff. I came up with a narrower API to get
thread-exit cleanup callbacks if you need them for your thread_local
resources, same pthread_key_t/FlsAlloc stuff underneath it but without
having to get lost in the weeds of <threads.h> conformance.

Nice!

I've now reviewed patches 0001-0007. I think they're ready to be
committed, with some small comments below:

v1-0002-port-Provide-minimal-pg_threads.h-API.patch

The main thing I'm wondering about is how to make the error reporting
a bit nicer. Neither pthreads nor C11 threads set errno, but it's
nice if you can use %m. Hmm.

Perhaps provide a function like:

void
set_errno_from_pg_thrd_error(int error)
{
switch((pg_thrd_error_t) error)
{
case pg_thrd_nomem:
errno = ENOMEM;
break;
case pg_thrd_busy:
errno = EBUSY;
break;
...
}
}

+/* 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
+}

The WIN32 version of this is confusing. There is only one caller of this
with WIN32:

+#elif defined(WIN32)
+	return pg_thrd_maperror(InitializeSynchronizationBarrier(barrier, count, 0));
+#else

So that's not wrong. But I'd suggest only defining pg_thrd_maperror()
with pthreads, and handling that one WIN32 caller directly without the
helper function. Maybe rename pg_thrd_maperror() to something like
map_pthread_retval_to_pg_thrd_error() or something.

Do the pthread functions return an errno on error? Currently, all errors
get squashed to pg_thrd_error, which is a little unfortunate.

+/* 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
+}

Since we only support plain mutexes, how about "Assert(type ==
pg_mtx_plan)" here?

+/*-------------------------------------------------------------------------
+ *
+ * Barriers.  Not in C11.
+ *
+ *-------------------------------------------------------------------------
+ */

I wonder if we really need barriers. They're not that useful IMHO. I'd
tend to just open-code this directly with a mutex and the condition
variable in most cases. (Not a strong objection, there's little harm in
having it either)

+/*
+ * 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)
+{

No callers use the *elected_thread return value. What was the idea here?

+#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

Does that refer to the GetCurrentThreadId() function? We use that in a
few places currently.

How can it return the same value in all threads, isn't that completely
useless? And does that mean all our current uses of it are broken?

- Heikki

#5Bryan Green
dbryan.green@gmail.com
In reply to: Heikki Linnakangas (#4)
Re: pg_threads.h take II

On 7/6/26 3:48 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

On 06/07/2026 15:09, Thomas Munro wrote:

Hi,

Here's another go at port/pg_threads.h, a C11 <threads.h>-like
interface, with some patches to use it (some just sketch-quality).
Previous work:

/messages/by-id/flat/
CA%2BhUKGLtmexrpMtxBRLCVePqV_dtWG-ZsEbyPrYc%2BNBB2TkNsw%40mail.gmail.com

We still can't require C11 <threads.h> yet, though the interface has a
clear future upgrade/remap path to do that eventually.  Since I last
wrote about this topic, we gained the ability to count on C11
thread_local being available (it's a language/compiler feature and
doesn't need a <threads.h> header or library), and partly because of
that, but also because I don't think anyone really likes it, I ripped
out that tss_t stuff.  I came up with a narrower API to get
thread-exit cleanup callbacks if you need them for your thread_local
resources, same pthread_key_t/FlsAlloc stuff underneath it but without
having to get lost in the weeds of <threads.h> conformance.

Nice!

I've now reviewed patches 0001-0007. I think they're ready to be
committed, with some small comments below:

v1-0002-port-Provide-minimal-pg_threads.h-API.patch

The main thing I'm wondering about is how to make the error reporting
a bit nicer.  Neither pthreads nor C11 threads set errno, but it's
nice if you can use %m.  Hmm.

Perhaps provide a function like:

void
set_errno_from_pg_thrd_error(int error)
{
    switch((pg_thrd_error_t) error)
    {
        case pg_thrd_nomem:
            errno = ENOMEM;
            break;
        case pg_thrd_busy:
            errno = EBUSY;
            break;
       ...
    }
}

+/* 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
+}

The WIN32 version of this is confusing. There is only one caller of this
with WIN32:

+#elif defined(WIN32)
+    return pg_thrd_maperror(InitializeSynchronizationBarrier(barrier,
count, 0));
+#else

So that's not wrong. But I'd suggest only defining pg_thrd_maperror()
with pthreads, and handling that one WIN32 caller directly without the
helper function. Maybe rename pg_thrd_maperror() to something like
map_pthread_retval_to_pg_thrd_error() or something.

Do the pthread functions return an errno on error? Currently, all errors
get squashed to pg_thrd_error, which is a little unfortunate.

+/* 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
+}

Since we only support plain mutexes, how about "Assert(type ==
pg_mtx_plan)" here?

+/
*-------------------------------------------------------------------------
+ *
+ * Barriers.  Not in C11.
+ *
+
*-------------------------------------------------------------------------
+ */

I wonder if we really need barriers. They're not that useful IMHO. I'd
tend to just open-code this directly with a mutex and the condition
variable in most cases. (Not a strong objection, there's little harm in
having it either)

+/*
+ * 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)
+{

No callers use the *elected_thread return value. What was the idea here?

+#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

Does that refer to the GetCurrentThreadId() function? We use that in a
few places currently.

How can it return the same value in all threads, isn't that completely
useless? And does that mean all our current uses of it are broken?

- Heikki

GetCurrentThread() not GetCurrentThreadId()...  GetCurrentThread()
returns a special sentinel handle (-2) that when passed to another
Win32 function causes the kernel to resolve to whatever thread made
the call....

So, no-- not broken.

#6Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Bryan Green (#5)
Re: pg_threads.h take II

On 07/07/2026 00:01, dbryan.green@gmail.com wrote:

On 7/6/26 3:48 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

On 06/07/2026 15:09, Thomas Munro wrote:

+#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

Does that refer to the GetCurrentThreadId() function? We use that in a
few places currently.

How can it return the same value in all threads, isn't that completely
useless? And does that mean all our current uses of it are broken?

GetCurrentThread() not GetCurrentThreadId()...  GetCurrentThread()
returns a special sentinel handle (-2) that when passed to another
Win32 function causes the kernel to resolve to whatever thread made
the call....

So, no-- not broken.

Ah gotcha. Could we use GetCurrentThreadId() here then? (I have no
problem with the way it's currently done in the patch either though,
just curious)

- Heikki

#7Bryan Green
dbryan.green@gmail.com
In reply to: Heikki Linnakangas (#6)
Re: pg_threads.h take II

On 7/6/2026 4:13 PM, Heikki Linnakangas wrote:

On 07/07/2026 00:01, dbryan.green@gmail.com wrote:

On 7/6/26 3:48 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:

On 06/07/2026 15:09, Thomas Munro wrote:

+#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

Does that refer to the GetCurrentThreadId() function? We use that in a
few places currently.

How can it return the same value in all threads, isn't that completely
useless? And does that mean all our current uses of it are broken?

GetCurrentThread() not GetCurrentThreadId()...  GetCurrentThread()
returns a special sentinel handle (-2) that when passed to another
Win32 function causes the kernel to resolve to whatever thread made
the call....

So, no-- not broken.

Ah gotcha. Could we use GetCurrentThreadId() here then? (I have no
problem with the way it's currently done in the patch either though,
just curious)

- Heikki

I haven't looked at all of the code, but I would assume there is a
WaitFor...Object() somewhere and those only work with actual
handles...not id's. GetCurrentThreadId() returns a dword for id. I'll
look over the patches as well.

--
Bryan Green
EDB: https://www.enterprisedb.com

#8Thomas Munro
thomas.munro@gmail.com
In reply to: Bryan Green (#7)
Re: pg_threads.h take II

On Tue, Jul 7, 2026 at 8:48 AM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

Perhaps provide a function like:

void
set_errno_from_pg_thrd_error(int error)
{
switch((pg_thrd_error_t) error)
{
case pg_thrd_nomem:
errno = ENOMEM;
break;
case pg_thrd_busy:
errno = EBUSY;
break;
...
}
}

I thought a lot more about error reporting, standards conformance,
expected future evolution and maintainability, and now I have a more
developed version that tries harder to expose underlying error
conditions.

Since we only support plain mutexes, how about "Assert(type ==
pg_mtx_plan)" here?

It returns an error for bogus type values now, and also supports all
mutex types.

I wonder if we really need barriers. They're not that useful IMHO. I'd
tend to just open-code this directly with a mutex and the condition
variable in most cases. (Not a strong objection, there's little harm in
having it either)

It's just that pgbench is using them. If we boot them out of here
then it'll need to be defined there instead, and then either you won't
get the native one on most systems (which might be slightly better in
some way?), or native threading code will once again roam the tree...
I think this is OK like this, but:

No callers use the *elected_thread return value. What was the idea here?

I have removed that argument for now, as pgbench doesn't use it. I
just wanted to expose the pthread/Windows special result for that,
which involved making up names. One of the goals of following
<threads.h> was not to have to make up names for things so I'm happy
to remove that bit :-)

Speaking of making names up, I'm also trying the name
pg_thrd_barrier_t rather than pg_barrier_t, as that felt a little
vague/overloaded. (Someone suggested that but I've lost track of
who/where, sorry...)

On Tue, Jul 7, 2026 at 9:28 AM Bryan Green <dbryan.green@gmail.com> wrote:

I haven't looked at all of the code, but I would assume there is a
WaitFor...Object() somewhere and those only work with actual
handles...not id's. GetCurrentThreadId() returns a dword for id. I'll
look over the patches as well.

Yeah. And once I tackled thrd_detach() as part of my API completeness
drive, I realised we really need both. See details in patch.

Changes in this version:

* pg_threads.h has 100% of the <threads.h> API covered, and none of
our extensions.

* pg_threads_ext.h now has the (few) extra things that we want as of
today on top of that. Now it's much clearer when we're inventing
things and when we're following a standard to the letter.

* The per-platform implementations are now in separate files
pg_threads/map_pthread.h, .../map_threads.h and .../map_windows.h.

* That second implementation, for system-provided <threads.h> option,
is a new addition.

* Now that we require Visual Studio 2022, I think we might as well
actually use the <threads.h> implementation when we detect Visual
Studio? Technically it's 17.8 (not 17.0) that introduced it, but
that's out of vendor support. But if that's a problem, perhaps the
test could be not just defined(_MSC_VER) but also _MSC_VER >= 1938.
(See also question about associated DLL at end.)

* We still need map_windows.h for MinGW, either way. At some point it
will hopefully have <threads.h>, and then we could consider deleting
map_windows.h and most of pg_threads.c. That'd leave very little
code, as paper-thin <threads.h> and <pthread.h> mappings are mostly
just inline renaming wrappers + the small extensions.

* I think it's very useful to be able to test <threads.h> without
Windows, so I made it so that you can define PG_THREADS_USE_THREADS_H
manually. That's a little tricky because no standard tells us how to
define static initializers for cnd_t and mtx_t (though fortunately we
do have authoritative values for Windows). It's easy in practice on
at least Linux and FreeBSD though, so I tried to come up with enough
disclaimers and assertions to get away with that as a developer-only
option.

* I studied the Windows handle-vs-ID stuff a bit more and realised
that I'd actually solved a conformance problem that Visual Studio's
own <threads.h> suffers from. On the other hand, that has a runtime
cost, which I assume its authors declined to pay. Real programs
almost certainly don't care about that finer point anyway (namely
threads detaching themselves or passing their own thrd_t to another
thread for that thread to join). So now I just do it the same
non-conforming way as Visual Studio. I have left the code present as
an option guarded with PG_THRD_CURRENT_CONFORMING just to illustrate
what's going on here; unless anyone sees any reason not to, I'll
probably delete that code.

* I think I see why macOS hasn't given us <threads.h> yet: its
<pthread.h> mutexes don't support timeouts (the <unistd.h>
_POSIX_TIMEOUTS feature set). Those operations will simply fail. At
some point, Apple will presumably solve this problem and give us
<threads.h>, or if they don't, if we actually really want timeouts we
could build our own from lower level nuts and bolts and use that when
you ask for pg_mtx_timed, but for now pg_mtx_init(..., pg_mtx_timed)
and pg_mtx_timedwait() fail with an unsupported message and the macro
PG_MTX_TIMED_NOT_SUPPORTED is defined. I don't think it's much of a
problem in practice. pg_mtx_trylock() works everywhere, and is
probably more useful in real programs.

* map_windows.h has the same problem: no timeouts with any of the
basic Win32 APIs (well I can see how to do that, but then you lose
other things, I wrote some details in a comment). Visual Studio's
<threads.h> solved that problem in its mtx_t, so is more conforming in
that respect (I declined to write a low level mtx_t and cnd_t
operation for now... I mean it's not that hard, but right now we
don't need it).

* I realised we don't really need a configure check for
pthread_barrier_wait, because <unistd.h> advertises _POSIX_BARRIERS
for that purpose (another macOS-only problem, POSIX:2018 made it
non-optional).

* Patches 0009-0011 are the pg_dump change I hack on a few weeks ago,
an area Bryan is actively working on. He's gone further than me with
the in-memory queue stuff that we clearly want, so let's focus on his
patches for pg_dump (I will write about that soon). I'm just
including a rebase of my earlier attempt because it exercises and
demonstrates API usage...

* I dislike synthesised/translated OS errors, and the interesting case
thrd_error has no translation anyway. I came up with new extended
error message retrieval API and put it in pg_thread_ext.h where our
other API extensions live.

Example of error reporting:

if ((error = pg_thrd_create(...)) != pg_thrd_success)
pg_fatal("could not create thread: %s",
pg_thrd_error_string_with_detail(error));

It regurgitates platform-specific information which lives in a
thread-local buffer referring to the last failure, much like the scope
of errno and GetLastError():

could not create thread: Windows error 1234
could not create thread: Operation not permitted

It's a string because I also wanted implementations to have the option
of adding context about internal checks or the system call that failed
in multi-step operations:

pg_mtx_init(): bad type flags 42
pg_thrd_join(): no handle for thread 14304
pg_thrd_join(): GetExitCodeThread() failed: Windows error 259

The motivation for collecting as much information as possible is that
I'm imagining the sort of Windows issues or
running-in-container-with-weird-limits errors that show up on the
list. Just converting pg_thrd_error -> EINVAL or whatever bogus value
you want to pick seems like a bad plan in that context.

When using system-provided <threads.h> that isn't available so you
just get 5 pre-baked error strings, but there isn't much we can do
about that. At least if map_windows.h contains bugs then the detailed
error messages might help us find them.

About using system <threads.h> on Windows:

I'm unsure about the packaging/distribution implications. The runtime
code for that seems to be in a new library vcruntime140_threads.dll.
If it's statically linked there should be no issue, but otherwise, I
guess it might need to be distributed, like I guess vcruntime140.dll
is? I just know that it works on CI, need to learn more about that...

We could also decide that it's too soon to pull that trigger, and keep
map_threads.h as a working but not-yet-selected-by-default option on
any OS. Even if we don't use it yet, I like the fact that it acts as
proof that our pg_threads.h models <threads.h> exactly, with
systematic name prefixes.

(Perhaps the code in vrtunetime140_threads.dll will eventually migrate
to UCRT, I mean it *is* C runtime code, one would think. I wonder if
that's what MinGW needs to happen before it can supply <threads.h>,
but I have no clues about any of that.)

Attachments:

t252998_8
v2-0001-ecpg-Fix-auto_mem-cleanup-on-thread-exit.patchtext/x-patch; charset=US-ASCII; name=v2-0001-ecpg-Fix-auto_mem-cleanup-on-thread-exit.patchDownload+8-4
v2-0002-port-Provide-pg_threads.h-API.patchtext/x-patch; charset=US-ASCII; name=v2-0002-port-Provide-pg_threads.h-API.patchDownload+2102-4
v2-0003-port-Use-pg_threads.h-API-for-pg_localconv_r.patchtext/x-patch; charset=US-ASCII; name=v2-0003-port-Use-pg_threads.h-API-for-pg_localconv_r.patchDownload+5-5
v2-0004-ecpg-Improve-variable-name.patchtext/x-patch; charset=US-ASCII; name=v2-0004-ecpg-Improve-variable-name.patchDownload+6-7
v2-0005-ecpg-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v2-0005-ecpg-Use-pg_threads.h.patchDownload+286-697
v2-0006-pgbench-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v2-0006-pgbench-Use-pg_threads.h.patchDownload+13-257
v2-0007-libpq-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v2-0007-libpq-Use-pg_threads.h.patchDownload+21-140
v2-0008-pg_basebackup-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v2-0008-pg_basebackup-Use-pg_threads.h.patchDownload+54-209
v2-0009-pg_signal_processor-Infrastructure-for-cleanup.patchtext/x-patch; charset=US-ASCII; name=v2-0009-pg_signal_processor-Infrastructure-for-cleanup.patchDownload+217-1
v2-0010-fe_utils-Provide-cancel_set-for-fast-quit-paths.patchtext/x-patch; charset=US-ASCII; name=v2-0010-fe_utils-Provide-cancel_set-for-fast-quit-paths.patchDownload+182-1
v2-0011-XXX-pg_dump-Refactor-to-use-threads-on-all-platfo.patchtext/x-patch; charset=US-ASCII; name=v2-0011-XXX-pg_dump-Refactor-to-use-threads-on-all-platfo.patchDownload+239-776
#9Thomas Munro
thomas.munro@gmail.com
In reply to: Thomas Munro (#8)
Re: pg_threads.h take II

I decided to split the API patch into two parts, to highlight
something interesting:

v3-0002 pg_threads.h with support for <pthread.h> or <threads.h>
v3-0003 adds <windows.h> option for native Windows API

If we are ready to make <threads.h> a hard requirement for Visual
Studio builds, and willing to use the <pthread.h> implementation that
ships with MinGW, then we don't actually need v3-0003. That means
that we're left with almost no code, mostly just renaming wrappers and
comments.

CI passes with or without v3-0003.

I also fixed the commit message for v3-0006 which I'd failed to update
(I abandoned my thread-exit invention and just went with tss_t in the
end). No other changes from v2.

Attachments:

t252998_9
v3-0001-ecpg-Fix-auto_mem-cleanup-on-thread-exit.patchtext/x-patch; charset=US-ASCII; name=v3-0001-ecpg-Fix-auto_mem-cleanup-on-thread-exit.patchDownload+8-4
v3-0002-port-Provide-pg_threads.h-API.patchtext/x-patch; charset=US-ASCII; name=v3-0002-port-Provide-pg_threads.h-API.patchDownload+1417-4
v3-0003-port-Native-Windows-mapping-for-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v3-0003-port-Native-Windows-mapping-for-pg_threads.h.patchDownload+714-30
v3-0004-port-Use-pg_threads.h-API-for-pg_localconv_r.patchtext/x-patch; charset=US-ASCII; name=v3-0004-port-Use-pg_threads.h-API-for-pg_localconv_r.patchDownload+5-5
v3-0005-ecpg-Improve-variable-name.patchtext/x-patch; charset=US-ASCII; name=v3-0005-ecpg-Improve-variable-name.patchDownload+6-7
v3-0006-ecpg-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v3-0006-ecpg-Use-pg_threads.h.patchDownload+286-697
v3-0007-pgbench-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v3-0007-pgbench-Use-pg_threads.h.patchDownload+13-257
v3-0008-libpq-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v3-0008-libpq-Use-pg_threads.h.patchDownload+21-140
v3-0009-pg_basebackup-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=v3-0009-pg_basebackup-Use-pg_threads.h.patchDownload+54-209
v3-0010-pg_signal_processor-Infrastructure-for-cleanup.patchtext/x-patch; charset=US-ASCII; name=v3-0010-pg_signal_processor-Infrastructure-for-cleanup.patchDownload+217-1
v3-0011-fe_utils-Provide-cancel_set-for-fast-quit-paths.patchtext/x-patch; charset=US-ASCII; name=v3-0011-fe_utils-Provide-cancel_set-for-fast-quit-paths.patchDownload+182-1
v3-0012-XXX-pg_dump-Refactor-to-use-threads-on-all-platfo.patchtext/x-patch; charset=US-ASCII; name=v3-0012-XXX-pg_dump-Refactor-to-use-threads-on-all-platfo.patchDownload+239-776
#10Jakub Wartak
jakub.wartak@enterprisedb.com
In reply to: Thomas Munro (#9)
Re: pg_threads.h take II

On Fri, Jul 31, 2026 at 4:19 AM Thomas Munro <thomas.munro@gmail.com> wrote:

[..v3..]

Hi Thomas,

I've wanted to somehow help the threads initiative for long time so I've
started playing with patchset, mainly with using multi-threaded pgbench
and pgbasebackup to get a basic feeling, and yay it works! I haven't catched
any problems so far with it, but I had some ideas when dealing with this:
so all of this is mostly about v3 0002+0007+0009:

1. Couldn't we have another (optional?) arg for pg_thrd_create() to issue
pthread_setname_np() from day 1? It would be nice to have something to see
which thread does what (similiar to setproctiltle()) or should we directly
embed something like pg_thrd_setname() that uses pthread_setname_np()?

E.g. @@ -7493,6 +7493,9 @@ threadRun(void *arg)

   +       char            thrid[20];
   +       snprintf(thrid, sizeof(thrid), "pgbench thr%d", thread->tid);
   +       pthread_setname_np(pg_thrd_current(), thrid);

Then we could use something like "ps -aeL -o tid,comm,args | grep bench"
or in GDB to see those threads. So problem seems to be that comm is not
often displayed and sometimes thread 0 (tid=pid) is the process itself
because e.g. in pgbench.c case main calls threadRun() directly too to
make it thread#0. Alternative we could make it conditional there in
threadRun() to bypass that if tid == pid...

BTW: I have found portable way of doing this in MySQL code, see [1]https://github.com/MariaDB/server/blob/bfabe0e53042d6a954c84b58a3c9eade794b9e90/mysys/my_thread_name.cc#L73

2. In src/include/port/pg_threads.h shouldn't the enum be like below?
   enum
   {
           pg_mtx_plain = pg_mtx_plain_impl,
   -       pg_mtx_recursive = pg_mtx_plain_impl,
   +       pg_mtx_recursive = pg_mtx_recursive_impl,
   ?

3. Could we have PTHREAD_MUTEX_ERRORCHECK enabled by default in at least
assert builds? I'm not sure, but if we have stuff like that

pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutex_init(&mutex, &attr);
pthread_mutex_unlock(&mutex); // returns EPERM (unlocking unlocked)
pthread_mutex_lock(&mutex);
pthread_mutex_lock(&mutex); // returns EDEADLK (instead of deadlock)

Please see attached sample nanopatch, but this bring me to the next
problem:

3b.The problem with above is that e.g. double pg_mtx_lock() on same mutex in
with the patchset didn't abort with above add-on for #2 (ERRORCHECK),
because the remapping pg_threads layer does not trigger any Assert() or
we are not checking for any non-zero retcode at all, so which way it should
be ? (should check errors on every pg_mtx_* on every call site?
or should be that part of API to have code reuse?)

4. BTW: the v03-0009/pg_basebackup didn't want to apply due to the
introduction of g_parse_lsn() there in f31d6fbc31d3. Attached is simple
fixup patch. I was kind of interested in that to see how threads there
could be used in far future to unlock even more performance (but that would
have to occur probably after [2]/messages/by-id/CAKZiRmwwW-hDc3B6ERJB+paX7RNSBcQLheq1KdsTf42cGuRvuA@mail.gmail.com with some paralell-backup redesign, but
that got me thinking on how we are going to be compatible with all this
stuff: liburing, pthreads one day) - frankley I couldn't think of any
issues. Anyway, thinking of basebackups, I've reminded myself that zstd
can already uses pthreads both on client (relevant to this $thread) and
server too, e.g:
pg_basebackup -c fast -v -Ft -D /tmp/full.tar -Z client-zstd:workers=2
but when thinking through all of this of my only worry would be, that
in such case with this patch applied and in far future I would be having

100% CPU PID with multiple threads and without fix from #1 it would be

impossible to tell what is being bottlenecked? (saturated ZSTD threads or
now the the thread fetching the)

5. This is something that sent me to the land of doubt: when reviewing that
   0009 for basebackup there's this change:
   -static volatile sig_atomic_t bgchild_exited = false;
   +static volatile bool wal_streamer_thread_exited = false;
   It works here (x86_64), but is it safe/platform compatible? I've read a lot
   about _Atomic / atomic_int / sig_atomic_t / pg_atomic_flag / stdatomic.h
   patch of Your's in [3] and Greg even mention pg_atomic_bool by Heikki [4]
   there, but the more I read the more confused I am, so any  gudiance and
   help please? :) (and could we maybe put some README to nearby API
   implementaion to mention that for such usecase what should be used going
   forward as solid point of reference? I would almost by defintion use
   "sig_atomic_t" there for such case.
   (it's similiar to size_t vs pgoff_t  vs Size vs ...). The only thing I
   believe right now that atomic_int store really disassembly down to xchgl'
   instruction (sounds like it is safer?)

6. I was wondering shouldn't we have some stub (for now) to initialize the
whole thing just before first use, something like: pg_thrd_init().
Over time we
could place pthread_attr_setstacksize() there if necessary or some (frontend
for now?) stuff like even fprintf() to show some debug info.

-J.

[1]: https://github.com/MariaDB/server/blob/bfabe0e53042d6a954c84b58a3c9eade794b9e90/mysys/my_thread_name.cc#L73
[2]: /messages/by-id/CAKZiRmwwW-hDc3B6ERJB+paX7RNSBcQLheq1KdsTf42cGuRvuA@mail.gmail.com
[3]: /messages/by-id/CA+hUKGKfNuXYVKT7WPpKTNYTgPduzu0=G5yFEMju_4kbW0ybOQ@mail.gmail.com
[4]: /messages/by-id/bb0ba423-816c-4e21-a40f-b1be13b54c5f@iki.fi

Attachments:

nocfbot_fixup_rebase_v3-0009-pg_basebackup-Use-pg_threads.h.patchtext/x-patch; charset=US-ASCII; name=nocfbot_fixup_rebase_v3-0009-pg_basebackup-Use-pg_threads.h.patchDownload+57-207
nocfbot_idea3-partially-working-USE_ASSERT_CHECKING-for-pth.patchtext/x-patch; charset=US-ASCII; name=nocfbot_idea3-partially-working-USE_ASSERT_CHECKING-for-pth.patchDownload+12-1